From 689d1ce3dd850e50e3f0bea2e50ea6528584a040 Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Sun, 24 Jan 2021 01:14:56 +0100 Subject: [PATCH] [db] Refactor queue_item functions - Use prepared statements - Add qi_mfi_map that defines mapping between mfi, dbmfi and qi - Use qi_cols_map/qi_mfi_map for iteration (avoid duplicating field references) - Stick to "qi" as name for a queue_item in db.c (more similar to mfi/pli/gri) - Some renaming and other minor stuff in db.c's queue code --- src/db.c | 579 ++++++++++++++++++++++++-------------- src/db.h | 14 +- src/httpd_jsonapi.c | 2 +- src/library/filescanner.c | 44 +-- src/player.c | 2 +- src/spotify_webapi.c | 6 +- 6 files changed, 382 insertions(+), 265 deletions(-) diff --git a/src/db.c b/src/db.c index cb1f1172..b6b058f1 100644 --- a/src/db.c +++ b/src/db.c @@ -112,16 +112,26 @@ struct db_statements sqlite3_stmt *playlists_insert; sqlite3_stmt *playlists_update; + + sqlite3_stmt *queue_items_insert; + sqlite3_stmt *queue_items_update; }; struct col_type_map { char *name; - size_t offset; + ssize_t offset; short type; enum fixup_type fixup; short flag; }; +struct qi_mfi_map +{ + ssize_t qi_offset; + ssize_t mfi_offset; + ssize_t dbmfi_offset; +}; + struct fixup_ctx { const struct col_type_map *map; @@ -129,7 +139,7 @@ struct fixup_ctx void *data; struct media_file_info *mfi; struct playlist_info *pli; - struct db_queue_item *queue_item; + struct db_queue_item *qi; }; struct query_clause { @@ -248,11 +258,12 @@ static const struct col_type_map pli_cols_map[] = /* This list must be kept in sync with * - the order of the columns in the queue table * - the type and name of the fields in struct db_queue_item + * - with qi_mfi_map */ static const struct col_type_map qi_cols_map[] = { { "id", qi_offsetof(id), DB_TYPE_INT, DB_FIXUP_STANDARD, DB_FLAG_NO_BIND }, - { "file_id", qi_offsetof(id), DB_TYPE_INT }, + { "file_id", qi_offsetof(file_id), DB_TYPE_INT }, { "pos", qi_offsetof(pos), DB_TYPE_INT }, { "shuffle_pos", qi_offsetof(shuffle_pos), DB_TYPE_INT }, { "data_kind", qi_offsetof(data_kind), DB_TYPE_INT }, @@ -405,6 +416,43 @@ static const ssize_t dbgri_cols_map[] = dbgri_offsetof(seek), }; +/* This list must be kept in sync with + * - qi_cols_map + */ +static const struct qi_mfi_map qi_mfi_map[] = + { + { qi_offsetof(id), -1, -1 }, + { qi_offsetof(file_id), mfi_offsetof(id), dbmfi_offsetof(id) }, + { qi_offsetof(pos), -1, -1 }, + { qi_offsetof(shuffle_pos), -1, -1 }, + { qi_offsetof(data_kind), mfi_offsetof(data_kind), dbmfi_offsetof(data_kind) }, + { qi_offsetof(media_kind), mfi_offsetof(media_kind), dbmfi_offsetof(media_kind) }, + { qi_offsetof(song_length), mfi_offsetof(song_length), dbmfi_offsetof(song_length) }, + { qi_offsetof(path), mfi_offsetof(path), dbmfi_offsetof(path) }, + { qi_offsetof(virtual_path), mfi_offsetof(virtual_path), dbmfi_offsetof(virtual_path) }, + { qi_offsetof(title), mfi_offsetof(title), dbmfi_offsetof(title) }, + { qi_offsetof(artist), mfi_offsetof(artist), dbmfi_offsetof(artist) }, + { qi_offsetof(album_artist), mfi_offsetof(album_artist), dbmfi_offsetof(album_artist) }, + { qi_offsetof(album), mfi_offsetof(album), dbmfi_offsetof(album) }, + { qi_offsetof(genre), mfi_offsetof(genre), dbmfi_offsetof(genre) }, + { qi_offsetof(songalbumid), mfi_offsetof(songalbumid), dbmfi_offsetof(songalbumid) }, + { qi_offsetof(time_modified), mfi_offsetof(time_modified), dbmfi_offsetof(time_modified) }, + { qi_offsetof(artist_sort), mfi_offsetof(artist_sort), dbmfi_offsetof(artist_sort) }, + { qi_offsetof(album_sort), mfi_offsetof(album_sort), dbmfi_offsetof(album_sort) }, + { qi_offsetof(album_artist_sort), mfi_offsetof(album_artist_sort), dbmfi_offsetof(album_artist_sort) }, + { qi_offsetof(year), mfi_offsetof(year), dbmfi_offsetof(year) }, + { qi_offsetof(track), mfi_offsetof(track), dbmfi_offsetof(track) }, + { qi_offsetof(disc), mfi_offsetof(disc), dbmfi_offsetof(disc) }, + { qi_offsetof(artwork_url), -1, -1 }, + { qi_offsetof(queue_version), -1, -1 }, + { qi_offsetof(composer), mfi_offsetof(composer), dbmfi_offsetof(composer) }, + { qi_offsetof(songartistid), mfi_offsetof(songartistid), dbmfi_offsetof(songartistid) }, + { qi_offsetof(type), mfi_offsetof(type), dbmfi_offsetof(type) }, + { qi_offsetof(bitrate), mfi_offsetof(bitrate), dbmfi_offsetof(bitrate) }, + { qi_offsetof(samplerate), mfi_offsetof(samplerate), dbmfi_offsetof(samplerate) }, + { qi_offsetof(channels), mfi_offsetof(channels), dbmfi_offsetof(channels) }, + }; + /* This list must be kept in sync with * - the order of the columns in the inotify table * - the name and type of the fields in struct watch_info @@ -719,29 +767,73 @@ free_query_params(struct query_params *qp, int content_only) } void -free_queue_item(struct db_queue_item *queue_item, int content_only) +free_queue_item(struct db_queue_item *qi, int content_only) { - if (!queue_item) + if (!qi) return; - free(queue_item->path); - free(queue_item->virtual_path); - free(queue_item->title); - free(queue_item->artist); - free(queue_item->album_artist); - free(queue_item->composer); - free(queue_item->album); - free(queue_item->genre); - free(queue_item->artist_sort); - free(queue_item->album_sort); - free(queue_item->album_artist_sort); - free(queue_item->artwork_url); - free(queue_item->type); + free(qi->path); + free(qi->virtual_path); + free(qi->title); + free(qi->artist); + free(qi->album_artist); + free(qi->composer); + free(qi->album); + free(qi->genre); + free(qi->artist_sort); + free(qi->album_sort); + free(qi->album_artist_sort); + free(qi->artwork_url); + free(qi->type); if (!content_only) - free(queue_item); + free(qi); else - memset(queue_item, 0, sizeof(struct db_queue_item)); + memset(qi, 0, sizeof(struct db_queue_item)); +} + +// Utility function for stepping through mfi/pli/qi structs and setting the +// values from some source, which could be another struct. Some examples: +// - src is a dbmfi, dst is a qi, dst_type is DB_TYPE_INT, caller wants to own +// qi so must_strdup is true, dbmfi has strings so parse_integers is true +// - src is a **char, dst is a qi, dst_type is DB_TYPE_STRING, src_offset is 0 +// because src is not a struct, caller wants to strdup the string so +// must_strdup is true +static inline void +struct_set(void *dst_struct, ssize_t dst_offset, int dst_type, const void *src_struct, ssize_t src_offset, bool must_strdup, bool parse_integers) +{ + char *srcstr; + uint32_t srcu32val; + int64_t srci64val; + char **dstptr; + + srcstr = *(char **)(src_struct + src_offset); + + switch (dst_type) + { + case DB_TYPE_STRING: + dstptr = (char **)(dst_struct + dst_offset); + *dstptr = must_strdup ? safe_strdup(srcstr) : srcstr; + break; + + case DB_TYPE_INT: + if (parse_integers) + safe_atou32(srcstr, &srcu32val); + else + srcu32val = *(uint32_t *)(src_struct + src_offset); + + memcpy(dst_struct + dst_offset, &srcu32val, sizeof(srcu32val)); + break; + + case DB_TYPE_INT64: + if (parse_integers) + safe_atoi64(srcstr, &srci64val); + else + srci64val = *(int64_t *)(src_struct + src_offset); + + memcpy(dst_struct + dst_offset, &srci64val, sizeof(srci64val)); + break; + } } static void @@ -902,8 +994,8 @@ fixup_defaults(char **tag, enum fixup_type fixup, struct fixup_ctx *ctx) } else if (ctx->pli && ctx->pli->path) *tag = strdup(ctx->pli->path); - else if (ctx->queue_item && ctx->queue_item->path) - *tag = strdup(ctx->queue_item->path); + else if (ctx->qi && ctx->qi->path) + *tag = strdup(ctx->qi->path); else *tag = strdup(CFG_NAME_UNKNOWN_TITLE); break; @@ -950,8 +1042,8 @@ fixup_defaults(char **tag, enum fixup_type fixup, struct fixup_ctx *ctx) *tag = strdup(ca); // If ca is empty string then the artist will not be shown in artist view else if (ctx->mfi && ctx->mfi->artist) *tag = strdup(ctx->mfi->artist); - else if (ctx->queue_item && ctx->queue_item->artist) - *tag = strdup(ctx->queue_item->artist); + else if (ctx->qi && ctx->qi->artist) + *tag = strdup(ctx->qi->artist); else *tag = strdup(CFG_NAME_UNKNOWN_ARTIST); break; @@ -970,8 +1062,8 @@ fixup_defaults(char **tag, enum fixup_type fixup, struct fixup_ctx *ctx) ctx->mfi->media_kind = MEDIA_KIND_MUSIC; else if (ctx->pli && !ctx->pli->media_kind) ctx->pli->media_kind = MEDIA_KIND_MUSIC; - else if (ctx->queue_item && !ctx->queue_item->media_kind) - ctx->queue_item->media_kind = MEDIA_KIND_MUSIC; + else if (ctx->qi && !ctx->qi->media_kind) + ctx->qi->media_kind = MEDIA_KIND_MUSIC; break; @@ -1022,22 +1114,22 @@ fixup_sort_tags(char **tag, enum fixup_type fixup, struct fixup_ctx *ctx) case DB_FIXUP_ARTIST_SORT: if (ctx->mfi) sort_tag_create(tag, ctx->mfi->artist); - else if (ctx->queue_item) - sort_tag_create(tag, ctx->queue_item->artist); + else if (ctx->qi) + sort_tag_create(tag, ctx->qi->artist); break; case DB_FIXUP_ALBUM_SORT: if (ctx->mfi) sort_tag_create(tag, ctx->mfi->album); - else if (ctx->queue_item) - sort_tag_create(tag, ctx->queue_item->album); + else if (ctx->qi) + sort_tag_create(tag, ctx->qi->album); break; case DB_FIXUP_ALBUM_ARTIST_SORT: if (ctx->mfi) sort_tag_create(tag, ctx->mfi->album_artist); - else if (ctx->queue_item) - sort_tag_create(tag, ctx->queue_item->album_artist); + else if (ctx->qi) + sort_tag_create(tag, ctx->qi->album_artist); break; case DB_FIXUP_COMPOSER_SORT: @@ -1103,11 +1195,11 @@ fixup_tags_pli(struct playlist_info *pli) } static void -fixup_tags_queue_item(struct db_queue_item *queue_item) +fixup_tags_qi(struct db_queue_item *qi) { struct fixup_ctx ctx = { 0 }; - ctx.data = queue_item; - ctx.queue_item = queue_item; + ctx.data = qi; + ctx.qi = qi; ctx.map = qi_cols_map; ctx.map_size = ARRAY_SIZE(qi_cols_map); @@ -1171,6 +1263,12 @@ bind_pli(sqlite3_stmt *stmt, struct playlist_info *pli) return bind_generic(stmt, pli, pli_cols_map, ARRAY_SIZE(pli_cols_map), pli->id); } +static int +bind_qi(sqlite3_stmt *stmt, struct db_queue_item *qi) +{ + return bind_generic(stmt, qi, qi_cols_map, ARRAY_SIZE(qi_cols_map), qi->id); +} + /* Unlock notification support */ static void unlock_notify_cb(void **args, int nargs) @@ -4664,7 +4762,7 @@ queue_transaction_begin() * * This function must be called after modifying the queue. * - * @param retval 'retval' == 0, if modifying the queue was successful or 'retval' < 0 if an error occurred + * @param retval 'retval' >= 0, if modifying the queue was successful or 'retval' < 0 if an error occurred * @param queue_version The new queue version, for the pending modifications */ static void @@ -4672,7 +4770,7 @@ queue_transaction_end(int retval, int queue_version) { int ret; - if (retval != 0) + if (retval < 0) goto error; ret = db_admin_setint(DB_ADMIN_QUEUE_VERSION, queue_version); @@ -4691,116 +4789,139 @@ static int queue_reshuffle(uint32_t item_id, int queue_version); static int -queue_add_file(struct db_media_file_info *dbmfi, int pos, int shuffle_pos, int queue_version) +queue_item_add(struct db_queue_item *qi) { -#define Q_TMPL "INSERT INTO queue " \ - "(id, file_id, song_length, data_kind, media_kind, " \ - "pos, shuffle_pos, path, virtual_path, title, " \ - "artist, composer, album_artist, album, genre, songalbumid, songartistid," \ - "time_modified, artist_sort, album_sort, album_artist_sort, year, " \ - "type, bitrate, samplerate, channels, " \ - "track, disc, queue_version)" \ - "VALUES" \ - "(NULL, %s, %s, %s, %s, " \ - "%d, %d, %Q, %Q, %Q, " \ - "%Q, %Q, %Q, %Q, %Q, %s, %s," \ - "%s, %Q, %Q, %Q, %s, " \ - "%Q, %s, %s, %s, " \ - "%s, %s, %d);" - - char *query; int ret; - query = sqlite3_mprintf(Q_TMPL, - dbmfi->id, dbmfi->song_length, dbmfi->data_kind, dbmfi->media_kind, - pos, shuffle_pos, dbmfi->path, dbmfi->virtual_path, dbmfi->title, - dbmfi->artist, dbmfi->composer, dbmfi->album_artist, dbmfi->album, dbmfi->genre, dbmfi->songalbumid, dbmfi->songartistid, - dbmfi->time_modified, dbmfi->artist_sort, dbmfi->album_sort, dbmfi->album_artist_sort, dbmfi->year, - dbmfi->type, dbmfi->bitrate, dbmfi->samplerate, dbmfi->channels, - dbmfi->track, dbmfi->disc, queue_version); - ret = db_query_run(query, 1, 0); + fixup_tags_qi(qi); + + ret = bind_qi(db_statements.queue_items_insert, qi); + if (ret < 0) + return -1; + + // Do not update events here, only caller knows if more items will be added + ret = db_statement_run(db_statements.queue_items_insert, 0); + if (ret < 0) + return -1; + + ret = (int)sqlite3_last_insert_rowid(hdl); + if (ret == 0) + { + DPRINTF(E_LOG, L_DB, "Successful queue item insert but no last_insert_rowid!\n"); + return -1; + } return ret; - -#undef Q_TMPL } static int -queue_add_item(struct db_queue_item *item, int pos, int shuffle_pos, int queue_version) +queue_item_add_from_file(struct db_media_file_info *dbmfi, int pos, int shuffle_pos, int queue_version) { -#define Q_TMPL "INSERT INTO queue " \ - "(id, file_id, song_length, data_kind, media_kind, " \ - "pos, shuffle_pos, path, virtual_path, title, " \ - "artist, composer, album_artist, album, genre, songalbumid, songartistid, " \ - "time_modified, artist_sort, album_sort, album_artist_sort, year, " \ - "type, bitrate, samplerate, channels, " \ - "track, disc, artwork_url, queue_version)" \ - "VALUES" \ - "(NULL, %d, %d, %d, %d, " \ - "%d, %d, %Q, %Q, %Q, " \ - "%Q, %Q, %Q, %Q, %Q, %" PRIi64 ", %" PRIi64 "," \ - "%d, %Q, %Q, %Q, %d, " \ - "%Q, %" PRIu32 ", %" PRIu32 ", %" PRIu32 ", " \ - "%d, %d, %Q, %d);" - - char *query; + struct db_queue_item qi; int ret; - query = sqlite3_mprintf(Q_TMPL, - item->file_id, item->song_length, item->data_kind, item->media_kind, - pos, shuffle_pos, item->path, item->virtual_path, item->title, - item->artist, item->composer, item->album_artist, item->album, item->genre, item->songalbumid, item->songartistid, - item->time_modified, item->artist_sort, item->album_sort, item->album_artist_sort, item->year, - item->type, item->bitrate, item->samplerate, item->channels, - item->track, item->disc, item->artwork_url, queue_version); - ret = db_query_run(query, 1, 0); + // No allocations, just the struct content is copied + db_queue_item_from_dbmfi(&qi, dbmfi); + + // No tag fixup, we can't touch the strings in qi, plus must assume dbmfi has proper tags + + qi.pos = pos; + qi.shuffle_pos = shuffle_pos; + qi.queue_version = queue_version; + + ret = bind_qi(db_statements.queue_items_insert, &qi); + if (ret < 0) + return -1; + + // Do not update events here, only caller knows if more items will be added + ret = db_statement_run(db_statements.queue_items_insert, 0); + if (ret < 0) + return -1; + + ret = (int)sqlite3_last_insert_rowid(hdl); + if (ret == 0) + { + DPRINTF(E_LOG, L_DB, "Successful queue item insert but no last_insert_rowid!\n"); + return -1; + } return ret; - -#undef Q_TMPL } -int -db_queue_update_item(struct db_queue_item *qi) +static int +queue_item_update(struct db_queue_item *qi) { -#define Q_TMPL "UPDATE queue SET " \ - "file_id = %d, song_length = %d, data_kind = %d, media_kind = %d, " \ - "pos = %d, shuffle_pos = %d, path = '%q', virtual_path = %Q, " \ - "title = %Q, artist = %Q, album_artist = %Q, album = %Q, " \ - "composer = %Q, " \ - "genre = %Q, time_modified = %d, " \ - "songalbumid = %" PRIi64 ", songartistid = %" PRIi64 ", " \ - "artist_sort = %Q, album_sort = %Q, album_artist_sort = %Q, " \ - "year = %d, track = %d, disc = %d, artwork_url = %Q, " \ - "queue_version = %d " \ - "WHERE id = %d;" - - int queue_version; - char *query; int ret; - fixup_tags_queue_item(qi); + fixup_tags_qi(qi); + + ret = bind_qi(db_statements.queue_items_update, qi); + if (ret < 0) + return -1; + + // Do not update events here, only caller knows if more items will be updated + ret = db_statement_run(db_statements.queue_items_update, 0); + if (ret < 0) + return -1; + + return qi->id; +} + +void +db_queue_item_from_mfi(struct db_queue_item *qi, struct media_file_info *mfi) +{ + int i; + + memset(qi, 0, sizeof(struct db_queue_item)); + + for (i = 0; i < ARRAY_SIZE(qi_mfi_map); i++) + { + if (qi_mfi_map[i].mfi_offset < 0) + continue; + + struct_set(qi, qi_cols_map[i].offset, qi_cols_map[i].type, mfi, qi_mfi_map[i].mfi_offset, true, false); + } + + if (!qi->file_id) + qi->file_id = DB_MEDIA_FILE_NON_PERSISTENT_ID; +} + +void +db_queue_item_from_dbmfi(struct db_queue_item *qi, struct db_media_file_info *dbmfi) +{ + int i; + + memset(qi, 0, sizeof(struct db_queue_item)); + + for (i = 0; i < ARRAY_SIZE(qi_mfi_map); i++) + { + if (qi_mfi_map[i].dbmfi_offset < 0) + continue; + + struct_set(qi, qi_cols_map[i].offset, qi_cols_map[i].type, dbmfi, qi_mfi_map[i].dbmfi_offset, false, true); + } + + if (!qi->file_id) + qi->file_id = DB_MEDIA_FILE_NON_PERSISTENT_ID; +} + + +int +db_queue_item_update(struct db_queue_item *qi) +{ + int queue_version; + int ret; queue_version = queue_transaction_begin(); - query = sqlite3_mprintf(Q_TMPL, - qi->file_id, qi->song_length, qi->data_kind, qi->media_kind, - qi->pos, qi->shuffle_pos, qi->path, qi->virtual_path, - qi->title, qi->artist, qi->album_artist, qi->album, - qi->composer, - qi->genre, qi->time_modified, - qi->songalbumid, qi->songartistid, - qi->artist_sort, qi->album_sort, qi->album_artist_sort, - qi->year, qi->track, qi->disc, qi->artwork_url, queue_version, - qi->id); + qi->queue_version = queue_version; - ret = db_query_run(query, 1, 0); + ret = queue_item_update(qi); - /* MPD changes playlist version when metadata changes */ + // MPD changes playlist version when metadata changes, also makes LISTENER_QUEUE queue_transaction_end(ret, queue_version); return ret; -#undef Q_TMPL } /* @@ -4888,25 +5009,26 @@ db_queue_add_end(struct db_queue_add_info *queue_add_info, char reshuffle, uint3 } int -db_queue_add_item(struct db_queue_add_info *queue_add_info, struct db_queue_item *item) +db_queue_add_next(struct db_queue_add_info *queue_add_info, struct db_queue_item *qi) { int ret; - fixup_tags_queue_item(item); - ret = queue_add_item(item, queue_add_info->pos, queue_add_info->shuffle_pos, queue_add_info->queue_version); - if (ret == 0) - { - queue_add_info->pos++; - queue_add_info->shuffle_pos++; - queue_add_info->count++; + qi->pos = queue_add_info->pos; + qi->shuffle_pos = queue_add_info->shuffle_pos; + qi->queue_version = queue_add_info->queue_version; - if (queue_add_info->new_item_id == 0) - queue_add_info->new_item_id = (int) sqlite3_last_insert_rowid(hdl); - } + ret = queue_item_add(qi); + if (ret < 0) + return ret; + + queue_add_info->pos++; + queue_add_info->shuffle_pos++; + queue_add_info->count++; + + if (queue_add_info->new_item_id == 0) + queue_add_info->new_item_id = ret; return ret; - -#undef Q_TMPL } /* @@ -4980,7 +5102,7 @@ db_queue_add_by_query(struct query_params *qp, char reshuffle, uint32_t item_id, while (((ret = db_query_fetch_file(qp, &dbmfi)) == 0) && (dbmfi.id)) { - ret = queue_add_file(&dbmfi, pos, queue_count, queue_version); + ret = queue_item_add_from_file(&dbmfi, pos, queue_count, queue_version); if (ret < 0) { @@ -4988,10 +5110,10 @@ db_queue_add_by_query(struct query_params *qp, char reshuffle, uint32_t item_id, break; } - DPRINTF(E_DBG, L_DB, "Added song id %s (%s) to queue\n", dbmfi.id, dbmfi.title); + DPRINTF(E_DBG, L_DB, "Added song id %s (%s) to queue with item id %d\n", dbmfi.id, dbmfi.title, ret); if (new_item_id && *new_item_id == 0) - *new_item_id = (int) sqlite3_last_insert_rowid(hdl); + *new_item_id = ret; if (count) (*count)++; @@ -5133,11 +5255,16 @@ strdup_if(char *str, int cond) } static int -queue_enum_fetch(struct query_params *qp, struct db_queue_item *queue_item, int keep_item) +queue_enum_fetch(struct query_params *qp, struct db_queue_item *qi, int must_strdup) { + const void *srcptr; + const char *str; + int32_t i32; + int64_t i64; int ret; + int i; - memset(queue_item, 0, sizeof(struct db_queue_item)); + memset(qi, 0, sizeof(struct db_queue_item)); if (!qp->stmt) { @@ -5157,36 +5284,32 @@ queue_enum_fetch(struct query_params *qp, struct db_queue_item *queue_item, int return -1; } - queue_item->id = (uint32_t)sqlite3_column_int(qp->stmt, 0); - queue_item->file_id = (uint32_t)sqlite3_column_int(qp->stmt, 1); - queue_item->pos = (uint32_t)sqlite3_column_int(qp->stmt, 2); - queue_item->shuffle_pos = (uint32_t)sqlite3_column_int(qp->stmt, 3); - queue_item->data_kind = sqlite3_column_int(qp->stmt, 4); - queue_item->media_kind = sqlite3_column_int(qp->stmt, 5); - queue_item->song_length = (uint32_t)sqlite3_column_int(qp->stmt, 6); - queue_item->path = strdup_if((char *)sqlite3_column_text(qp->stmt, 7), keep_item); - queue_item->virtual_path = strdup_if((char *)sqlite3_column_text(qp->stmt, 8), keep_item); - queue_item->title = strdup_if((char *)sqlite3_column_text(qp->stmt, 9), keep_item); - queue_item->artist = strdup_if((char *)sqlite3_column_text(qp->stmt, 10), keep_item); - queue_item->album_artist = strdup_if((char *)sqlite3_column_text(qp->stmt, 11), keep_item); - queue_item->album = strdup_if((char *)sqlite3_column_text(qp->stmt, 12), keep_item); - queue_item->genre = strdup_if((char *)sqlite3_column_text(qp->stmt, 13), keep_item); - queue_item->songalbumid = sqlite3_column_int64(qp->stmt, 14); - queue_item->time_modified = sqlite3_column_int(qp->stmt, 15); - queue_item->artist_sort = strdup_if((char *)sqlite3_column_text(qp->stmt, 16), keep_item); - queue_item->album_sort = strdup_if((char *)sqlite3_column_text(qp->stmt, 17), keep_item); - queue_item->album_artist_sort = strdup_if((char *)sqlite3_column_text(qp->stmt, 18), keep_item); - queue_item->year = sqlite3_column_int(qp->stmt, 19); - queue_item->track = sqlite3_column_int(qp->stmt, 20); - queue_item->disc = sqlite3_column_int(qp->stmt, 21); - queue_item->artwork_url = strdup_if((char *)sqlite3_column_text(qp->stmt, 22), keep_item); - queue_item->queue_version = sqlite3_column_int(qp->stmt, 23); - queue_item->composer = strdup_if((char *)sqlite3_column_text(qp->stmt, 24), keep_item); - queue_item->songartistid = sqlite3_column_int64(qp->stmt, 25); - queue_item->type = strdup_if((char *)sqlite3_column_text(qp->stmt, 26), keep_item); - queue_item->bitrate = sqlite3_column_int(qp->stmt, 27); - queue_item->samplerate = sqlite3_column_int(qp->stmt, 28); - queue_item->channels = sqlite3_column_int(qp->stmt, 29); + for (i = 0; i < ARRAY_SIZE(qi_cols_map); i++) + { + switch (qi_cols_map[i].type) + { + case DB_TYPE_STRING: + str = (const char *)sqlite3_column_text(qp->stmt, i); + srcptr = &str; + break; + + case DB_TYPE_INT: + i32 = sqlite3_column_int(qp->stmt, i); + srcptr = &i32; + break; + + case DB_TYPE_INT64: + i64 = sqlite3_column_int64(qp->stmt, i); + srcptr = &i64; + break; + + default: + DPRINTF(E_LOG, L_DB, "BUG! Unknown data type (%d) in queue_enum_fetch()\n", qi_cols_map[i].type); + return -1; + } + + struct_set(qi, qi_cols_map[i].offset, qi_cols_map[i].type, srcptr, 0, must_strdup, false); + } return 0; } @@ -5214,9 +5337,9 @@ db_queue_enum_end(struct query_params *qp) } int -db_queue_enum_fetch(struct query_params *qp, struct db_queue_item *queue_item) +db_queue_enum_fetch(struct query_params *qp, struct db_queue_item *qi) { - return queue_enum_fetch(qp, queue_item, 0); + return queue_enum_fetch(qp, qi, 0); } int @@ -5244,7 +5367,7 @@ db_queue_get_pos(uint32_t item_id, char shuffle) } static int -queue_fetch_byitemid(uint32_t item_id, struct db_queue_item *queue_item, int with_metadata) +queue_fetch_byitemid(uint32_t item_id, struct db_queue_item *qi, int with_metadata) { struct query_params qp; int ret; @@ -5259,7 +5382,7 @@ queue_fetch_byitemid(uint32_t item_id, struct db_queue_item *queue_item, int wit return -1; } - ret = queue_enum_fetch(&qp, queue_item, with_metadata); + ret = queue_enum_fetch(&qp, qi, with_metadata); db_query_end(&qp); sqlite3_free(qp.filter); return ret; @@ -5268,46 +5391,46 @@ queue_fetch_byitemid(uint32_t item_id, struct db_queue_item *queue_item, int wit struct db_queue_item * db_queue_fetch_byitemid(uint32_t item_id) { - struct db_queue_item *queue_item; + struct db_queue_item *qi; int ret; - queue_item = calloc(1, sizeof(struct db_queue_item)); - if (!queue_item) + qi = calloc(1, sizeof(struct db_queue_item)); + if (!qi) { DPRINTF(E_LOG, L_DB, "Out of memory for queue_item\n"); return NULL; } db_transaction_begin(); - ret = queue_fetch_byitemid(item_id, queue_item, 1); + ret = queue_fetch_byitemid(item_id, qi, 1); db_transaction_end(); if (ret < 0) { - free_queue_item(queue_item, 0); + free_queue_item(qi, 0); DPRINTF(E_LOG, L_DB, "Error fetching queue item by item id\n"); return NULL; } - else if (queue_item->id == 0) + else if (qi->id == 0) { // No item found - free_queue_item(queue_item, 0); + free_queue_item(qi, 0); return NULL; } - return queue_item; + return qi; } struct db_queue_item * db_queue_fetch_byfileid(uint32_t file_id) { - struct db_queue_item *queue_item; + struct db_queue_item *qi; struct query_params qp; int ret; memset(&qp, 0, sizeof(struct query_params)); - queue_item = calloc(1, sizeof(struct db_queue_item)); - if (!queue_item) + qi = calloc(1, sizeof(struct db_queue_item)); + if (!qi) { DPRINTF(E_LOG, L_DB, "Out of memory for queue_item\n"); return NULL; @@ -5322,34 +5445,34 @@ db_queue_fetch_byfileid(uint32_t file_id) { sqlite3_free(qp.filter); db_transaction_end(); - free_queue_item(queue_item, 0); + free_queue_item(qi, 0); DPRINTF(E_LOG, L_DB, "Error fetching queue item by file id\n"); return NULL; } - ret = queue_enum_fetch(&qp, queue_item, 1); + ret = queue_enum_fetch(&qp, qi, 1); db_query_end(&qp); sqlite3_free(qp.filter); db_transaction_end(); if (ret < 0) { - free_queue_item(queue_item, 0); + free_queue_item(qi, 0); DPRINTF(E_LOG, L_DB, "Error fetching queue item by file id\n"); return NULL; } - else if (queue_item->id == 0) + else if (qi->id == 0) { // No item found - free_queue_item(queue_item, 0); + free_queue_item(qi, 0); return NULL; } - return queue_item; + return qi; } static int -queue_fetch_bypos(uint32_t pos, char shuffle, struct db_queue_item *queue_item, int with_metadata) +queue_fetch_bypos(uint32_t pos, char shuffle, struct db_queue_item *qi, int with_metadata) { struct query_params qp; int ret; @@ -5367,7 +5490,7 @@ queue_fetch_bypos(uint32_t pos, char shuffle, struct db_queue_item *queue_item, return -1; } - ret = queue_enum_fetch(&qp, queue_item, with_metadata); + ret = queue_enum_fetch(&qp, qi, with_metadata); db_query_end(&qp); sqlite3_free(qp.filter); return ret; @@ -5376,38 +5499,38 @@ queue_fetch_bypos(uint32_t pos, char shuffle, struct db_queue_item *queue_item, struct db_queue_item * db_queue_fetch_bypos(uint32_t pos, char shuffle) { - struct db_queue_item *queue_item; + struct db_queue_item *qi; int ret; - queue_item = calloc(1, sizeof(struct db_queue_item)); - if (!queue_item) + qi = calloc(1, sizeof(struct db_queue_item)); + if (!qi) { DPRINTF(E_LOG, L_MAIN, "Out of memory for queue_item\n"); return NULL; } db_transaction_begin(); - ret = queue_fetch_bypos(pos, shuffle, queue_item, 1); + ret = queue_fetch_bypos(pos, shuffle, qi, 1); db_transaction_end(); if (ret < 0) { - free_queue_item(queue_item, 0); + free_queue_item(qi, 0); DPRINTF(E_LOG, L_DB, "Error fetching queue item by pos id\n"); return NULL; } - else if (queue_item->id == 0) + else if (qi->id == 0) { // No item found - free_queue_item(queue_item, 0); + free_queue_item(qi, 0); return NULL; } - return queue_item; + return qi; } static int -queue_fetch_byposrelativetoitem(int pos, uint32_t item_id, char shuffle, struct db_queue_item *queue_item, int with_metadata) +queue_fetch_byposrelativetoitem(int pos, uint32_t item_id, char shuffle, struct db_queue_item *qi, int with_metadata) { int pos_absolute; int ret; @@ -5424,12 +5547,12 @@ queue_fetch_byposrelativetoitem(int pos, uint32_t item_id, char shuffle, struct pos_absolute += pos; - ret = queue_fetch_bypos(pos_absolute, shuffle, queue_item, with_metadata); + ret = queue_fetch_bypos(pos_absolute, shuffle, qi, with_metadata); if (ret < 0) DPRINTF(E_LOG, L_DB, "Error fetching item by pos: pos (%d) relative to item with id (%d)\n", pos, item_id); else - DPRINTF(E_DBG, L_DB, "Fetch by pos: fetched item (id=%d, pos=%d, file-id=%d)\n", queue_item->id, queue_item->pos, queue_item->file_id); + DPRINTF(E_DBG, L_DB, "Fetch by pos: fetched item (id=%d, pos=%d, file-id=%d)\n", qi->id, qi->pos, qi->file_id); return ret; } @@ -5437,13 +5560,13 @@ queue_fetch_byposrelativetoitem(int pos, uint32_t item_id, char shuffle, struct struct db_queue_item * db_queue_fetch_byposrelativetoitem(int pos, uint32_t item_id, char shuffle) { - struct db_queue_item *queue_item; + struct db_queue_item *qi; int ret; DPRINTF(E_DBG, L_DB, "Fetch by pos: pos (%d) relative to item with id (%d)\n", pos, item_id); - queue_item = calloc(1, sizeof(struct db_queue_item)); - if (!queue_item) + qi = calloc(1, sizeof(struct db_queue_item)); + if (!qi) { DPRINTF(E_LOG, L_MAIN, "Out of memory for queue_item\n"); return NULL; @@ -5451,26 +5574,26 @@ db_queue_fetch_byposrelativetoitem(int pos, uint32_t item_id, char shuffle) db_transaction_begin(); - ret = queue_fetch_byposrelativetoitem(pos, item_id, shuffle, queue_item, 1); + ret = queue_fetch_byposrelativetoitem(pos, item_id, shuffle, qi, 1); db_transaction_end(); if (ret < 0) { - free_queue_item(queue_item, 0); + free_queue_item(qi, 0); DPRINTF(E_LOG, L_DB, "Error fetching queue item by pos relative to item id\n"); return NULL; } - else if (queue_item->id == 0) + else if (qi->id == 0) { // No item found - free_queue_item(queue_item, 0); + free_queue_item(qi, 0); return NULL; } - DPRINTF(E_DBG, L_DB, "Fetch by pos: fetched item (id=%d, pos=%d, file-id=%d)\n", queue_item->id, queue_item->pos, queue_item->file_id); + DPRINTF(E_DBG, L_DB, "Fetch by pos: fetched item (id=%d, pos=%d, file-id=%d)\n", qi->id, qi->pos, qi->file_id); - return queue_item; + return qi; } struct db_queue_item * @@ -5604,13 +5727,13 @@ db_queue_clear(uint32_t keep_item_id) } static int -queue_delete_item(struct db_queue_item *queue_item, int queue_version) +queue_delete_item(struct db_queue_item *qi, int queue_version) { char *query; int ret; // Remove item with the given item_id - query = sqlite3_mprintf("DELETE FROM queue where id = %d;", queue_item->id); + query = sqlite3_mprintf("DELETE FROM queue where id = %d;", qi->id); ret = db_query_run(query, 1, 0); if (ret < 0) { @@ -5618,7 +5741,7 @@ queue_delete_item(struct db_queue_item *queue_item, int queue_version) } // Update pos for all items after the item with given item_id - query = sqlite3_mprintf("UPDATE queue SET pos = pos - 1, queue_version = %d WHERE pos > %d;", queue_version, queue_item->pos); + query = sqlite3_mprintf("UPDATE queue SET pos = pos - 1, queue_version = %d WHERE pos > %d;", queue_version, qi->pos); ret = db_query_run(query, 1, 0); if (ret < 0) { @@ -5626,7 +5749,7 @@ queue_delete_item(struct db_queue_item *queue_item, int queue_version) } // Update shuffle_pos for all items after the item with given item_id - query = sqlite3_mprintf("UPDATE queue SET shuffle_pos = shuffle_pos - 1, queue_version = %d WHERE shuffle_pos > %d;", queue_version, queue_item->shuffle_pos); + query = sqlite3_mprintf("UPDATE queue SET shuffle_pos = shuffle_pos - 1, queue_version = %d WHERE shuffle_pos > %d;", queue_version, qi->shuffle_pos); ret = db_query_run(query, 1, 0); if (ret < 0) { @@ -6759,6 +6882,9 @@ db_open(void) int synchronous; int mmap_size; + if (!db_path) + return -1; + ret = sqlite3_open(db_path, &hdl); if (ret != SQLITE_OK) { @@ -6951,8 +7077,12 @@ db_statements_prepare(void) db_statements.playlists_insert = db_statements_prepare_insert(pli_cols_map, ARRAY_SIZE(pli_cols_map), "playlists"); db_statements.playlists_update = db_statements_prepare_update(pli_cols_map, ARRAY_SIZE(pli_cols_map), "playlists"); + db_statements.queue_items_insert = db_statements_prepare_insert(qi_cols_map, ARRAY_SIZE(qi_cols_map), "queue"); + db_statements.queue_items_update = db_statements_prepare_update(qi_cols_map, ARRAY_SIZE(qi_cols_map), "queue"); + if ( !db_statements.files_insert || !db_statements.files_update || !db_statements.files_ping || !db_statements.playlists_insert || !db_statements.playlists_update + || !db_statements.queue_items_insert || !db_statements.queue_items_update ) return -1; @@ -7196,7 +7326,9 @@ db_init(void) uint32_t files; uint32_t pls; int ret; + int i; + // Consistency checks if (ARRAY_SIZE(dbmfi_cols_map) != ARRAY_SIZE(mfi_cols_map)) { DPRINTF(E_FATAL, L_DB, "BUG: mfi column maps are not in sync\n"); @@ -7209,6 +7341,21 @@ db_init(void) return -1; } + if (ARRAY_SIZE(qi_cols_map) != ARRAY_SIZE(qi_mfi_map)) + { + DPRINTF(E_FATAL, L_DB, "BUG: queue_item column maps are not in sync\n"); + return -1; + } + + for (i = 0; i < ARRAY_SIZE(qi_cols_map); i++) + { + if (qi_cols_map[i].offset == qi_mfi_map[i].qi_offset) + continue; + + DPRINTF(E_FATAL, L_DB, "BUG: queue_item offset maps are not in sync (at %d)\n", i); + return -1; + } + db_path = cfg_getstr(cfg_getsec(cfg, "general"), "db_path"); db_rating_updates = cfg_getbool(cfg_getsec(cfg, "library"), "rating_updates"); diff --git a/src/db.h b/src/db.h index 755aab20..aa4ae5ba 100644 --- a/src/db.h +++ b/src/db.h @@ -543,7 +543,7 @@ void free_query_params(struct query_params *qp, int content_only); void -free_queue_item(struct db_queue_item *queue_item, int content_only); +free_queue_item(struct db_queue_item *qi, int content_only); /* Maintenance and DB hygiene */ void @@ -816,8 +816,14 @@ int db_speaker_get(struct output_device *device, uint64_t id); /* Queue */ +void +db_queue_item_from_mfi(struct db_queue_item *qi, struct media_file_info *mfi); // Use free_queue_item(qi, 0) to free + +void +db_queue_item_from_dbmfi(struct db_queue_item *qi, struct db_media_file_info *dbmfi); // Do not free qi content + int -db_queue_update_item(struct db_queue_item *queue_item); +db_queue_item_update(struct db_queue_item *qi); int db_queue_add_by_queryafteritemid(struct query_params *qp, uint32_t item_id); @@ -838,7 +844,7 @@ int db_queue_add_end(struct db_queue_add_info *queue_add_info, char reshuffle, uint32_t item_id, int ret); int -db_queue_add_item(struct db_queue_add_info *queue_add_info, struct db_queue_item *item); +db_queue_add_next(struct db_queue_add_info *queue_add_info, struct db_queue_item *qi); int db_queue_enum_start(struct query_params *qp); @@ -847,7 +853,7 @@ void db_queue_enum_end(struct query_params *qp); int -db_queue_enum_fetch(struct query_params *qp, struct db_queue_item *queue_item); +db_queue_enum_fetch(struct query_params *qp, struct db_queue_item *qi); struct db_queue_item * db_queue_fetch_byitemid(uint32_t item_id); diff --git a/src/httpd_jsonapi.c b/src/httpd_jsonapi.c index 4f81d7fa..a5232c26 100644 --- a/src/httpd_jsonapi.c +++ b/src/httpd_jsonapi.c @@ -2607,7 +2607,7 @@ jsonapi_reply_queue_tracks_update(struct httpd_request *hreq) return ret; if (is_changed) - db_queue_update_item(queue_item); + db_queue_item_update(queue_item); return HTTP_NOCONTENT; } diff --git a/src/library/filescanner.c b/src/library/filescanner.c index 86f047cf..07edd5fc 100644 --- a/src/library/filescanner.c +++ b/src/library/filescanner.c @@ -1742,47 +1742,11 @@ filescanner_fullrescan() return 0; } -static void -map_media_file_to_queue_item(struct db_queue_item *queue_item, struct media_file_info *mfi) -{ - memset(queue_item, 0, sizeof(struct db_queue_item)); - - if (mfi->id) - queue_item->file_id = mfi->id; - else - queue_item->file_id = DB_MEDIA_FILE_NON_PERSISTENT_ID; - - queue_item->title = safe_strdup(mfi->title); - queue_item->artist = safe_strdup(mfi->artist); - queue_item->album_artist = safe_strdup(mfi->album_artist); - queue_item->album = safe_strdup(mfi->album); - queue_item->genre = safe_strdup(mfi->genre); - queue_item->artist_sort = safe_strdup(mfi->artist_sort); - queue_item->album_artist_sort = safe_strdup(mfi->album_artist_sort); - queue_item->album_sort = safe_strdup(mfi->album_sort); - queue_item->path = safe_strdup(mfi->path); - queue_item->virtual_path = safe_strdup(mfi->virtual_path); - queue_item->data_kind = mfi->data_kind; - queue_item->media_kind = mfi->media_kind; - queue_item->song_length = mfi->song_length; - queue_item->seek = mfi->seek; - queue_item->songalbumid = mfi->songalbumid; - queue_item->time_modified = mfi->time_modified; - queue_item->year = mfi->year; - queue_item->track = mfi->track; - queue_item->disc = mfi->disc; - //queue_item->artwork_url - queue_item->type = safe_strdup(mfi->type); - queue_item->channels = mfi->channels; - queue_item->samplerate = mfi->samplerate; - queue_item->bitrate = mfi->bitrate; -} - static int queue_item_stream_add(const char *path, int position, char reshuffle, uint32_t item_id, int *count, int *new_item_id) { struct media_file_info mfi; - struct db_queue_item item; + struct db_queue_item qi; struct db_queue_add_info queue_add_info; int ret; @@ -1790,12 +1754,12 @@ queue_item_stream_add(const char *path, int position, char reshuffle, uint32_t i scan_metadata_stream(&mfi, path); - map_media_file_to_queue_item(&item, &mfi); + db_queue_item_from_mfi(&qi, &mfi); ret = db_queue_add_start(&queue_add_info, position); if (ret == 0) { - ret = db_queue_add_item(&queue_add_info, &item); + ret = db_queue_add_next(&queue_add_info, &qi); ret = db_queue_add_end(&queue_add_info, reshuffle, item_id, ret); if (ret == 0) { @@ -1806,7 +1770,7 @@ queue_item_stream_add(const char *path, int position, char reshuffle, uint32_t i } } - free_queue_item(&item, 1); + free_queue_item(&qi, 1); free_mfi(&mfi, 1); return 0; diff --git a/src/player.c b/src/player.c index 2be6a76c..a110e9c4 100644 --- a/src/player.c +++ b/src/player.c @@ -601,7 +601,7 @@ metadata_update_queue_cb(void *arg) if (metadata->len_ms) queue_item->song_length = metadata->len_ms; - ret = db_queue_update_item(queue_item); + ret = db_queue_item_update(queue_item); if (ret < 0) DPRINTF(E_LOG, L_PLAYER, "Database error while updating queue with new metadata\n"); } diff --git a/src/spotify_webapi.c b/src/spotify_webapi.c index c417f7be..9e02c6f0 100644 --- a/src/spotify_webapi.c +++ b/src/spotify_webapi.c @@ -1080,7 +1080,7 @@ queue_add_track(const char *uri, int position, char reshuffle, uint32_t item_id, ret = db_queue_add_start(&queue_add_info, position); if (ret == 0) { - ret = db_queue_add_item(&queue_add_info, &item); + ret = db_queue_add_next(&queue_add_info, &item); ret = db_queue_add_end(&queue_add_info, reshuffle, item_id, ret); if (ret == 0) { @@ -1122,7 +1122,7 @@ queue_add_album_tracks(json_object *item, int index, int total, enum spotify_req map_track_to_queueitem(&queue_item, &track, ¶m->album); - ret = db_queue_add_item(¶m->queue_add_info, &queue_item); + ret = db_queue_add_next(¶m->queue_add_info, &queue_item); free_queue_item(&queue_item, 1); @@ -1236,7 +1236,7 @@ queue_add_playlist_tracks(json_object *item, int index, int total, enum spotify_ map_track_to_queueitem(&queue_item, &track, NULL); - ret = db_queue_add_item(queue_add_info, &queue_item); + ret = db_queue_add_next(queue_add_info, &queue_item); free_queue_item(&queue_item, 1);