File indexing completed on 2025-01-19 04:24:25
0001 /**************************************************************************************** 0002 * Copyright (c) 2007 Maximilian Kossick <maximilian.kossick@googlemail.com> * 0003 * * 0004 * This program is free software; you can redistribute it and/or modify it under * 0005 * the terms of the GNU General Public License as published by the Free Software * 0006 * Foundation; either version 2 of the License, or (at your option) any later * 0007 * version. * 0008 * * 0009 * This program is distributed in the hope that it will be useful, but WITHOUT ANY * 0010 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * 0011 * PARTICULAR PURPOSE. See the GNU General Public License for more details. * 0012 * * 0013 * You should have received a copy of the GNU General Public License along with * 0014 * this program. If not, see <http://www.gnu.org/licenses/>. * 0015 ****************************************************************************************/ 0016 0017 #ifndef SQLREGISTRY_H 0018 #define SQLREGISTRY_H 0019 0020 #include "SqlMeta.h" 0021 #include "amarok_sqlcollection_export.h" 0022 0023 #include <QHash> 0024 #include <QMutex> 0025 #include <QObject> 0026 #include <QPair> 0027 #include <QTimer> 0028 #include <QList> 0029 0030 0031 class TestSqlAlbum; 0032 class TestSqlArtist; 0033 class TestSqlTrack; 0034 class TestSqlCollectionLocation; 0035 0036 namespace Collections { 0037 class SqlCollection; 0038 class SqlQueryMakerInternal; 0039 } 0040 typedef QPair<int, QString> TrackPath; 0041 0042 /** The SqlRegistry class buffers Meta objects from an Sql database. 0043 This class can be considered a memory cache for the Sql database. 0044 All requests for Meta objects like SqlTrack should go through here. 0045 0046 Some notes regarding performance: 0047 Scanning of nearly 10000 tracks on my lokal disk takes over 2 minutes. 0048 The second time it only a little over 4 seconds. However I would not see the 0049 second scan as a valid usecase. 0050 Putting 10000 tracks from memory directly into the database with 0051 single inserts takes around 12 seconds. 0052 This time however increases dramatically with the amount of tracks. 0053 50000 tracks take around 15 minutes. 0054 The reason is the many indices that need to be updated. The tracks 0055 table e.g. has around 15 indices. 0056 0057 To increase the performance we are currently using two tricks. 0058 1. Prevent queries. 0059 The SqlScanResultProcessor will query all tracks in the database. 0060 The SqlRegistry is caching them as usually but while the scanner 0061 is running it will not clean the cache. 0062 The SqlScanResultProcessor will also cache all urls entries. 0063 0064 2. Combine inserts and updates. 0065 All dirty tracks will be written in one big insert or with delayed 0066 updates. 0067 The ScanResultProcessor will block database access for five 0068 seconds at a time to collect dirty tracks for such a batch update. 0069 Have a look at the AbstractTrackTableCommitter to see how this 0070 is done. 0071 0072 Note: updating tracks is not optimized for single changes. 0073 A single update is only done very seldom and does currently not 0074 need to be optimized. 0075 */ 0076 class AMAROK_SQLCOLLECTION_EXPORT SqlRegistry : public QObject 0077 { 0078 Q_OBJECT 0079 0080 public: 0081 explicit SqlRegistry(Collections::SqlCollection *collection); 0082 ~SqlRegistry() override; 0083 0084 /** Searches a directory entry in the scanned directories 0085 This function searches an existing directory entry. 0086 @param path the directory path 0087 @param mtime if mtime is != 0 then the mtime of the entry is updated 0088 @returns the directory id 0089 */ 0090 int getDirectory( const QString &path, uint mtime = 0 ); 0091 0092 Meta::TrackPtr getTrack( int urlId ); 0093 Meta::TrackPtr getTrack( const QString &path ); 0094 0095 /** Returns the track located at the given url or a new one if not existing. 0096 This is kind of dangerous because it can generate a new track in 0097 the database without a file on the filesystem, so don't call it unless 0098 you really want tracks to be generate. 0099 The new track must be committed by writing 0100 some other meta information. 0101 Use SqlCollection::trackForUrl instead. 0102 */ 0103 Meta::TrackPtr getTrack( int deviceId, const QString &rpath, int directoryId, const QString &uidUrl ); 0104 0105 /** Returns a track from a specific uid. 0106 Returns a complete track or 0. 0107 */ 0108 Meta::TrackPtr getTrackFromUid( const QString &uid ); 0109 0110 Meta::ArtistPtr getArtist( const QString &name ); 0111 Meta::ArtistPtr getArtist( int id ); 0112 0113 Meta::GenrePtr getGenre( const QString &name ); 0114 Meta::GenrePtr getGenre( int id ); 0115 0116 Meta::ComposerPtr getComposer( const QString &name ); 0117 Meta::ComposerPtr getComposer( int id ); 0118 0119 Meta::YearPtr getYear( int year, int yearId = -1 ); 0120 0121 Meta::AlbumPtr getAlbum( const QString &album, const QString &artist ); 0122 Meta::AlbumPtr getAlbum( int id ); 0123 0124 Meta::LabelPtr getLabel( const QString &label ); 0125 Meta::LabelPtr getLabel( int id ); 0126 0127 /** Call this function to collect changes for the sql database. 0128 This function can be called in preparation of larger updates. 0129 */ 0130 void blockDatabaseUpdate(); 0131 0132 /** Unblocks one blockDatabaseUpdate call. */ 0133 void unblockDatabaseUpdate(); 0134 0135 private Q_SLOTS: 0136 /** empytCache clears up the different hash tables by unrefing all pointers that are no longer ref'd by anyone else. 0137 SqlRegistry is calling this function periodically. 0138 This is no free ticket for modifying the database directly as 0139 parties holding Meta pointers will still have the old status. 0140 */ 0141 void emptyCache(); 0142 0143 private: 0144 typedef QPair<QString, QString> AlbumKey; 0145 0146 // only SqlTrack can change this 0147 /** Updates the uid of an already cached track. 0148 @return true if the update was successful. 0149 */ 0150 bool updateCachedUrl( const QString &oldPath, const QString &newPath ); 0151 0152 /** Updates the uid of an already cached track. 0153 @return true if the update was successful. 0154 */ 0155 bool updateCachedUid( const QString &oldUid, const QString &newUid ); 0156 0157 /** 0158 * Removes the track and associated entries (url, statistics, lyrics, labels) 0159 * from the database and the cache (but not from the file system). This function 0160 * is normally called by SqlTrack. Do not call directly unless you know what you 0161 * do. 0162 */ 0163 void removeTrack( int urlId, const QString &uid ); 0164 0165 // --- functions needed to commit a track 0166 0167 /** Returns a string with all the values needed to be committed to the urls table */ 0168 QString getTrackUrlsValues( Meta::SqlTrack *track ); 0169 0170 /** Returns a string with all the values needed to be committed to the tracks table */ 0171 QString getTrackTracksValues( Meta::SqlTrack *track ); 0172 0173 /** Returns a string with all the values needed to be committed to the statistics table */ 0174 QString getTrackStatisticsValues( Meta::SqlTrack *track ); 0175 0176 void commitDirtyTracks(); 0177 0178 0179 0180 friend class Meta::SqlTrack; 0181 0182 // only the query maker creates Metas like this 0183 Meta::TrackPtr getTrack( int id, const QStringList &rowData ); 0184 Meta::ArtistPtr getArtist( int id, const QString &name ); 0185 Meta::GenrePtr getGenre( int id, const QString &name ); 0186 Meta::ComposerPtr getComposer( int id, const QString &name ); 0187 Meta::AlbumPtr getAlbum( int id, const QString &album, int artistId ); 0188 Meta::LabelPtr getLabel( int id, const QString &label ); 0189 0190 friend class Collections::SqlQueryMakerInternal; 0191 0192 // we don't care about the ordering so use the faster QHash 0193 QHash<TrackPath, Meta::TrackPtr > m_trackMap; 0194 QHash<QString, Meta::TrackPtr > m_uidMap; 0195 QHash<QString, Meta::ArtistPtr > m_artistMap; 0196 QHash<int, Meta::ArtistPtr > m_artistIdMap; 0197 QHash<QString, Meta::ComposerPtr > m_composerMap; 0198 QHash<QString, Meta::GenrePtr > m_genreMap; 0199 QHash<int, Meta::YearPtr > m_yearMap; 0200 QHash<AlbumKey, Meta::AlbumPtr > m_albumMap; 0201 QHash<int, Meta::AlbumPtr > m_albumIdMap; 0202 QHash<QString, Meta::LabelPtr > m_labelMap; 0203 0204 QMutex m_trackMutex; // guards access to m_trackMap, m_uidMap 0205 QMutex m_artistMutex; // guards access to m_artistMap, m_artistIdMap 0206 QMutex m_composerMutex; // guards access to m_composerMap 0207 QMutex m_genreMutex; // guards access to m_genreMap 0208 QMutex m_yearMutex; // guards access to m_yearMap 0209 QMutex m_albumMutex; // guards access to m_albumMap, m_albumIdMap 0210 QMutex m_labelMutex; // guards access to m_labelMap 0211 0212 /** The timer is used for cleaning up the different caches. */ 0213 QTimer *m_timer; 0214 0215 Collections::SqlCollection *m_collection; 0216 0217 QMutex m_blockMutex; // protects the count and all the dirty sets. 0218 int m_blockDatabaseUpdateCount; 0219 0220 /** A set of all tracks that need to be written to the database */ 0221 QSet< Meta::SqlTrackPtr > m_dirtyTracks; 0222 0223 /** A set of all tracks that are dirty. 0224 Dirty years do not need to be written back as they are 0225 invariant. However we need to notice the observers and 0226 invalidate the cache. */ 0227 QSet< Meta::SqlYearPtr > m_dirtyYears; 0228 QSet< Meta::SqlGenrePtr > m_dirtyGenres; 0229 QSet< Meta::SqlAlbumPtr > m_dirtyAlbums; 0230 QSet< Meta::SqlArtistPtr > m_dirtyArtists; 0231 QSet< Meta::SqlComposerPtr > m_dirtyComposers; 0232 0233 /** Set to true when something was added or removed form the database */ 0234 bool m_collectionChanged; 0235 0236 friend class SqlScanResultProcessor; 0237 0238 // all those classes need to call emptyCache 0239 friend class TestSqlScanManager; 0240 friend class TestSqlAlbum; 0241 friend class TestSqlArtist; 0242 friend class TestSqlTrack; 0243 friend class TestSqlCollectionLocation; 0244 }; 0245 0246 #endif /* SQLREGISTRY_H */