Commit a8ca6ce1 authored by Hugo Beauzée-Luyssen's avatar Hugo Beauzée-Luyssen

Media: Store the number of playlist a media is present in

This can be used to avoid full table scans when knowing if a media is
present in a playlist or not
parent 641a9d5d
......@@ -77,7 +77,8 @@ Media::Media( MediaLibraryPtr ml, sqlite::Row& row )
>> m_title
>> m_filename
>> m_isFavorite
>> m_isPresent;
>> m_isPresent
>> m_nbPlaylists;
}
Media::Media( MediaLibraryPtr ml, const std::string& title, Type type )
......@@ -97,6 +98,7 @@ Media::Media( MediaLibraryPtr ml, const std::string& title, Type type )
, m_filename( title )
, m_isFavorite( false )
, m_isPresent( true )
, m_nbPlaylists( 0 )
, m_metadata( m_ml, IMetadata::EntityType::Media )
, m_changed( false )
{
......@@ -310,6 +312,17 @@ unsigned int Media::releaseDate() const
return m_releaseDate;
}
uint32_t Media::nbPlaylists() const
{
return m_nbPlaylists.load( std::memory_order_relaxed );
}
void Media::udpateNbPlaylist(int32_t increment) const
{
// Only update the cached representation, let the triggers handle the DB values
m_nbPlaylists.fetch_add( increment, std::memory_order_relaxed );
}
const IMetadata& Media::metadata( IMedia::MetadataType type ) const
{
using MDType = typename std::underlying_type<IMedia::MetadataType>::type;
......@@ -559,7 +572,7 @@ void Media::createTable( sqlite::Connection* connection )
sqlite::Tools::executeRequest( connection, req );
}
void Media::createTriggers( sqlite::Connection* connection )
void Media::createTriggers( sqlite::Connection* connection, uint32_t modelVersion )
{
const std::string reqs[] = {
#include "database/tables/Media_triggers_v14.sql"
......@@ -567,6 +580,27 @@ void Media::createTriggers( sqlite::Connection* connection )
for ( const auto& req : reqs )
sqlite::Tools::executeRequest( connection, req );
if ( modelVersion >= 14 )
{
sqlite::Tools::executeRequest( connection,
"CREATE TRIGGER IF NOT EXISTS increment_media_nb_playlist AFTER INSERT ON "
" PlaylistMediaRelation "
" BEGIN "
" UPDATE " + policy::MediaTable::Name + " SET nb_playlists = nb_playlists + 1 "
" WHERE id_media = new.media_id;"
" END;"
);
sqlite::Tools::executeRequest( connection,
"CREATE TRIGGER IF NOT EXISTS decrement_media_nb_playlist AFTER DELETE ON "
" PlaylistMediaRelation "
" BEGIN "
" UPDATE " + policy::MediaTable::Name + " SET nb_playlists = nb_playlists - 1 "
" WHERE id_media = old.media_id;"
" END;"
);
}
}
bool Media::addLabel( LabelPtr label )
......
......@@ -66,7 +66,7 @@ class Media : public IMedia, public DatabaseHelpers<Media, policy::MediaTable>
static std::shared_ptr<Media> create( MediaLibraryPtr ml, Type type, const std::string& fileName );
static void createTable( sqlite::Connection* connection );
static void createTriggers( sqlite::Connection* connection );
static void createTriggers( sqlite::Connection* connection, uint32_t modelVersion );
virtual int64_t id() const override;
virtual Type type() const override;
......@@ -113,6 +113,8 @@ class Media : public IMedia, public DatabaseHelpers<Media, policy::MediaTable>
bool setThumbnail( const std::string& thumbnail, Thumbnail::Origin origin );
virtual unsigned int insertionDate() const override;
virtual unsigned int releaseDate() const override;
uint32_t nbPlaylists() const;
void udpateNbPlaylist( int32_t increment ) const;
virtual const IMetadata& metadata( MetadataType type ) const override;
virtual bool setMetadata( MetadataType type, const std::string& value ) override;
......@@ -172,6 +174,7 @@ private:
std::string m_filename;
bool m_isFavorite;
bool m_isPresent;
mutable std::atomic_uint32_t m_nbPlaylists;
// Auto fetched related properties
mutable Cache<AlbumTrackPtr> m_albumTrack;
......
......@@ -178,7 +178,7 @@ void MediaLibrary::createAllTriggers()
Album::createTriggers( m_dbConnection.get() );
AlbumTrack::createTriggers( m_dbConnection.get() );
Artist::createTriggers( m_dbConnection.get(), dbModelVersion );
Media::createTriggers( m_dbConnection.get() );
Media::createTriggers( m_dbConnection.get(), dbModelVersion );
File::createTriggers( m_dbConnection.get() );
Genre::createTriggers( m_dbConnection.get() );
Playlist::createTriggers( m_dbConnection.get() );
......@@ -1012,7 +1012,7 @@ void MediaLibrary::migrateModel3to5()
for ( const auto& req : reqs )
sqlite::Tools::executeRequest( getConn(), req );
// Re-create triggers removed in the process
Media::createTriggers( getConn() );
Media::createTriggers( getConn(), 5 );
Playlist::createTriggers( getConn() );
t->commit();
}
......@@ -1041,7 +1041,7 @@ void MediaLibrary::migrateModel7to8()
sqlite::Tools::executeRequest( getConn(), req );
// Re-create triggers removed in the process
Artist::createTriggers( getConn(), 8u );
Media::createTriggers( getConn() );
Media::createTriggers( getConn(), 5 );
File::createTriggers( getConn() );
t->commit();
}
......@@ -1167,7 +1167,7 @@ void MediaLibrary::migrateModel13to14()
Show::createTable( dbConn );
VideoTrack::createTable( dbConn );
// Re-create triggers removed in the process
Media::createTriggers( dbConn );
Media::createTriggers( dbConn, 14 );
AlbumTrack::createTriggers( dbConn );
Album::createTriggers( dbConn );
Artist::createTriggers( dbConn, 14 );
......
......@@ -192,9 +192,12 @@ bool Playlist::add( const IMedia& media, unsigned int position )
LOG_ERROR( "Can't add a media without any files to a playlist" );
return false;
}
return sqlite::Tools::executeInsert( m_ml->getConn(), req, media.id(),
if ( sqlite::Tools::executeInsert( m_ml->getConn(), req, media.id(),
(*mainFile)->mrl(), m_id,
sqlite::ForeignKey{ position } );
sqlite::ForeignKey{ position } ) == false )
return false;
static_cast<const Media&>( media ).udpateNbPlaylist( 1 );
return true;
}
catch (const sqlite::errors::ConstraintViolation& ex)
{
......@@ -257,7 +260,10 @@ bool Playlist::remove( const IMedia& media )
{
static const std::string req = "DELETE FROM PlaylistMediaRelation WHERE "
"playlist_id = ? AND media_id = ?";
return sqlite::Tools::executeDelete( m_ml->getConn(), req, m_id, media.id() );
if ( sqlite::Tools::executeDelete( m_ml->getConn(), req, m_id, media.id() ) == false )
return false;
static_cast<const Media&>( media ).udpateNbPlaylist( -1 );
return true;
}
void Playlist::createTable( sqlite::Connection* dbConn, uint32_t dbModel )
......
......@@ -51,6 +51,12 @@ std::to_string( static_cast<typename std::underlying_type<IMedia::Type>::type>(
"type = " + std::to_string( static_cast<typename std::underlying_type<IMedia::Type>::type>(
IMedia::Type::Unknown ) ),
/******************* Populate new media.nb_playlists **************************/
"UPDATE " + MediaTable::Name + " SET nb_playlists = "
"(SELECT COUNT(media_id) FROM PlaylistMediaRelation WHERE media_id = id_media )"
"WHERE id_media IN (SELECT media_id FROM PlaylistMediaRelation)",
/******************* Migrate metadata table ***********************************/
"CREATE TEMPORARY TABLE " + MetadataTable::Name + "_backup"
"("
......
......@@ -16,6 +16,7 @@
"filename TEXT,"
"is_favorite BOOLEAN NOT NULL DEFAULT 0,"
"is_present BOOLEAN NOT NULL DEFAULT 1,"
"nb_playlists UNSIGNED INTEGER NOT NULL DEFAULT 0,"
"FOREIGN KEY(thumbnail_id) REFERENCES " + policy::ThumbnailTable::Name
+ "(id_thumbnail)"
......
......@@ -750,6 +750,46 @@ TEST_F( Medias, VacuumOldExternal )
ASSERT_EQ( nullptr, s1 );
}
TEST_F( Medias, NbPlaylists )
{
auto m = std::static_pointer_cast<Media>( ml->addExternalMedia( "media.mkv" ) );
ASSERT_EQ( 0u, m->nbPlaylists() );
auto playlist = ml->createPlaylist( "playlisẗ" );
auto res = playlist->append( *m );
ASSERT_TRUE( res );
ASSERT_EQ( 1u, m->nbPlaylists() );
Reload();
m = ml->media( m->id() );
ASSERT_EQ( 1u, m->nbPlaylists() );
playlist = ml->playlist( playlist->id() );
playlist->remove( m->id() );
ASSERT_EQ( 0u, m->nbPlaylists() );
Reload();
m = ml->media( m->id() );
ASSERT_EQ( 0u, m->nbPlaylists() );
playlist = ml->playlist( playlist->id() );
playlist->append( *m );
ASSERT_EQ( 1u, m->nbPlaylists() );
Reload();
m = ml->media( m->id() );
ASSERT_EQ( 1u, m->nbPlaylists() );
ml->deletePlaylist( playlist->id() );
Reload();
m = ml->media( m->id() );
ASSERT_EQ( 0u, m->nbPlaylists() );
}
class FetchMedia : public Tests
{
protected:
......
......@@ -174,7 +174,7 @@ TEST_F( DbModel, Upgrade12to13 )
// We can't check for the number of albums anymore since they are deleted
// as part of 13 -> 14 migration
CheckNbTriggers( 32 );
CheckNbTriggers( 34 );
}
TEST_F( DbModel, Upgrade13to14 )
......@@ -202,14 +202,19 @@ TEST_F( DbModel, Upgrade13to14 )
auto playlistMedia = playlists[0]->media()->all();
ASSERT_EQ( 3u, playlistMedia.size() );
ASSERT_EQ( media[0]->id(), playlistMedia[0]->id() );
ASSERT_EQ( 1u, std::static_pointer_cast<Media>( playlistMedia[0] )->nbPlaylists() );
ASSERT_EQ( media[1]->id(), playlistMedia[1]->id() );
ASSERT_EQ( 1u, std::static_pointer_cast<Media>( playlistMedia[1] )->nbPlaylists() );
ASSERT_EQ( media[2]->id(), playlistMedia[2]->id() );
ASSERT_EQ( 1u, std::static_pointer_cast<Media>( playlistMedia[2] )->nbPlaylists() );
ASSERT_EQ( IMedia::Type::External, media[2]->type() );
auto externalMedia = ml->media( 99 );
ASSERT_NE( nullptr, externalMedia );
ASSERT_EQ( IMedia::Type::Unknown, externalMedia->type() );
ASSERT_EQ( 0u, std::static_pointer_cast<Media>( externalMedia )->nbPlaylists() );
CheckNbTriggers( 32 );
CheckNbTriggers( 34 );
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment