From a97c1c33d08db14f557329168b57413805d6b096 Mon Sep 17 00:00:00 2001 From: Uwe Klotz Date: Tue, 27 Jul 2021 15:21:55 +0200 Subject: [PATCH 1/3] Disable update of play counter for SQLite versions before 3.33.0 --- src/library/dao/trackdao.cpp | 72 +++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 26 deletions(-) diff --git a/src/library/dao/trackdao.cpp b/src/library/dao/trackdao.cpp index 5235ce9716e..278004365d9 100644 --- a/src/library/dao/trackdao.cpp +++ b/src/library/dao/trackdao.cpp @@ -9,6 +9,10 @@ #include #include +#ifdef __SQLITE3__ +#include +#endif // __SQLITE3__ + #include "library/coverart.h" #include "library/coverartutils.h" #include "library/dao/analysisdao.h" @@ -2224,33 +2228,49 @@ bool TrackDAO::updatePlayCounterFromPlayedHistory( // NOTE: The played flag for the current session is NOT updated! // The current session is unaffected, because the corresponding // playlist cannot be deleted. - FwdSqlQuery query( - m_database, - QStringLiteral( - "UPDATE library SET " - "timesplayed=q.timesplayed," - "last_played_at=q.last_played_at " - "FROM(" - "SELECT " - "PlaylistTracks.track_id as id," - "COUNT(PlaylistTracks.track_id) as timesplayed," - "MAX(PlaylistTracks.pl_datetime_added) as last_played_at " - "FROM PlaylistTracks " - "JOIN Playlists ON " - "PlaylistTracks.playlist_id=Playlists.id " - "WHERE Playlists.hidden=%2 " - "GROUP BY PlaylistTracks.track_id" - ") q " - "WHERE library.id=q.id " - "AND library.id IN (%1)") - .arg(joinTrackIdList(trackIds), - QString::number(PlaylistDAO::PLHT_SET_LOG))); - VERIFY_OR_DEBUG_ASSERT(!query.hasError()) { - return false; - } - VERIFY_OR_DEBUG_ASSERT(query.execPrepared()) { - return false; + // + // https://www.sqlite.org/lang_update.html#upfrom + // UPDATE-FROM is supported beginning in SQLite version 3.33.0 (2020-08-14) + // https://bugs.launchpad.net/mixxx/+bug/1937941 +#ifdef __SQLITE3__ + if (sqlite3_libversion_number() >= 3033000) { +#endif // __SQLITE3__ + FwdSqlQuery query( + m_database, + QStringLiteral( + "UPDATE library SET " + "timesplayed=q.timesplayed," + "last_played_at=q.last_played_at " + "FROM(" + "SELECT " + "PlaylistTracks.track_id as id," + "COUNT(PlaylistTracks.track_id) as timesplayed," + "MAX(PlaylistTracks.pl_datetime_added) as last_played_at " + "FROM PlaylistTracks " + "JOIN Playlists ON " + "PlaylistTracks.playlist_id=Playlists.id " + "WHERE Playlists.hidden=%2 " + "GROUP BY PlaylistTracks.track_id" + ") q " + "WHERE library.id=q.id " + "AND library.id IN (%1)") + .arg(joinTrackIdList(trackIds), + QString::number(PlaylistDAO::PLHT_SET_LOG))); + VERIFY_OR_DEBUG_ASSERT(!query.hasError()) { + return false; + } + VERIFY_OR_DEBUG_ASSERT(query.execPrepared()) { + return false; + } +#ifdef __SQLITE3__ + } else { + // FIXME: Update tracks one by one + qWarning() << "FIXME: Update play counter of" + << trackIds.size() + << "track(s) from history for SQLite version" + << sqlite3_libversion(); } +#endif // __SQLITE3__ // TODO: DAOs should be passive and simply execute queries. They // should neither make assumptions about transaction boundaries // nor receive or emit any signals. From 0c555663035ecf58a026b7050ea00da063759868 Mon Sep 17 00:00:00 2001 From: Uwe Klotz Date: Tue, 27 Jul 2021 22:54:47 +0200 Subject: [PATCH 2/3] TrackDAO: Fix updating of play counter for not played tracks --- src/library/dao/trackdao.cpp | 38 ++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/src/library/dao/trackdao.cpp b/src/library/dao/trackdao.cpp index 278004365d9..ecfcd153d92 100644 --- a/src/library/dao/trackdao.cpp +++ b/src/library/dao/trackdao.cpp @@ -2235,7 +2235,8 @@ bool TrackDAO::updatePlayCounterFromPlayedHistory( #ifdef __SQLITE3__ if (sqlite3_libversion_number() >= 3033000) { #endif // __SQLITE3__ - FwdSqlQuery query( + const QString trackIdList = joinTrackIdList(trackIds); + auto updatePlayed = FwdSqlQuery( m_database, QStringLiteral( "UPDATE library SET " @@ -2249,17 +2250,42 @@ bool TrackDAO::updatePlayCounterFromPlayedHistory( "FROM PlaylistTracks " "JOIN Playlists ON " "PlaylistTracks.playlist_id=Playlists.id " - "WHERE Playlists.hidden=%2 " + "WHERE Playlists.hidden=:playlistHidden " "GROUP BY PlaylistTracks.track_id" ") q " "WHERE library.id=q.id " "AND library.id IN (%1)") - .arg(joinTrackIdList(trackIds), - QString::number(PlaylistDAO::PLHT_SET_LOG))); - VERIFY_OR_DEBUG_ASSERT(!query.hasError()) { + .arg(trackIdList)); + updatePlayed.bindValue( + QStringLiteral(":playlistHidden"), + PlaylistDAO::PLHT_SET_LOG); + VERIFY_OR_DEBUG_ASSERT(!updatePlayed.hasError()) { return false; } - VERIFY_OR_DEBUG_ASSERT(query.execPrepared()) { + VERIFY_OR_DEBUG_ASSERT(updatePlayed.execPrepared()) { + return false; + } + auto updateNotPlayed = FwdSqlQuery( + m_database, + QStringLiteral( + "UPDATE library SET " + "timesplayed=0," + "last_played_at=NULL " + "WHERE library.id NOT IN(" + "SELECT PlaylistTracks.track_id " + "FROM PlaylistTracks " + "JOIN Playlists ON " + "PlaylistTracks.playlist_id=Playlists.id " + "WHERE Playlists.hidden=:playlistHidden " + "AND PlaylistTracks.track_id IN (%1))") + .arg(trackIdList)); + updateNotPlayed.bindValue( + QStringLiteral(":playlistHidden"), + PlaylistDAO::PLHT_SET_LOG); + VERIFY_OR_DEBUG_ASSERT(!updateNotPlayed.hasError()) { + return false; + } + VERIFY_OR_DEBUG_ASSERT(updateNotPlayed.execPrepared()) { return false; } #ifdef __SQLITE3__ From 705d07b3453b60c15be6a8778f8eae3c22ef3a3e Mon Sep 17 00:00:00 2001 From: Uwe Klotz Date: Tue, 27 Jul 2021 22:56:37 +0200 Subject: [PATCH 3/3] TrackDAO: Fix updating of play counter for SQLite version prior 3.33.0 --- src/library/dao/trackdao.cpp | 69 +++++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 5 deletions(-) diff --git a/src/library/dao/trackdao.cpp b/src/library/dao/trackdao.cpp index ecfcd153d92..78d1bc305a6 100644 --- a/src/library/dao/trackdao.cpp +++ b/src/library/dao/trackdao.cpp @@ -2290,11 +2290,70 @@ bool TrackDAO::updatePlayCounterFromPlayedHistory( } #ifdef __SQLITE3__ } else { - // FIXME: Update tracks one by one - qWarning() << "FIXME: Update play counter of" - << trackIds.size() - << "track(s) from history for SQLite version" - << sqlite3_libversion(); + // TODO: Remove this workaround after dropping support for Ubuntu 20.04 + auto playCounterQuery = FwdSqlQuery( + m_database, + QStringLiteral( + "SELECT " + "COUNT(PlaylistTracks.track_id)," + "MAX(PlaylistTracks.pl_datetime_added) " + "FROM PlaylistTracks " + "JOIN Playlists ON " + "PlaylistTracks.playlist_id=Playlists.id " + "WHERE Playlists.hidden=:playlistHidden " + "AND PlaylistTracks.track_id=:trackId")); + playCounterQuery.bindValue( + QStringLiteral(":playlistHidden"), + PlaylistDAO::PLHT_SET_LOG); + auto trackUpdateQuery = FwdSqlQuery( + m_database, + QStringLiteral( + "UPDATE library SET " + "timesplayed=:timesplayed," + "last_played_at=:last_played_at " + "WHERE library.id=:trackId")); + for (const auto& trackId : trackIds) { + playCounterQuery.bindValue( + QStringLiteral(":trackId"), + trackId.toVariant()); + VERIFY_OR_DEBUG_ASSERT(!playCounterQuery.hasError()) { + continue; + } + VERIFY_OR_DEBUG_ASSERT(playCounterQuery.execPrepared()) { + continue; + } + QVariant timesplayed; + QVariant last_played_at; + DEBUG_ASSERT(last_played_at.isNull()); + if (playCounterQuery.next()) { + timesplayed = playCounterQuery.fieldValue(0); + last_played_at = playCounterQuery.fieldValue(1); + // Result is a single row + DEBUG_ASSERT(!playCounterQuery.next()); + } + if (timesplayed.isNull()) { + // Never played and timesplayed should not be NULL + DEBUG_ASSERT(last_played_at.isNull()); + timesplayed = 0; + } + trackUpdateQuery.bindValue( + QStringLiteral(":trackId"), + trackId.toVariant()); + trackUpdateQuery.bindValue( + QStringLiteral(":timesplayed"), + timesplayed); + trackUpdateQuery.bindValue( + QStringLiteral(":last_played_at"), + last_played_at); + VERIFY_OR_DEBUG_ASSERT(!trackUpdateQuery.hasError()) { + continue; + } + VERIFY_OR_DEBUG_ASSERT(trackUpdateQuery.execPrepared()) { + continue; + } + // 0 for tracks that have just been deleted + DEBUG_ASSERT(trackUpdateQuery.numRowsAffected() <= 1); + } } #endif // __SQLITE3__ // TODO: DAOs should be passive and simply execute queries. They