// (c) 2004 Mark Kretschmann // (c) 2004 Christian Muehlhaeuser // (c) 2004 Sami Nieminen // (c) 2005 Ian Monroe // (c) 2005 Jeff Mitchell // (c) 2005 Isaiah Damron // (c) 2005 Alexandre Pereira de Oliveira // (c) 2006 Jonas Hurrelmann // (c) 2006 Shane King // (c) 2006 Peter C. Ndikuwera // See COPYING file for licensing information. #ifndef AMAROK_COLLECTIONDB_H #define AMAROK_COLLECTIONDB_H #include "engineobserver.h" #include "threadmanager.h" //baseclass #include "amarok_export.h" #include #include //stack allocated #include #include #include #include //baseclass #include //baseclass #include //stack allocated #include //stack allocated #include #include #include namespace KIO { class Job; } class DbConnection; class CoverFetcher; class MetaBundle; class OrganizeCollectionDialog; class PodcastChannelBundle; class PodcastEpisodeBundle; class TQListViewItem; class Scrobbler; class DbConfig {}; class SqliteConfig : public DbConfig { public: SqliteConfig( const TQString& /* dbfile */ ); TQString dbFile() const { return m_dbfile; } private: TQString m_dbfile; }; class MySqlConfig : public DbConfig { public: MySqlConfig( const TQString& /* host */, const int /* port */, const TQString& /* database */, const TQString& /* username */, const TQString& /* password */); TQString host() const { return m_host; } int port() const { return m_port; } TQString database() const { return m_database; } TQString username() const { return m_username; } TQString password() const { return m_password; } private: TQString m_host; int m_port; TQString m_database; TQString m_username; TQString m_password; }; class PostgresqlConfig : public DbConfig { public: PostgresqlConfig( const TQString& /* host */, const int /* port */, const TQString& /* database */, const TQString& /* username */, const TQString& /* password */); TQString host() const { return m_host; } int port() const { return m_port; } TQString database() const { return m_database; } TQString username() const { return m_username; } TQString password() const { return m_password; } private: TQString m_host; int m_port; TQString m_database; TQString m_username; TQString m_password; }; class DbConnection { public: enum DbConnectionType { sqlite = 0, mysql = 1, postgresql = 2 }; DbConnection(); virtual ~DbConnection() {} virtual TQStringList query( const TQString& /* statement */, bool suppressDebug ) = 0; virtual int insert( const TQString& /* statement */, const TQString& /* table */ ) = 0; bool isInitialized() const { return m_initialized; } virtual bool isConnected() const = 0; virtual TQString lastError() const { return "None"; } protected: bool m_initialized; }; typedef struct sqlite3 sqlite3; typedef struct sqlite3_context sqlite3_context; typedef struct Mem sqlite3_value; class SqliteConnection : public DbConnection { public: SqliteConnection( const SqliteConfig* /* config */ ); ~SqliteConnection(); TQStringList query( const TQString& /* statement */, bool suppressDebug = false ); int insert( const TQString& /* statement */, const TQString& /* table */ ); bool isConnected()const { return true; } private: static void sqlite_rand( sqlite3_context *context, int /*argc*/, sqlite3_value ** /*argv*/ ); static void sqlite_power( sqlite3_context *context, int argc, sqlite3_value **argv ); static void sqlite_like_new( sqlite3_context *context, int argc, sqlite3_value **argv ); sqlite3* m_db; }; #ifdef USE_MYSQL typedef struct st_mysql MYSQL; class MySqlConnection : public DbConnection { public: MySqlConnection( const MySqlConfig* /* config */ ); ~MySqlConnection(); TQStringList query( const TQString& /* statement */, bool suppressDebug = false ); int insert( const TQString& /* statement */, const TQString& /* table */ ); bool isConnected()const { return m_connected; } TQString lastError() const { return m_error; } private: void setMysqlError(); MYSQL* m_db; bool m_connected; TQString m_error; }; #endif #ifdef USE_POSTGRESQL typedef struct pg_conn PGconn; class PostgresqlConnection : public DbConnection { public: PostgresqlConnection( const PostgresqlConfig* /* config */ ); ~PostgresqlConnection(); TQStringList query( const TQString& /* statement */, bool suppressDebug = false ); int insert( const TQString& /* statement */, const TQString& /* table */ ); bool isConnected()const { return m_connected; } TQString lastError() const { return m_error; } private: void setPostgresqlError(); PGconn* m_db; bool m_connected; TQString m_error; }; #endif class LIBAMAROK_EXPORT CollectionDB : public TQObject, public EngineObserver { Q_OBJECT TQ_OBJECT friend class SimilarArtistsInsertionJob; signals: void scanStarted(); void scanDone( bool changed ); void databaseEngineChanged(); void databaseUpdateDone(); void scoreChanged( const TQString &url, float score ); void ratingChanged( const TQString &url, int rating ); void labelsChanged( const TQString &url ); void fileMoved( const TQString &srcUrl, const TQString &dstUrl ); void fileMoved( const TQString &srcUrl, const TQString &dstUrl, const TQString &uniqueid ); void fileDeleted( const TQString &absPath ); void fileDeleted( const TQString &absPath, const TQString &uniqueid ); void fileAdded( const TQString &absPath ); void fileAdded( const TQString &absPath, const TQString &uniqueid ); void filesAdded( const TQMap &map ); void uniqueIdChanged( const TQString &url, const TQString &originalid, const TQString &newid ); void coverChanged( const TQString &artist, const TQString &album ); //whenever a cover changes void coverFetched( const TQString &artist, const TQString &album ); //only when fetching from amazon void coverRemoved( const TQString &artist, const TQString &album ); void coverFetcherError( const TQString &error ); void similarArtistsFetched( const TQString &artist ); void tagsChanged( const MetaBundle &bundle ); void tagsChanged( const TQString &oldArtist, const TQString &oldAlbum ); void imageFetched( const TQString &remoteURL ); //for fetching remote podcast images public: CollectionDB(); ~CollectionDB(); static CollectionDB *instance(); /** * performs all initializations which require directory or URL data stored in the * database. */ void initDirOperations(); enum labelTypes { typeUser = 1 }; //add new types add the end! TQString escapeString(TQString string ) const { return #ifdef USE_MYSQL // We have to escape "\" for mysql, but can't do so for sqlite ( m_dbConnType == DbConnection::mysql ) ? string.replace("\\", "\\\\").replace( '\'', "''" ) : #endif string.replace( '\'', "''" ); } TQString boolT() const { if (getDbConnectionType() == DbConnection::postgresql) return "true"; else return "1"; } TQString boolF() const { if (getDbConnectionType() == DbConnection::postgresql) return "false"; else return "0"; } inline bool boolFromSql( const TQString &b ) { return ( b == boolT() || b == "t" ); } //textColumnType should be used for normal strings, which need to be compared //either case-sensitively or -insensitively TQString textColumnType( int length=255 ) const { if ( getDbConnectionType() == DbConnection::postgresql ) return "TEXT"; else return TQString("VARCHAR(%1)").arg(length); } //exactTextColumnType should be used for strings that must be stored exactly, such //as URLs (necessary for holding control chars etc. if present in URL), except for //trailing spaces. Comparisions should always be done case-sensitively. //As we create indices on these columns, we have to restrict them to //<= 255 chars for mysql < 5.0.3 TQString exactTextColumnType( int length=1024 ) const { if ( getDbConnectionType() == DbConnection::mysql ) return TQString( "VARBINARY(%1)" ).arg( length>255 ? 255 : length ); else return textColumnType( length ); } // We might consider using LONGTEXT type, as some lyrics could be VERY long..??? TQString longTextColumnType() const { if ( getDbConnectionType() == DbConnection::postgresql ) return "TEXT"; else return "TEXT"; } TQString randomFunc() const { if ( getDbConnectionType() == DbConnection::postgresql ) return "random()"; else return "RAND()"; } inline static TQString exactCondition( const TQString &right ); static TQString likeCondition( const TQString &right, bool anyBegin=false, bool anyEnd=false ); int getType() { return getDbConnectionType(); } //sql helper methods TQStringList query( const TQString& statement, bool suppressDebug = false ); int insert( const TQString& statement, const TQString& table ); /** * TODO: write doc * @param showAll * @return a string which can be appended to an existing sql where statement */ TQString deviceidSelection( const bool showAll = false ); /** * converts the result of a query which contains a deviceid and a relative path * to a list of absolute paths. the order of entries in each result row must be * deviceid first, relative path second. * @param result the result of the sql query, deviceid first, relative path second * @return a list of urls */ TQStringList URLsFromQuery( const TQStringList &result ) const; /** * converts the result list of a amarok-sql query to a list of urls */ KURL::List URLsFromSqlDrag( const TQStringList &values ) const; //table management methods bool isEmpty(); bool isValid(); TQString adminValue( TQString noption ); void setAdminValue( TQString noption, TQString value ); void createTables( const bool temporary = false ); void createIndices( ); void createPermanentIndices(); void dropTables( const bool temporary = false); void clearTables( const bool temporary = false); void copyTempTables( ); void prepareTempTables(); uint artistID( TQString value, bool autocreate = true, const bool temporary = false, bool exact = true ); uint composerID( TQString value, bool autocreate = true, const bool temporary = false, bool exact = true ); uint albumID( TQString value, bool autocreate = true, const bool temporary = false, bool exact = true ); uint genreID( TQString value, bool autocreate = true, const bool temporary = false, bool exact = true ); uint yearID( TQString value, bool autocreate = true, const bool temporary = false, bool exact = true ); bool isDirInCollection( TQString path ); bool isFileInCollection( const TQString &url ); TQString getURL( const MetaBundle &bundle ); void removeDirFromCollection( TQString path ); void removeSongsInDir( TQString path, TQMap *tagsRemoved = 0 ); void removeSongs( const KURL::List& urls ); void updateDirStats( TQString path, const long datetime, const bool temporary = false ); //song methods bool addSong( MetaBundle* bundle, const bool incremental = false ); void aftCheckPermanentTables( const TQString &currdeviceid, const TQString &currid, const TQString &currurl ); void doAFTStuff( MetaBundle *bundle, const bool tempTables = true ); void emitFileAdded( const TQString &absPath, const TQString &uniqueid = TQString() ); void emitFilesAdded( const TQMap &map ) { emit filesAdded( map ); } void emitFileDeleted( const TQString &absPath, const TQString &uniqueid = TQString() ); bool newUniqueIdForFile( const TQString &path ); bool removeUniqueIdFromFile( const TQString &path ); TQString urlFromUniqueId( const TQString &id ); TQString uniqueIdFromUrl( const KURL &url ); //podcast methods /// Insert a podcast channel into the database. If @param replace is true, replace the row /// use updatePodcastChannel() always in preference bool addPodcastChannel( const PodcastChannelBundle &pcb, const bool &replace=false ); /// Insert a podcast episode into the database. If @param idToUpdate is provided, replace the row /// use updatePodcastEpisode() always in preference int addPodcastEpisode( const PodcastEpisodeBundle &episode, const int idToUpdate=0 ); int addPodcastFolder( const TQString &name, const int parent_id=0, const bool isOpen=false ); TQValueList getPodcastChannels(); PodcastEpisodeBundle getPodcastEpisodeById( int id ); TQValueList getPodcastEpisodes( const KURL &parent, bool newOnly=false, int limit=-1 ); void removePodcastChannel( const KURL &url ); // will remove all episodes too void removePodcastEpisode( const int id ); void removePodcastFolder( const int id ); void updatePodcastChannel( const PodcastChannelBundle &b ); void updatePodcastEpisode( const int id, const PodcastEpisodeBundle &b ); void updatePodcastFolder( const int folder_id, const TQString &name, const int parent_id=0, const bool isOpen=false ); // these return false when no bundle was available bool getPodcastChannelBundle( const KURL &url, PodcastChannelBundle *channel ); bool getPodcastEpisodeBundle( const KURL &url, PodcastEpisodeBundle *channel ); MetaBundle bundleFromQuery( TQStringList::const_iterator *iter ); /** * The @p bundle parameter's url() will be looked up in the Collection * @param bundle this will be filled in with tags for you * @return true if in the collection */ bool bundleForUrl( MetaBundle* bundle ); TQValueList bundlesByUrls( const KURL::List& urls ); void addAudioproperties( const MetaBundle& bundle ); //Helper function for updateTags void deleteRedundantName( const TQString &table, const TQString &id ); void deleteAllRedundant( const TQString &table ); void updateTags( const TQString &url, const MetaBundle &bundle, const bool updateView = true); void updateURL( const TQString &url, const bool updateView = true ); TQString getUniqueId( const TQString &url ); //statistics methods void addSongPercentage( const TQString &url, float percentage, const TQString &reason, const TQDateTime *playtime = 0 ); float getSongPercentage( const TQString &url ); int getSongRating( const TQString &url ); void setSongPercentage( const TQString &url, float percentage ); void setSongRating( const TQString &url, int percentage, bool toggleHalf = false ); int getPlayCount( const TQString &url ); TQDateTime getFirstPlay( const TQString &url ); TQDateTime getLastPlay( const TQString &url ); void migrateFile( const TQString &oldURL, const TQString &newURL ); bool moveFile( const TQString &src, const TQString &dest, bool overwrite, bool copy = false ); bool organizeFile( const KURL &src, OrganizeCollectionDialog &dialog, bool copy ); //artist methods TQStringList similarArtists( const TQString &artist, uint count ); //album methods void checkCompilations( const TQString &path, const bool temporary = false ); void setCompilation( const KURL::List &urls, bool enabled, bool updateView ); TQString albumSongCount( const TQString &artist_id, const TQString &album_id ); bool albumIsCompilation( const TQString &album_id ); void sanitizeCompilations(); //label methods TQStringList getLabels( const TQString &url, const uint type ); void removeLabels( const TQString &url, const TQStringList &labels, const uint type ); bool addLabel( const TQString &url, const TQString &label, const TQString &uid, const uint type ); void setLabels( const TQString &url, const TQStringList &labels, const TQString &uid, const uint type ); void cleanLabels(); TQStringList favoriteLabels( int type = CollectionDB::typeUser, int count = 10 ); //list methods TQStringList artistList( bool withUnknowns = true, bool withCompilations = true ); TQStringList composerList( bool withUnknowns = true, bool withCompilations = true ); TQStringList albumList( bool withUnknowns = true, bool withCompilations = true ); TQStringList genreList( bool withUnknowns = true, bool withCompilations = true ); TQStringList yearList( bool withUnknowns = true, bool withCompilations = true ); TQStringList labelList(); TQStringList albumListOfArtist( const TQString &artist, bool withUnknown = true, bool withCompilations = true ); TQStringList artistAlbumList( bool withUnknown = true, bool withCompilations = true ); TQStringList albumTracks( const TQString &artist_id, const TQString &album_id ); TQStringList albumDiscTracks( const TQString &artist_id, const TQString &album_id, const TQString &discNumber ); TQStringList artistTracks( const TQString &artist_id ); //cover management methods /** Returns the image from a given URL, network-transparently. * You must run KIO::NetAccess::removeTempFile( tmpFile ) when you are finished using the image; **/ static TQImage fetchImage( const KURL& url, TQString &tmpFile ); /** Saves images located on the user's filesystem */ bool setAlbumImage( const TQString& artist, const TQString& album, const KURL& url ); /** Saves images obtained from CoverFetcher */ bool setAlbumImage( const TQString& artist, const TQString& album, TQImage img, const TQString& amazonUrl = TQString(), const TQString& asin = TQString() ); TQString findAmazonImage( const TQString &artist, const TQString &album, const uint width = 1 ); TQString findDirectoryImage( const TQString& artist, const TQString& album, uint width = 0 ); TQString findEmbeddedImage( const TQString& artist, const TQString& album, uint width = 1 ); TQString findMetaBundleImage( const MetaBundle &trackInformation, const uint = 1 ); /// ensure the sql only return urls to tracks for efficiency static TQPixmap createDragPixmapFromSQL( const TQString &sql, TQString textOverRide=TQString() ); static TQPixmap createDragPixmap( const KURL::List &urls, TQString textOverRide=TQString() ); static const int DRAGPIXMAP_OFFSET_X = -12; static const int DRAGPIXMAP_OFFSET_Y = -28; /* * Retrieves the path to the local copy of the image pointed to by url, * initiates fetching of the remote image if necessary. * @param width the size of the image. 0 == full size, 1 == preview size */ TQString podcastImage( const MetaBundle &bundle, const bool withShadow = false, uint width = 1 ); TQString podcastImage( const TQString &remoteURL, const bool withShadow = false, uint width = 1 ); /** * Retrieves the path to the image for the album of the requested item * @param width the size of the image. 0 == full size, 1 == preview size * @param embedded if not NULL, sets a bool indicating whether the path is an embedded image */ TQString albumImage( const MetaBundle &trackInformation, const bool withShadow = false, uint width = 1, bool* embedded = 0 ); TQString albumImage( const uint artist_id, const uint album_id, const bool withShadow = false, uint width = 1, bool* embedded = 0 ); TQString albumImage( const TQString &artist, const TQString &album, const bool withShadow = false, uint width = 1, bool* embedded = 0 ); TQMap * getItemCoverMap() { return itemCoverMap; } TQMutex * getItemCoverMapMutex() { return itemCoverMapMutex; } bool removeAlbumImage( const uint artist_id, const uint album_id ); bool removeAlbumImage( const TQString &artist, const TQString &album ); static TQString makeShadowedImage( const TQString& albumImage, bool cache = true ); //local cover methods void addImageToAlbum( const TQString& image, TQValueList< TQPair > info, const bool temporary ); TQString notAvailCover( const bool withShadow = false, int width = 1 ); //embedded cover methods void addEmbeddedImage( const TQString& path, const TQString& hash, const TQString& description ); void removeOrphanedEmbeddedImages(); void applySettings(); void setLyrics( const TQString& url, const TQString& lyrics, const TQString &uniqueid = TQString() ); TQString getLyrics( const TQString& url ); /** Remove from the amazon table the item with the specified md5sum **/ void removeInvalidAmazonInfo( const TQString& md5sum ); void newAmazonReloadDate( const TQString& asin, const TQString& locale, const TQString& md5sum ); TQStringList staleImages(); DbConnection::DbConnectionType getDbConnectionType() const { return m_dbConnType; } bool isConnected(); void releasePreviousConnection(TQThread *currThread); void invalidateArtistAlbumCache() { m_validArtistCache=false; m_validComposerCache=false; m_validAlbumCache=false; }; void vacuum(); /** * Cancel the underlying move/copy file action */ void cancelMovingFileJob(); protected: TQCString md5sum( const TQString& artist, const TQString& album, const TQString& file = TQString() ); void engineTrackEnded( int finalPosition, int trackLength, const TQString &reason ); /** Manages regular folder monitoring scan */ void timerEvent( TQTimerEvent* e ); public slots: void fetchCover( TQWidget* parent, const TQString& artist, const TQString& album, bool noedit, TQListViewItem* item = 0 ); void scanMonitor(); void startScan(); void stopScan(); void scanModifiedDirs(); void disableAutoScoring( bool disable = true ) { m_autoScoring = !disable; } void checkDatabase(); private slots: void dirDirty( const TQString& path ); void coverFetcherResult( CoverFetcher* ); void similarArtistsFetched( const TQString& artist, const TQStringList& suggestions ); void fileOperationResult( KIO::Job *job ); // moveFile depends on it void podcastImageResult( KIO::Job *job ); //for fetching remote podcast images void aftMigratePermanentTablesUrl( const TQString& oldUrl, const TQString& newUrl, const TQString& uniqueid ); //AFT-enable stats void aftMigratePermanentTablesUniqueId( const TQString& url, const TQString& oldid, const TQString& newid ); private: //bump DATABASE_VERSION whenever changes to the table structure are made. // This erases tags, album, artist, composer, genre, year, images, embed, directory and related_artists tables. static const int DATABASE_VERSION = 35; // Persistent Tables hold data that is somehow valuable to the user, and can't be erased when rescaning. // When bumping this, write code to convert the data! static const int DATABASE_PERSISTENT_TABLES_VERSION = 19; // Bumping this erases stats table. If you ever need to, write code to convert the data! static const int DATABASE_STATS_VERSION = 12; // When bumping this, you should provide code to convert the data. static const int DATABASE_PODCAST_TABLES_VERSION = 2; static const int DATABASE_AFT_VERSION = 2; // persistent table. you should provide code to convert the data when bumping this static const int DATABASE_DEVICES_VERSION = 1; static const int MONITOR_INTERVAL = 60; //sec static TQDir largeCoverDir(); static TQDir tagCoverDir(); static TQDir cacheCoverDir(); void initialize(); void destroy(); DbConnection* getMyConnection(); //helper methods which perform updates of amarok's database void updateStatsTables(); void updatePersistentTables(); void updatePodcastTables(); //A dirty hack to preserve Group By settings in Collection Browser after addition //of Composer table void updateGroupBy(); void customEvent( TQCustomEvent * ); // helpers for embedded images TQString loadHashFile( const TQCString& hash, uint width ); bool extractEmbeddedImage( const MetaBundle &trackInformation, TQCString& hash ); //general management methods void createStatsTable(); void dropStatsTable(); void createPersistentTables(); void dropPersistentTables(); void createPodcastTables(); void dropPodcastTables(); void createDevicesTable(); void dropDevicesTable(); //Archived forms of the above. useful for providing a linear upgrade routine that //stays the same void createStatsTableV8(); void createStatsTableV10( bool temp ); void dropStatsTableV1(); void createPersistentTablesV12(); void createPersistentTablesV14( bool temp ); void dropPersistentTablesV14(); void createPodcastTablesV2( bool temp ); void dropPodcastTablesV2(); TQCString makeWidthKey( uint width ); TQString artistValue( uint id ); TQString composerValue( uint id ); TQString albumValue( uint id ); TQString genreValue( uint id ); TQString yearValue( uint id ); //These should be avoided as they will be slow and potentially unsafe. //Use the Exact version where possible (faster and safer). //To convert output from Exact version from TQString to uint, use .toUInt() uint IDFromValue( TQString name, TQString value, bool autocreate = true, const bool temporary = false ); TQString IDFromExactValue( TQString table, TQString value, bool autocreate = true, bool temporary = false ); TQString valueFromID( TQString table, uint id ); //member variables TQString m_amazonLicense; bool m_validArtistCache; bool m_validComposerCache; bool m_validAlbumCache; TQString m_cacheArtist[2]; uint m_cacheArtistID[2]; TQString m_cacheComposer[2]; uint m_cacheComposerID[2]; TQString m_cacheAlbum[2]; uint m_cacheAlbumID[2]; bool m_monitor; bool m_autoScoring; static TQMap *itemCoverMap; static TQMutex *itemCoverMapMutex; TQImage m_noCover, m_shadowImage; static TQMap *threadConnections; static TQMutex *connectionMutex; DbConnection::DbConnectionType m_dbConnType; DbConfig *m_dbConfig; //organize files stuff bool m_waitForFileOperation; bool m_fileOperationFailed; bool m_scanInProgress; bool m_rescanRequired; TQStringList m_aftEnabledPersistentTables; // Cancel move/copy job bool m_moveFileJobCancelled; // for handling podcast image url redirects TQMap m_podcastImageJobs; // protect against multiple simultaneous queries/inserts TQMutex m_mutex; }; #ifdef Q_MOC_RUN // MOC_SKIP_BEGIN class INotify : public JobBase // MOC_SKIP_END #else // Q_MOC_RUN class INotify : public ThreadManager::DependentJob #endif // Q_MOC_RUN { Q_OBJECT TQ_OBJECT public: INotify( CollectionDB *parent, int fd ); ~INotify(); static INotify *instance() { return s_instance; } bool watchDir( const TQString directory ); int fd() { return m_fd; } private: virtual bool doJob(); CollectionDB* m_parent; int m_fd; static INotify* s_instance; }; class QueryBuilder { public: //attributes: enum qBuilderTables { tabAlbum = 1, tabArtist = 2, tabComposer = 4, tabGenre = 8, tabYear = 16, tabSong = 64, tabStats = 128, tabLyrics = 256, tabPodcastChannels = 512, tabPodcastEpisodes = 1024, tabPodcastFolders = 2048, tabDevices = 4096, tabLabels = 8192 /* dummy table for filtering */, tabDummy = 0 }; enum qBuilderOptions { optNoCompilations = 1, optOnlyCompilations = 2, optRemoveDuplicates = 4, optRandomize = 8, optShowAll = 16 /* get all songs, not just mounted ones */ }; /* This has been an enum in the past, but 32 bits wasn't enough anymore :-( */ static const TQ_INT64 valDummy = 0; static const TQ_INT64 valID = 1LL << 0; static const TQ_INT64 valName = 1LL << 1; static const TQ_INT64 valURL = 1LL << 2; static const TQ_INT64 valTitle = 1LL << 3; static const TQ_INT64 valTrack = 1LL << 4; static const TQ_INT64 valScore = 1LL << 5; static const TQ_INT64 valComment = 1LL << 6; static const TQ_INT64 valBitrate = 1LL << 7; static const TQ_INT64 valLength = 1LL << 8; static const TQ_INT64 valSamplerate = 1LL << 9; static const TQ_INT64 valPlayCounter = 1LL << 10; static const TQ_INT64 valCreateDate = 1LL << 11; static const TQ_INT64 valAccessDate = 1LL << 12; //static const TQ_INT64 valPercentage = 1LL << 13; // same as valScore static const TQ_INT64 valArtistID = 1LL << 14; static const TQ_INT64 valAlbumID = 1LL << 15; static const TQ_INT64 valYearID = 1LL << 16; static const TQ_INT64 valGenreID = 1LL << 17; static const TQ_INT64 valDirectory = 1LL << 18; static const TQ_INT64 valLyrics = 1LL << 19; static const TQ_INT64 valRating = 1LL << 20; static const TQ_INT64 valComposerID = 1LL << 21; static const TQ_INT64 valDiscNumber = 1LL << 22; static const TQ_INT64 valFilesize = 1LL << 23; static const TQ_INT64 valFileType = 1LL << 24; static const TQ_INT64 valIsCompilation = 1LL << 25; static const TQ_INT64 valBPM = 1LL << 26; // podcast relevant: static const TQ_INT64 valCopyright = 1LL << 27; static const TQ_INT64 valParent = 1LL << 28; static const TQ_INT64 valWeblink = 1LL << 29; static const TQ_INT64 valAutoscan = 1LL << 30; static const TQ_INT64 valFetchtype = 1LL << 31; static const TQ_INT64 valAutotransfer = 1LL << 32; static const TQ_INT64 valPurge = 1LL << 33; static const TQ_INT64 valPurgeCount = 1LL << 34; static const TQ_INT64 valIsNew = 1LL << 35; // dynamic collection relevant: static const TQ_INT64 valDeviceId = 1LL << 36; static const TQ_INT64 valRelativePath = 1LL << 37; static const TQ_INT64 valDeviceLabel = 1LL << 38; static const TQ_INT64 valMountPoint = 1LL << 39; //label relevant static const TQ_INT64 valType = 1LL << 40; static TQ_INT64 valForFavoriteSorting(); void sortByFavorite(); // sortByFavoriteAvg() add the average rating, if enabled, the average score, if enabled, // and the average playcounter as return values! void sortByFavoriteAvg(); enum qBuilderFunctions { funcNone = 0, funcCount = 1, funcMax = 2, funcMin = 4, funcAvg = 8, funcSum = 16 }; // Note: modes beginMatch, endMatch are only supported for string filters // Likewise, modes between and notBetween are only supported for numeric filters enum qBuilderFilter { modeNormal = 0, modeLess = 1, modeGreater = 2, modeEndMatch = 3, modeBeginMatch = 4, modeBetween = 5, modeNotBetween = 6}; QueryBuilder(); void addReturnValue( int table, TQ_INT64 value, bool caseSensitive = false /* unless value refers to a string */ ); void addReturnFunctionValue( int function, int table, TQ_INT64 value); uint countReturnValues(); // Note: the filter chain begins in AND mode void beginOR(); //filters will be ORed instead of ANDed void endOR(); //don't forget to end it! void beginAND(); // These do the opposite; for recursive and/or void endAND(); void setGoogleFilter( int defaultTables, TQString query ); void addURLFilters( const TQStringList& filter ); void addFilter( int tables, const TQString& filter); void addFilter( int tables, TQ_INT64 value, const TQString& filter, int mode = modeNormal, bool exact = false ); void addFilters( int tables, const TQStringList& filter ); void excludeFilter( int tables, const TQString& filter ); void excludeFilter( int tables, TQ_INT64 value, const TQString& filter, int mode = modeNormal, bool exact = false ); void addMatch( int tables, const TQString& match, bool interpretUnknown = true, bool caseSensitive = true ); void addMatch( int tables, TQ_INT64 value, const TQString& match, bool interpretUnknown = true, bool caseSensitive = true ); void addMatches( int tables, const TQStringList& match, bool interpretUnknown = true, bool caseSensitive = true ); void excludeMatch( int tables, const TQString& match ); void having( int table, TQ_INT64 value, int function, int mode, const TQString& match ); void exclusiveFilter( int tableMatching, int tableNotMatching, TQ_INT64 value ); // For numeric filters: // modeNormal means strict equality; modeBeginMatch and modeEndMatch are not // allowed; modeBetween needs a second value endRange void addNumericFilter(int tables, TQ_INT64 value, const TQString &n, int mode = modeNormal, const TQString &endRange = TQString()); void setOptions( int options ); void sortBy( int table, TQ_INT64 value, bool descending = false ); void sortByFunction( int function, int table, TQ_INT64 value, bool descending = false ); void groupBy( int table, TQ_INT64 value ); void setLimit( int startPos, int length ); // Returns the results in random order. // If a \p table and \p value are specified, uses weighted random order on // that field. // The shuffle is cumulative with other sorts, but any sorts after this are // pointless because of the precision of the random function. void shuffle( int table = 0, TQ_INT64 value = 0 ); static const int dragFieldCount; static TQString dragSQLFields(); void initSQLDrag(); void buildQuery( bool withDeviceidPlaceholder = false ); TQString getQuery(); //use withDeviceidPlaceholder = false if the query isn't run immediately (*CurrentTimeT*) //and replace (*MountedDeviceSelection*) with CollectionDB::instance()->deviceIdSelection() TQString query( bool withDeviceidPlaceholder = false ) { buildQuery( withDeviceidPlaceholder ); return m_query; }; void clear(); TQStringList run(); // Transform a string table.value "field" into enum values // @return true if we succeeded bool getField(const TQString &tableValue, int *table, TQ_INT64 *value); private: TQString tableName( int table ); const TQString &valueName( TQ_INT64 value ); TQString functionName( int functions ); bool coalesceField( int table, TQ_INT64 value ); int getTableByName(const TQString &name); TQ_INT64 getValueByName(const TQString &field); TQStringList cleanURL( TQStringList result ); void linkTables( int tables ); TQValueStack m_OR; bool m_showAll; uint m_deviceidPos; TQString ANDslashOR() const; TQString m_query; TQString m_values; TQString m_tables; TQString m_join; TQString m_where; TQString m_sort; TQString m_group; TQString m_limit; TQString m_having; TQString m_url; //url is used as primary key and linkTables needs to do some special stuff with it int m_linkTables; uint m_returnValues; }; inline void QueryBuilder::beginOR() { m_where += ANDslashOR() + " ( " + CollectionDB::instance()->boolF() + ' '; m_OR.push(true); } inline void QueryBuilder::endOR() { m_where += " ) "; m_OR.pop(); } inline void QueryBuilder::beginAND() { m_where += ANDslashOR() + " ( " + CollectionDB::instance()->boolT() + ' '; m_OR.push(false); } inline void QueryBuilder::endAND() { m_where += " ) "; m_OR.pop(); } inline TQString QueryBuilder::ANDslashOR() const { return m_OR.top() ? "OR" : "AND"; } #endif /* AMAROK_COLLECTIONDB_H */