[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
This commit is contained in:
ejurgensen 2021-01-24 01:14:56 +01:00
parent 76c52bba3b
commit 689d1ce3dd
6 changed files with 382 additions and 265 deletions

571
src/db.c
View File

@ -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)
{
qi->pos = queue_add_info->pos;
qi->shuffle_pos = queue_add_info->shuffle_pos;
qi->queue_version = queue_add_info->queue_version;
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 = (int) sqlite3_last_insert_rowid(hdl);
}
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");

View File

@ -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);

View File

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

View File

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

View File

@ -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");
}

View File

@ -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, &param->album);
ret = db_queue_add_item(&param->queue_add_info, &queue_item);
ret = db_queue_add_next(&param->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);