diff --git a/src/DAAP2SQL.g b/src/DAAP2SQL.g index 1fcbf12f..ac23a6e6 100644 --- a/src/DAAP2SQL.g +++ b/src/DAAP2SQL.g @@ -69,11 +69,7 @@ expr returns [ pANTLR3_STRING result, int valid ] @init { $result = NULL; $valid = 1; } : ^(OPAND a = expr b = expr) { - if (!$a.valid || !$b.valid) - { - $valid = 0; - } - else + if ($a.valid && $b.valid) { $result = $a.result->factory->newRaw($a.result->factory); $result->append8($result, "("); @@ -82,14 +78,24 @@ expr returns [ pANTLR3_STRING result, int valid ] $result->appendS($result, $b.result); $result->append8($result, ")"); } - } - | ^(OPOR a = expr b = expr) - { - if (!$a.valid || !$b.valid) + else if ($a.valid) + { + $result = $a.result->factory->newRaw($a.result->factory); + $result->appendS($result, $a.result); + } + else if ($b.valid) + { + $result = $b.result->factory->newRaw($b.result->factory); + $result->appendS($result, $b.result); + } + else { $valid = 0; } - else + } + | ^(OPOR a = expr b = expr) + { + if ($a.valid && $b.valid) { $result = $a.result->factory->newRaw($a.result->factory); $result->append8($result, "("); @@ -98,6 +104,20 @@ expr returns [ pANTLR3_STRING result, int valid ] $result->appendS($result, $b.result); $result->append8($result, ")"); } + else if ($a.valid) + { + $result = $a.result->factory->newRaw($a.result->factory); + $result->appendS($result, $a.result); + } + else if ($b.valid) + { + $result = $b.result->factory->newRaw($b.result->factory); + $result->appendS($result, $b.result); + } + else + { + $valid = 0; + } } | STR { @@ -194,13 +214,22 @@ expr returns [ pANTLR3_STRING result, int valid ] goto STR_result_valid_0; /* ABORT */ } + /* No need to exclude empty artist and album, as forked-daapd makes sure there always exists an artist/album. */ + if (neg_op && op == ':' + && (strcmp((char *)field, "daap.songalbumartist") == 0 + || strcmp((char *)field, "daap.songartist") == 0 + || strcmp((char *)field, "daap.songalbum") == 0)) + { + DPRINTF(E_DBG, L_DAAP, "Ignoring clause '\%s\%s\%c'\n", field, (neg_op) ? "!" : "", op); + $valid = 0; + goto STR_result_valid_0; + } + /* Need to check against NULL too */ if (op == ':') $result->append8($result, "("); } - $result->append8($result, dqfm->db_col); - /* Int field: check integer conversion */ if (dqfm->as_int) { @@ -225,6 +254,16 @@ expr returns [ pANTLR3_STRING result, int valid ] } *end = '\0'; /* Cut out potential garbage - we're being kind */ + + /* forked-daapd only has media_kind = 1 for music - so remove media_kind = 32 to imporve select query performance. */ + if (llval == 32 + && (strcmp((char *)field, "com.apple.itunes.mediakind") == 0 + || strcmp((char *)field, "com.apple.itunes.extended-media-kind") == 0)) + { + DPRINTF(E_DBG, L_DAAP, "Ignoring clause '\%s\%s\%c\%s'\n", field, (neg_op) ? "!" : "", op, val); + $valid = 0; + goto STR_result_valid_0; + } } /* String field: escape string, check for '*' */ else @@ -258,6 +297,8 @@ expr returns [ pANTLR3_STRING result, int valid ] val[strlen((char *)val) - 1] = '\%'; } } + + $result->append8($result, dqfm->db_col); switch(op) { diff --git a/src/artwork.c b/src/artwork.c index de8b4110..49134a3f 100644 --- a/src/artwork.c +++ b/src/artwork.c @@ -1303,20 +1303,12 @@ artwork_get_item(int id, int max_w, int max_h, struct evbuffer *evbuf) int artwork_get_group(int id, int max_w, int max_h, struct evbuffer *evbuf) { - int64_t persistentid; int format; DPRINTF(E_DBG, L_ART, "Artwork request for group %d\n", id); - /* Get the persistent id for the given group id */ - if (db_group_persistentid_byid(id, &persistentid) < 0) - { - DPRINTF(E_LOG, L_ART, "Error fetching persistent id for group id %d\n", id); - return -1; - } - /* Load artwork image for the persistent id */ - format = artwork_get_group_persistentid(persistentid, max_w, max_h, evbuf); + format = artwork_get_group_persistentid(id, max_w, max_h, evbuf); if (format <= 0) { DPRINTF(E_DBG, L_ART, "No artwork found for group %d\n", id); diff --git a/src/db.c b/src/db.c index 75815d19..8876058c 100644 --- a/src/db.c +++ b/src/db.c @@ -742,12 +742,14 @@ db_purge_cruft(time_t ref) char *errmsg; int i; int ret; - char *queries[3] = { NULL, NULL, NULL }; - char *queries_tmpl[3] = + char *queries[5] = { NULL, NULL, NULL, NULL, NULL }; + char *queries_tmpl[5] = { "DELETE FROM playlistitems WHERE playlistid IN (SELECT id FROM playlists p WHERE p.type <> 1 AND p.db_timestamp < %" PRIi64 ");", "DELETE FROM playlists WHERE type <> 1 AND db_timestamp < %" PRIi64 ";", - "DELETE FROM files WHERE db_timestamp < %" PRIi64 ";" + "DELETE FROM files WHERE db_timestamp < %" PRIi64 ";", + "DELETE FROM groups WHERE type = 1 AND id NOT IN (SELECT songalbumid FROM files);", + "DELETE FROM groups WHERE type = 2 AND id NOT IN (SELECT songartistid FROM files);" }; if (sizeof(queries) != sizeof(queries_tmpl)) @@ -758,7 +760,11 @@ db_purge_cruft(time_t ref) for (i = 0; i < (sizeof(queries_tmpl) / sizeof(queries_tmpl[0])); i++) { - queries[i] = sqlite3_mprintf(queries_tmpl[i], (int64_t)ref); + if (i < 3) + queries[i] = sqlite3_mprintf(queries_tmpl[i], (int64_t)ref); + else + queries[i] = sqlite3_mprintf(queries_tmpl[i]); + if (!queries[i]) { DPRINTF(E_LOG, L_DB, "Out of memory for query string\n"); @@ -1197,13 +1203,13 @@ db_build_query_group_albums(struct query_params *qp, char **q) sort = sort_clause[qp->sort]; if (idx && qp->filter) - query = sqlite3_mprintf("SELECT g.id, g.persistentid, f.album, f.album_sort, COUNT(f.id), 1, f.album_artist, f.songartistid FROM files f, groups g WHERE f.songalbumid = g.persistentid AND g.type = %d AND f.disabled = 0 AND %s GROUP BY f.album, g.name %s %s;", G_ALBUMS, qp->filter, sort, idx); + query = sqlite3_mprintf("SELECT f.songalbumid, f.songalbumid, f.album, f.album_sort, COUNT(f.id), 1, f.album_artist, f.songartistid FROM files f WHERE f.disabled = 0 AND %s GROUP BY f.songalbumid %s %s;", qp->filter, sort, idx); else if (idx) - query = sqlite3_mprintf("SELECT g.id, g.persistentid, f.album, f.album_sort, COUNT(f.id), 1, f.album_artist, f.songartistid FROM files f, groups g WHERE f.songalbumid = g.persistentid AND g.type = %d AND f.disabled = 0 GROUP BY f.album, g.name %s %s;", G_ALBUMS, sort, idx); + query = sqlite3_mprintf("SELECT f.songalbumid, f.songalbumid, f.album, f.album_sort, COUNT(f.id), 1, f.album_artist, f.songartistid FROM files f WHERE f.disabled = 0 GROUP BY f.songalbumid %s %s;", sort, idx); else if (qp->filter) - query = sqlite3_mprintf("SELECT g.id, g.persistentid, f.album, f.album_sort, COUNT(f.id), 1, f.album_artist, f.songartistid FROM files f, groups g WHERE f.songalbumid = g.persistentid AND g.type = %d AND f.disabled = 0 AND %s GROUP BY f.album, g.name %s;", G_ALBUMS, qp->filter, sort); + query = sqlite3_mprintf("SELECT f.songalbumid, f.songalbumid, f.album, f.album_sort, COUNT(f.id), 1, f.album_artist, f.songartistid FROM files f WHERE f.disabled = 0 AND %s GROUP BY f.songalbumid %s;", qp->filter, sort); else - query = sqlite3_mprintf("SELECT g.id, g.persistentid, f.album, f.album_sort, COUNT(f.id), 1, f.album_artist, f.songartistid FROM files f, groups g WHERE f.songalbumid = g.persistentid AND g.type = %d AND f.disabled = 0 GROUP BY f.album, g.name %s;", G_ALBUMS, sort); + query = sqlite3_mprintf("SELECT f.songalbumid, f.songalbumid, f.album, f.album_sort, COUNT(f.id), 1, f.album_artist, f.songartistid FROM files f WHERE f.disabled = 0 GROUP BY f.songalbumid %s;", sort); if (!query) { @@ -1236,13 +1242,13 @@ db_build_query_group_artists(struct query_params *qp, char **q) sort = sort_clause[qp->sort]; if (idx && qp->filter) - query = sqlite3_mprintf("SELECT g.id, g.persistentid, f.album_artist, f.album_artist_sort, COUNT(f.id), COUNT(DISTINCT f.songalbumid), f.album_artist, f.songartistid FROM files f, groups g WHERE f.songartistid = g.persistentid AND g.type = %d AND f.disabled = 0 AND %s GROUP BY f.album_artist, g.name %s %s;", G_ARTISTS, qp->filter, sort, idx); + query = sqlite3_mprintf("SELECT f.songartistid, f.songartistid, f.album_artist, f.album_artist_sort, COUNT(f.id), COUNT(DISTINCT f.songalbumid), f.album_artist, f.songartistid FROM files f WHERE f.disabled = 0 AND %s GROUP BY f.songartistid %s %s;", qp->filter, sort, idx); else if (idx) - query = sqlite3_mprintf("SELECT g.id, g.persistentid, f.album_artist, f.album_artist_sort, COUNT(f.id), COUNT(DISTINCT f.songalbumid), f.album_artist, f.songartistid FROM files f, groups g WHERE f.songartistid = g.persistentid AND g.type = %d AND f.disabled = 0 GROUP BY f.album_artist, g.name %s %s;", G_ARTISTS, sort, idx); + query = sqlite3_mprintf("SELECT f.songartistid, f.songartistid, f.album_artist, f.album_artist_sort, COUNT(f.id), COUNT(DISTINCT f.songalbumid), f.album_artist, f.songartistid FROM files f WHERE f.disabled = 0 GROUP BY f.songartistid %s %s;", sort, idx); else if (qp->filter) - query = sqlite3_mprintf("SELECT g.id, g.persistentid, f.album_artist, f.album_artist_sort, COUNT(f.id), COUNT(DISTINCT f.songalbumid), f.album_artist, f.songartistid FROM files f, groups g WHERE f.songartistid = g.persistentid AND g.type = %d AND f.disabled = 0 AND %s GROUP BY f.album_artist, g.name %s;", G_ARTISTS, qp->filter, sort); + query = sqlite3_mprintf("SELECT f.songartistid, f.songartistid, f.album_artist, f.album_artist_sort, COUNT(f.id), COUNT(DISTINCT f.songalbumid), f.album_artist, f.songartistid FROM files f WHERE f.disabled = 0 AND %s GROUP BY f.songartistid %s;", qp->filter, sort); else - query = sqlite3_mprintf("SELECT g.id, g.persistentid, f.album_artist, f.album_artist_sort, COUNT(f.id), COUNT(DISTINCT f.songalbumid), f.album_artist, f.songartistid FROM files f, groups g WHERE f.songartistid = g.persistentid AND g.type = %d AND f.disabled = 0 GROUP BY f.album_artist, g.name %s;", G_ARTISTS, sort); + query = sqlite3_mprintf("SELECT f.songartistid, f.songartistid, f.album_artist, f.album_artist_sort, COUNT(f.id), COUNT(DISTINCT f.songalbumid), f.album_artist, f.songartistid FROM files f WHERE f.disabled = 0 GROUP BY f.songartistid %s;", sort); if (!query) { @@ -1444,17 +1450,17 @@ db_build_query_browse(struct query_params *qp, char *field, char *sort_field, ch } if (idx && qp->filter) - query = sqlite3_mprintf("SELECT DISTINCT f.%s, f.%s FROM files f WHERE f.disabled = 0 AND f.%s != ''" - " AND %s %s %s;", field, sort_field, field, qp->filter, sort, idx); + query = sqlite3_mprintf("SELECT f.%s, f.%s FROM files f WHERE f.disabled = 0 AND f.%s != ''" + " AND %s GROUP BY f.%s %s %s;", field, sort_field, field, qp->filter, field, sort, idx); else if (idx) - query = sqlite3_mprintf("SELECT DISTINCT f.%s, f.%s FROM files f WHERE f.disabled = 0 AND f.%s != ''" - " %s %s;", field, sort_field, field, sort, idx); + query = sqlite3_mprintf("SELECT f.%s, f.%s FROM files f WHERE f.disabled = 0 AND f.%s != ''" + " GROUP BY f.%s %s %s;", field, sort_field, field, field, sort, idx); else if (qp->filter) - query = sqlite3_mprintf("SELECT DISTINCT f.%s, f.%s FROM files f WHERE f.disabled = 0 AND f.%s != ''" - " AND %s %s;", field, sort_field, field, qp->filter, sort); + query = sqlite3_mprintf("SELECT f.%s, f.%s FROM files f WHERE f.disabled = 0 AND f.%s != ''" + " AND %s GROUP BY f.%s %s;", field, sort_field, field, qp->filter, field, sort); else - query = sqlite3_mprintf("SELECT DISTINCT f.%s, f.%s FROM files f WHERE f.disabled = 0 AND f.%s != '' %s", - field, sort_field, field, sort); + query = sqlite3_mprintf("SELECT f.%s, f.%s FROM files f WHERE f.disabled = 0 AND f.%s != '' GROUP BY f.%s %s", + field, sort_field, field, field, sort); free(sort); @@ -2359,6 +2365,101 @@ db_file_fetch_byid(int id) #undef Q_TMPL } +static int +db_group_get_id(enum group_type type, const char *artist, const char *album, uint32_t *id) +{ +#define Q_TMPL "SELECT id FROM groups WHERE type = %d AND name = LOWER(TRIM(%Q)) || '==' || LOWER(TRIM(%Q));" + + sqlite3_stmt *stmt; + char *query; + int ret; + + // Select artist and album from groups + query = sqlite3_mprintf(Q_TMPL, type, artist, (album ? album : "")); + if (!query) + { + DPRINTF(E_LOG, L_DB, "Out of memory for query string\n"); + return -1; + } + + DPRINTF(E_DBG, L_DB, "Running query '%s'\n", query); + + ret = db_blocking_prepare_v2(query, strlen(query) + 1, &stmt, NULL); + if (ret != SQLITE_OK) + { + DPRINTF(E_LOG, L_DB, "Could not prepare statement: %s\n", sqlite3_errmsg(hdl)); + + sqlite3_free(query); + return -1; + } + + ret = db_blocking_step(stmt); + + if (ret == SQLITE_DONE) + { + DPRINTF(E_DBG, L_DB, "No results\n"); + sqlite3_finalize(stmt); + sqlite3_free(query); + + *id = 0; + return 0; + } + else if (ret == SQLITE_ROW) + { + *id = sqlite3_column_int(stmt, 0); + + sqlite3_finalize(stmt); + sqlite3_free(query); + return 0; + } + + DPRINTF(E_LOG, L_DB, "Could not step: %s\n", sqlite3_errmsg(hdl)); + sqlite3_finalize(stmt); + sqlite3_free(query); + return -1; + +#undef Q_TMPL +} + +static int +db_group_add(enum group_type type, const char *artist, const char *album, uint32_t *id) +{ +#define Q_TMPL "INSERT OR REPLACE INTO groups (type, name) VALUES (%d, LOWER(TRIM(%Q)) || '==' || LOWER(TRIM(%Q)));" + + char *query; + char *errmsg; + int ret; + + // Select artist and album from groups + query = sqlite3_mprintf(Q_TMPL, type, artist, (album ? album : "")); + if (!query) + { + DPRINTF(E_LOG, L_DB, "Out of memory for query string\n"); + return -1; + } + + DPRINTF(E_DBG, L_DB, "Running query '%s'\n", query); + + ret = db_exec(query, &errmsg); + if (ret != SQLITE_OK) + { + DPRINTF(E_LOG, L_DB, "Error '%s' while runnning '%s'\n", errmsg, query); + sqlite3_free(errmsg); + sqlite3_free(query); + *id = 0; + return -1; + } + + sqlite3_free(errmsg); + sqlite3_free(query); + + *id = sqlite3_last_insert_rowid(hdl); + + return 0; + +#undef Q_TMPL +} + int db_file_add(struct media_file_info *mfi) { @@ -2377,11 +2478,13 @@ db_file_add(struct media_file_info *mfi) " %Q, %" PRIi64 ", %" PRIi64 ", %" PRIi64 ", %" PRIi64 ", %d, %" PRIi64 "," \ " %Q, %d, %d, %d, %d, TRIM(%Q)," \ " %d, TRIM(%Q), TRIM(%Q), TRIM(%Q), %d, %d," \ - " daap_songalbumid(LOWER(TRIM(%Q)), ''), daap_songalbumid(LOWER(TRIM(%Q)), LOWER(TRIM(%Q))), " \ + " %" PRIi64 ", %" PRIi64 ", " \ " TRIM(%Q), TRIM(%Q), TRIM(%Q), TRIM(%Q), TRIM(%Q));" char *query; char *errmsg; + uint32_t albumid; + uint32_t artistid; int ret; @@ -2391,6 +2494,43 @@ db_file_add(struct media_file_info *mfi) return -1; } + // Select artist and album from groups + ret = db_group_get_id(G_ALBUMS, mfi->album_artist, mfi->album, &albumid); + if (ret < 0) + { + DPRINTF(E_LOG, L_DB, "Could not get albumid for artist '%s' album '%s'\n", mfi->album_artist, mfi->album); + return -1; + } + + if (albumid == 0) + { + ret = db_group_add(G_ALBUMS, mfi->album_artist, mfi->album, &albumid); + if (ret < 0) + { + DPRINTF(E_LOG, L_DB, "Could not add artist '%s' album '%s'\n", mfi->album_artist, mfi->album); + return -1; + } + } + + ret = db_group_get_id(G_ARTISTS, mfi->album_artist, NULL, &artistid); + if (ret < 0) + { + DPRINTF(E_LOG, L_DB, "Could not get artistid for artist '%s'\n", mfi->album_artist); + return -1; + } + + if (artistid == 0) + { + ret = db_group_add(G_ARTISTS, mfi->album_artist, NULL, &artistid); + if (ret < 0) + { + DPRINTF(E_LOG, L_DB, "Could not add artist '%s'\n", mfi->album_artist); + return -1; + } + } + + mfi->songalbumid = albumid; + mfi->songartistid = artistid; mfi->db_timestamp = (uint64_t)time(NULL); mfi->time_added = mfi->db_timestamp; @@ -2410,7 +2550,8 @@ db_file_add(struct media_file_info *mfi) mfi->contentrating, mfi->bits_per_sample, mfi->album_artist, mfi->media_kind, mfi->tv_series_name, mfi->tv_episode_num_str, mfi->tv_network_name, mfi->tv_episode_sort, mfi->tv_season_num, - mfi->album_artist, mfi->album_artist, mfi->album, mfi->title_sort, mfi->artist_sort, mfi->album_sort, + mfi->songartistid, mfi->songalbumid, + mfi->title_sort, mfi->artist_sort, mfi->album_sort, mfi->composer_sort, mfi->album_artist_sort); if (!query) @@ -2454,11 +2595,15 @@ db_file_update(struct media_file_info *mfi) " bits_per_sample = %d, album_artist = TRIM(%Q)," \ " media_kind = %d, tv_series_name = TRIM(%Q), tv_episode_num_str = TRIM(%Q)," \ " tv_network_name = TRIM(%Q), tv_episode_sort = %d, tv_season_num = %d," \ - " songartistid = daap_songalbumid(LOWER(TRIM(%Q)), ''), songalbumid = daap_songalbumid(LOWER(TRIM(%Q)), LOWER(TRIM(%Q)))," \ + " songartistid = %" PRIi64 ", songalbumid = %" PRIi64 "," \ " title_sort = TRIM(%Q), artist_sort = TRIM(%Q), album_sort = TRIM(%Q), composer_sort = TRIM(%Q), album_artist_sort = TRIM(%Q)" \ " WHERE id = %d;" + +// struct media_file_info *oldmfi; char *query; char *errmsg; + uint32_t albumid; + uint32_t artistid; int ret; if (mfi->id == 0) @@ -2467,6 +2612,55 @@ db_file_update(struct media_file_info *mfi) return -1; } + /* + oldmfi = db_file_fetch_byid(mfi->id); + + if (!oldmfi) + { + DPRINTF(E_WARN, L_DB, "File with id '%d' does not exist\n", mfi->id); + return -1; + } + + free_mfi(oldmfi, 0); + */ + + ret = db_group_get_id(G_ALBUMS, mfi->album_artist, mfi->album, &albumid); + if (ret < 0) + { + DPRINTF(E_LOG, L_DB, "Could not get albumid for artist '%s' album '%s'\n", mfi->album_artist, mfi->album); + return -1; + } + + if (albumid == 0) + { + ret = db_group_add(G_ALBUMS, mfi->album_artist, mfi->album, &albumid); + if (ret < 0) + { + DPRINTF(E_LOG, L_DB, "Could not add artist '%s' album '%s'\n", mfi->album_artist, mfi->album); + return -1; + } + } + + ret = db_group_get_id(G_ARTISTS, mfi->album_artist, NULL, &artistid); + if (ret < 0) + { + DPRINTF(E_LOG, L_DB, "Could not get artistid for artist '%s'\n", mfi->album_artist); + return -1; + } + + if (artistid == 0) + { + ret = db_group_add(G_ARTISTS, mfi->album_artist, NULL, &artistid); + if (ret < 0) + { + DPRINTF(E_LOG, L_DB, "Could not add artist '%s'\n", mfi->album_artist); + return -1; + } + } + + mfi->songalbumid = albumid; + mfi->songartistid = artistid; + mfi->db_timestamp = (uint64_t)time(NULL); if (mfi->time_modified == 0) @@ -2484,7 +2678,7 @@ db_file_update(struct media_file_info *mfi) mfi->bits_per_sample, mfi->album_artist, mfi->media_kind, mfi->tv_series_name, mfi->tv_episode_num_str, mfi->tv_network_name, mfi->tv_episode_sort, mfi->tv_season_num, - mfi->album_artist, mfi->album_artist, mfi->album, + mfi->songartistid, mfi->songalbumid, mfi->title_sort, mfi->artist_sort, mfi->album_sort, mfi->composer_sort, mfi->album_artist_sort, mfi->id); @@ -3109,16 +3303,11 @@ db_pl_enable_bycookie(uint32_t cookie, char *path) /* Groups */ -int -db_groups_clear(void) -{ - return db_query_run("DELETE FROM groups;", 0, 1); -} static enum group_type db_group_type_bypersistentid(int64_t persistentid) { -#define Q_TMPL "SELECT g.type FROM groups g WHERE g.persistentid = %" PRIi64 ";" +#define Q_TMPL "SELECT g.type FROM groups g WHERE g.id = %" PRIi64 ";" char *query; sqlite3_stmt *stmt; int ret; @@ -3170,61 +3359,6 @@ db_group_type_bypersistentid(int64_t persistentid) #undef Q_TMPL } -int -db_group_persistentid_byid(int id, int64_t *persistentid) -{ -#define Q_TMPL "SELECT g.persistentid FROM groups g WHERE g.id = %d;" - char *query; - sqlite3_stmt *stmt; - int ret; - - query = sqlite3_mprintf(Q_TMPL, id); - if (!query) - { - DPRINTF(E_LOG, L_DB, "Out of memory for query string\n"); - - return -1; - } - - DPRINTF(E_DBG, L_DB, "Running query '%s'\n", query); - - ret = db_blocking_prepare_v2(query, -1, &stmt, NULL); - if (ret != SQLITE_OK) - { - DPRINTF(E_LOG, L_DB, "Could not prepare statement: %s\n", sqlite3_errmsg(hdl)); - - sqlite3_free(query); - return -1; - } - - ret = db_blocking_step(stmt); - if (ret != SQLITE_ROW) - { - if (ret == SQLITE_DONE) - DPRINTF(E_DBG, L_DB, "No results\n"); - else - DPRINTF(E_LOG, L_DB, "Could not step: %s\n", sqlite3_errmsg(hdl)); - - sqlite3_finalize(stmt); - sqlite3_free(query); - return -1; - } - - *persistentid = sqlite3_column_int64(stmt, 0); - -#ifdef DB_PROFILE - while (db_blocking_step(stmt) == SQLITE_ROW) - ; /* EMPTY */ -#endif - - sqlite3_finalize(stmt); - sqlite3_free(query); - - return 0; - -#undef Q_TMPL -} - /* Remotes */ static int @@ -4303,8 +4437,7 @@ db_perthread_deinit(void) " id INTEGER PRIMARY KEY NOT NULL," \ " type INTEGER NOT NULL," \ " name VARCHAR(1024) NOT NULL COLLATE DAAP," \ - " persistentid INTEGER NOT NULL," \ - "CONSTRAINT groups_type_unique_persistentid UNIQUE (type, persistentid)" \ + "CONSTRAINT groups_type_unique_name UNIQUE (type, name)" \ ");" #define T_PAIRINGS \ @@ -4335,7 +4468,7 @@ db_perthread_deinit(void) "CREATE INDEX IF NOT EXISTS idx_sari ON files(songartistid);" #define I_SONGALBUMID \ - "CREATE INDEX IF NOT EXISTS idx_sali ON files(songalbumid);" + "CREATE INDEX IF NOT EXISTS idx_sali ON files(songalbumid, disabled, media_kind, album_sort, disc, track);" #define I_STATEMKINDSARI \ "CREATE INDEX IF NOT EXISTS idx_state_mkind_sari ON files(disabled, media_kind, songartistid);" @@ -4350,10 +4483,13 @@ db_perthread_deinit(void) "CREATE INDEX IF NOT EXISTS idx_albumartist ON files(album_artist, album_artist_sort);" #define I_COMPOSER \ - "CREATE INDEX IF NOT EXISTS idx_composer ON files(composer, composer_sort);" + "CREATE INDEX IF NOT EXISTS idx_composer ON files(disabled, media_kind, composer, data_kind);" + +#define I_GENRE \ + "CREATE INDEX IF NOT EXISTS idx_genre ON files(disabled, media_kind, genre, data_kind);" #define I_TITLE \ - "CREATE INDEX IF NOT EXISTS idx_title ON files(title, title_sort);" + "CREATE INDEX IF NOT EXISTS idx_title ON files(disabled, media_kind, title_sort, data_kind);" #define I_ALBUM \ "CREATE INDEX IF NOT EXISTS idx_album ON files(album, album_sort);" @@ -4370,27 +4506,13 @@ db_perthread_deinit(void) #define I_PLITEMID \ "CREATE INDEX IF NOT EXISTS idx_playlistid ON playlistitems(playlistid, filepath);" -#define I_GRP_TYPE_PERSIST \ - "CREATE INDEX IF NOT EXISTS idx_grp_type_persist ON groups(type, persistentid);" +#define I_GRP_TYPE_NAME \ + "CREATE INDEX IF NOT EXISTS idx_grp_type_name ON groups(type, name);" #define I_PAIRING \ "CREATE INDEX IF NOT EXISTS idx_pairingguid ON pairings(guid);" -#define TRG_GROUPS_INSERT_FILES \ - "CREATE TRIGGER update_groups_new_file AFTER INSERT ON files FOR EACH ROW" \ - " BEGIN" \ - " INSERT OR IGNORE INTO groups (type, name, persistentid) VALUES (1, NEW.album, NEW.songalbumid);" \ - " INSERT OR IGNORE INTO groups (type, name, persistentid) VALUES (2, NEW.album_artist, NEW.songartistid);" \ - " END;" - -#define TRG_GROUPS_UPDATE_FILES \ - "CREATE TRIGGER update_groups_update_file AFTER UPDATE OF songalbumid ON files FOR EACH ROW" \ - " BEGIN" \ - " INSERT OR IGNORE INTO groups (type, name, persistentid) VALUES (1, NEW.album, NEW.songalbumid);" \ - " INSERT OR IGNORE INTO groups (type, name, persistentid) VALUES (2, NEW.album_artist, NEW.songartistid);" \ - " END;" - #define Q_PL1 \ "INSERT INTO playlists (id, title, type, query, db_timestamp, path, idx, special_id)" \ " VALUES(1, 'Library', 1, '1 = 1', 0, '', 0, 0);" @@ -4421,9 +4543,9 @@ db_perthread_deinit(void) " VALUES(8, 'Purchased', 0, 'media_kind = 1024', 0, '', 0, 8);" */ -#define SCHEMA_VERSION 15 +#define SCHEMA_VERSION 16 #define Q_SCVER \ - "INSERT INTO admin (key, value) VALUES ('schema_version', '15');" + "INSERT INTO admin (key, value) VALUES ('schema_version', '16');" struct db_init_query { char *query; @@ -4447,11 +4569,12 @@ static const struct db_init_query db_init_queries[] = { I_STATEMKINDSARI, "create state/mkind/sari index" }, { I_STATEMKINDSALI, "create state/mkind/sali index" }, - { I_ARTIST, "create artist index" }, + { I_ARTIST, "create artist index" }, { I_ALBUMARTIST, "create album_artist index" }, - { I_COMPOSER, "create composer index" }, - { I_TITLE, "create title index" }, - { I_ALBUM, "create album index" }, + { I_COMPOSER, "create composer index" }, + { I_GENRE, "create genre index" }, + { I_TITLE, "create title index" }, + { I_ALBUM, "create album index" }, { I_PL_PATH, "create playlist path index" }, { I_PL_DISABLED, "create playlist state index" }, @@ -4459,13 +4582,10 @@ static const struct db_init_query db_init_queries[] = { I_FILEPATH, "create file path index" }, { I_PLITEMID, "create playlist id index" }, - { I_GRP_TYPE_PERSIST, "create groups type/persistentid index" }, + { I_GRP_TYPE_NAME, "create groups type/name index" }, { I_PAIRING, "create pairing guid index" }, - { TRG_GROUPS_INSERT_FILES, "create trigger update_groups_new_file" }, - { TRG_GROUPS_UPDATE_FILES, "create trigger update_groups_update_file" }, - { Q_PL1, "create default playlist" }, { Q_PL2, "create default smart playlist 'Music'" }, { Q_PL3, "create default smart playlist 'Movies'" }, @@ -5468,6 +5588,190 @@ db_upgrade_v15(void) #undef Q_DUMP } + +/* Upgrade from schema v15 to v16 */ + +#define U_V16_DROP_IDX \ + "DROP INDEX idx_grp_type_persist;" + +#define U_V16_DROP_TRG_NEW \ + "DROP TRIGGER update_groups_new_file;" + +#define U_V16_DROP_TRG_UPDATE \ + "DROP TRIGGER update_groups_update_file;" + +#define U_V16_DROP_TBL_GROUPS \ + "DROP TABLE groups;" + +#define U_V16_CREATE_TBL_GROUPS \ + "CREATE TABLE IF NOT EXISTS groups (" \ + " id INTEGER PRIMARY KEY NOT NULL," \ + " type INTEGER NOT NULL," \ + " name VARCHAR(1024) NOT NULL COLLATE DAAP," \ + "CONSTRAINT groups_type_unique_name UNIQUE (type, name)" \ + ");" + +#define U_V16_CREATE_IDX \ + "CREATE INDEX IF NOT EXISTS idx_grp_type_name ON groups(type, name);" + +#define U_V16_DROP_IDX_TITLE \ + "DROP INDEX idx_title;" + +#define U_V16_CREATE_IDX_TITLE \ + "CREATE INDEX IF NOT EXISTS idx_title ON files(disabled, media_kind, title_sort, data_kind);" + +#define U_V16_DROP_IDX_SONGALBUMID \ + "DROP INDEX idx_sali;" + +#define U_V16_CREATE_IDX_SONGALBUMID \ + "CREATE INDEX IF NOT EXISTS idx_sali ON files(songalbumid, disabled, media_kind, album_sort, disc, track);" + +#define U_V16_CREATE_IDX_GENRE \ + "CREATE INDEX IF NOT EXISTS idx_genre ON files(disabled, media_kind, genre, data_kind);" + +#define U_V16_DROP_IDX_COMPOSER \ + "DROP INDEX idx_composer;" + +#define U_V16_CREATE_IDX_COMPOSER \ + "CREATE INDEX IF NOT EXISTS idx_composer ON files(disabled, media_kind, composer, data_kind);" + +#define U_V16_SCVER \ + "UPDATE admin SET value = '16' WHERE key = 'schema_version';" + +static const struct db_init_query db_upgrade_v16_queries[] = + { + { U_V16_DROP_IDX, "drop index type/persistentid" }, + { U_V16_DROP_TRG_NEW, "drop trigger new files" }, + { U_V16_DROP_TRG_UPDATE, "drop trigger update files" }, + { U_V16_DROP_TBL_GROUPS, "drop table groups" }, + { U_V16_CREATE_TBL_GROUPS, "create table groups" }, + { U_V16_CREATE_IDX, "create index type/name" }, + { U_V16_DROP_IDX_TITLE, "drop index title on files" }, + { U_V16_CREATE_IDX_TITLE, "create index title on files" }, + { U_V16_DROP_IDX_SONGALBUMID, "drop index songalbumid on files" }, + { U_V16_CREATE_IDX_SONGALBUMID, "create index songalbumid on files" }, + { U_V16_CREATE_IDX_GENRE, "create index genre on files" }, + { U_V16_DROP_IDX_COMPOSER, "drop index composer on files" }, + { U_V16_CREATE_IDX_COMPOSER, "create index composer on files" }, + { U_V16_SCVER, "set schema_version to 16" }, + }; + +static int +db_upgrade_v16_persistentid(enum group_type type, char *artist, char *album) +{ + sqlite3_stmt *stmt; + char *query; + char *errmsg; + int id; + int ret; + + id = 0; + + query = sqlite3_mprintf("SELECT id FROM groups WHERE type = %d AND name = LOWER(TRIM(%Q)) || '==' || LOWER(TRIM(%Q));", type, (artist ? artist : ""), (album ? album : "")); + + ret = sqlite3_prepare_v2(hdl, query, -1, &stmt, NULL); + if (ret != SQLITE_OK) + { + DPRINTF(E_LOG, L_DB, "Could not prepare statement: %s\n", sqlite3_errmsg(hdl)); + return -1; + } + + ret = sqlite3_step(stmt); + if (ret == SQLITE_ROW) + { + id = sqlite3_column_int(stmt, 0); + } + else + { + if (ret != SQLITE_DONE) + { + DPRINTF(E_LOG, L_DB, "Could not step: %s\n", sqlite3_errmsg(hdl)); + } + id = 0; + } + + sqlite3_finalize(stmt); + sqlite3_free(query); + + if (id > 0) + return id; + + query = sqlite3_mprintf("INSERT INTO groups (type, name) VALUES (%d, LOWER(TRIM(%Q)) || '==' || LOWER(TRIM(%Q)));", type, (artist ? artist : ""), (album ? album : "")); + ret = sqlite3_exec(hdl, query, NULL, NULL, &errmsg); + if (ret == SQLITE_OK) + { + id = sqlite3_last_insert_rowid(hdl); + } + else + { + DPRINTF(E_LOG, L_DB, "Error adding group: %s\n", errmsg); + return -1; + } + + sqlite3_free(query); + sqlite3_free(errmsg); + + return id; +} + +static int +db_upgrade_v16(void) +{ + sqlite3_stmt *stmt; + char *query; + char *uquery; + char *errmsg; + char *artist; + char *album; + int id; + int artistid; + int albumid; + int ret; + + query = "SELECT id, album_artist, album FROM files;"; + + DPRINTF(E_DBG, L_DB, "Running query '%s'\n", query); + + ret = sqlite3_prepare_v2(hdl, query, -1, &stmt, NULL); + if (ret != SQLITE_OK) + { + DPRINTF(E_LOG, L_DB, "Could not prepare statement: %s\n", sqlite3_errmsg(hdl)); + return -1; + } + + while ((ret = sqlite3_step(stmt)) == SQLITE_ROW) + { + id = sqlite3_column_int(stmt, 0); + artist = (char *)sqlite3_column_text(stmt, 1); + album = (char *)sqlite3_column_text(stmt, 2); + + artistid = db_upgrade_v16_persistentid(G_ARTISTS, artist, NULL); + albumid = db_upgrade_v16_persistentid(G_ALBUMS, artist, album); + + if (artistid <= 0) + { + + } + if (albumid <= 0) + { + + } + uquery = sqlite3_mprintf("UPDATE files SET songartistid = %d, songalbumid = %d WHERE id = %d;", artistid, albumid, id); + ret = sqlite3_exec(hdl, uquery, NULL, NULL, &errmsg); + if (ret != SQLITE_OK) + { + DPRINTF(E_LOG, L_DB, "Error updating files: %s\n", errmsg); + } + + sqlite3_free(uquery); + sqlite3_free(errmsg); + } + + sqlite3_finalize(stmt); + + return 0; +} + static int db_check_version(void) { @@ -5510,6 +5814,14 @@ db_check_version(void) { DPRINTF(E_LOG, L_DB, "Database schema outdated, schema upgrade needed v%d -> v%d\n", cur_ver, SCHEMA_VERSION); + ret = sqlite3_exec(hdl, "BEGIN TRANSACTION;", NULL, NULL, &errmsg); + if (ret != SQLITE_OK) + { + DPRINTF(E_LOG, L_DB, "Error starting transaction: %s\n", errmsg); + sqlite3_free(errmsg); + return -1; + } + switch (cur_ver) { case 10: @@ -5561,6 +5873,15 @@ db_check_version(void) if (ret < 0) return -1; + case 15: + ret = db_generic_upgrade(db_upgrade_v16_queries, sizeof(db_upgrade_v16_queries) / sizeof(db_upgrade_v16_queries[0])); + if (ret < 0) + return -1; + + ret = db_upgrade_v16(); + if (ret < 0) + return -1; + break; default: @@ -5568,6 +5889,14 @@ db_check_version(void) return -1; } + ret = sqlite3_exec(hdl, "END TRANSACTION;", NULL, NULL, &errmsg); + if (ret != SQLITE_OK) + { + DPRINTF(E_LOG, L_DB, "Error ending transaction: %s\n", errmsg); + sqlite3_free(errmsg); + return -1; + } + /* What about some housekeeping work, eh? */ DPRINTF(E_INFO, L_DB, "Now vacuuming database, this may take some time...\n"); diff --git a/src/db.h b/src/db.h index a7d72bf4..499792e9 100644 --- a/src/db.h +++ b/src/db.h @@ -458,13 +458,6 @@ db_pl_disable_bymatch(char *path, char *strip, uint32_t cookie); int db_pl_enable_bycookie(uint32_t cookie, char *path); -/* Groups */ -int -db_groups_clear(void); - -int -db_group_persistentid_byid(int id, int64_t *persistentid); - /* Remotes */ int db_pairing_add(struct pairing_info *pi); diff --git a/src/filescanner.c b/src/filescanner.c index 439a2331..d0495fc7 100644 --- a/src/filescanner.c +++ b/src/filescanner.c @@ -1125,21 +1125,6 @@ filescanner(void *arg) pthread_exit(NULL); } - ret = db_groups_clear(); - if (ret < 0) - { - DPRINTF(E_LOG, L_SCAN, "Error: could not clear old groups from DB\n"); - - pthread_exit(NULL); - } - - /* Recompute all songartistids and songalbumids, in case the SQLite DB got transferred - * to a different host; the hash is not portable. - * It will also rebuild the groups we just cleared. - */ - db_files_update_songartistid(); - db_files_update_songalbumid(); - if (cfg_getbool(cfg_getsec(cfg, "library"), "filescan_disable")) bulk_scan(F_SCAN_BULK | F_SCAN_FAST); else