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 */