[Amarok] 2a24f8d Fix random crashes with SqlQueryMaker.

Mark Kretschmann kretschmann at kde.org
Tue Dec 29 12:30:03 CET 2009


commit 2a24f8d7d1484741aa0732d3f7e696c1e2e05496
Author: Mark Kretschmann <kretschmann at kde.org>
Date:   Mon Dec 28 08:03:25 2009 +0100

    Fix random crashes with SqlQueryMaker.
    
    Many thanks to Maximilian Kossick for making this patch. @Max: I had to modify
    this slightly to make it compile. Please check for correctness.
    @All: More testing for this is very welcome.
    
    BUG: 215684
    CCMAIL: amarok-devel at kde.org

diff --git a/src/collection/mysqlecollection/CMakeLists.txt b/src/collection/mysqlecollection/CMakeLists.txt
index 0193a5a..3d28353 100755
--- a/src/collection/mysqlecollection/CMakeLists.txt
+++ b/src/collection/mysqlecollection/CMakeLists.txt
@@ -36,6 +36,7 @@ set(amarok_collection-mysqlecollection_PART_SRCS
     ../sqlcollection/SqlCollectionDBusHandler.cpp
     ../sqlcollection/SqlCollectionLocation.cpp
     ../sqlcollection/SqlQueryMaker.cpp
+    ../sqlcollection/SqlQueryMakerInternal.cpp
     ../sqlcollection/SqlReadLabelCapability.cpp
     ../sqlcollection/SqlRegistry.cpp
     ../sqlcollection/SqlMeta.cpp
diff --git a/src/collection/mysqlservercollection/CMakeLists.txt b/src/collection/mysqlservercollection/CMakeLists.txt
index 940bf6d..525204b 100755
--- a/src/collection/mysqlservercollection/CMakeLists.txt
+++ b/src/collection/mysqlservercollection/CMakeLists.txt
@@ -36,6 +36,8 @@ set(amarok_collection-mysqlservercollection_PART_SRCS
     ../sqlcollection/SqlCollectionDBusHandler.cpp
     ../sqlcollection/SqlCollectionLocation.cpp
     ../sqlcollection/SqlQueryMaker.cpp
+    ../sqlcollection/SqlQueryMakerInternal.cpp
+    ../sqlcollection/SqlReadLabelCapability.cpp
     ../sqlcollection/SqlReadLabelCapability.cpp
     ../sqlcollection/SqlRegistry.cpp
     ../sqlcollection/SqlMeta.cpp
diff --git a/src/collection/sqlcollection/SqlQueryMaker.cpp b/src/collection/sqlcollection/SqlQueryMaker.cpp
index c027b3c..ecef045 100644
--- a/src/collection/sqlcollection/SqlQueryMaker.cpp
+++ b/src/collection/sqlcollection/SqlQueryMaker.cpp
@@ -23,7 +23,10 @@
 
 #include "MountPointManager.h"
 #include "SqlCollection.h"
+#include "SqlQueryMakerInternal.h"
+#include "collection/SqlStorage.h"
 
+#include <QPointer>
 #include <QStack>
 
 #include <threadweaver/Job.h>
@@ -34,30 +37,37 @@ using namespace Meta;
 class SqlWorkerThread : public ThreadWeaver::Job
 {
     public:
-        SqlWorkerThread( SqlQueryMaker *queryMaker )
+        SqlWorkerThread( SqlQueryMakerInternal *queryMakerInternal )
             : ThreadWeaver::Job()
-            , m_queryMaker( queryMaker )
+            , m_queryMakerInternal( queryMakerInternal )
             , m_aborted( false )
         {
             //nothing to do
         }
 
+        virtual ~SqlWorkerThread()
+        {
+            delete m_queryMakerInternal;
+        }
+
         virtual void requestAbort()
         {
             m_aborted = true;
         }
 
+        SqlQueryMakerInternal* queryMakerInternal() const
+        {
+            return m_queryMakerInternal;
+        }
+
     protected:
         virtual void run()
         {
-            QString query = m_queryMaker->query();
-            QStringList result = m_queryMaker->runQuery( query );
-            if( !m_aborted )
-                m_queryMaker->handleResult( result );
+            m_queryMakerInternal->run();
             setFinished( !m_aborted );
         }
     private:
-        SqlQueryMaker *m_queryMaker;
+        SqlQueryMakerInternal *m_queryMakerInternal;
 
         bool m_aborted;
 };
@@ -83,7 +93,14 @@ struct SqlQueryMaker::Private
 
     QStack<bool> andStack;
 
-    Meta::DataList data;
+    QStringList blockingCustomData;
+    Meta::DataList blockingData;
+    Meta::TrackList blockingTracks;
+    Meta::AlbumList blockingAlbums;
+    Meta::ArtistList blockingArtists;
+    Meta::GenreList blockingGenres;
+    Meta::ComposerList blockingComposers;
+    Meta::YearList blockingYears;
     bool blocking;
     bool used;
 };
@@ -101,6 +118,8 @@ SqlQueryMaker::SqlQueryMaker( SqlCollection* collection )
 
 SqlQueryMaker::~SqlQueryMaker()
 {
+    disconnect();
+    abortQuery();
     delete d;
 }
 
@@ -125,7 +144,15 @@ SqlQueryMaker::reset()
     d->andStack.push( true );   //and is default
     d->blocking = false;
     d->used = false;
-    d->data.clear();
+    d->blockingCustomData.clear();
+    d->blockingData.clear();
+    d->blockingAlbums.clear();
+    d->blockingArtists.clear();
+    d->blockingComposers.clear();
+    d->blockingGenres.clear();
+    d->blockingTracks.clear();
+    d->blockingYears.clear();
+
     return this;
 }
 
@@ -133,7 +160,12 @@ void
 SqlQueryMaker::abortQuery()
 {
     if( d->worker )
+    {
         d->worker->requestAbort();
+        d->worker->disconnect( this );
+        if( d->worker->queryMakerInternal() )
+            d->worker->queryMakerInternal()->disconnect( this );
+    }
 }
 
 QueryMaker*
@@ -162,18 +194,41 @@ SqlQueryMaker::run()
         //TODO: wait or job to complete
 
     }
-    else if ( ! d->blocking )
-    {
-        //debug() << "Query is " << query();
-        d->worker = new SqlWorkerThread( this );
-        connect( d->worker, SIGNAL( done( ThreadWeaver::Job* ) ), SLOT( done( ThreadWeaver::Job* ) ) );
-        ThreadWeaver::Weaver::instance()->enqueue( d->worker );
-    }
-    else //use it blocking
+    else
     {
-        QString query = this->query();
-        QStringList result = runQuery( query );
-        handleResult( result );
+        SqlQueryMakerInternal *qmi = new SqlQueryMakerInternal( m_collection );
+        qmi->setQuery( query() );
+        qmi->setQueryType( d->queryType );
+        qmi->setResultAsDataPtrs( d->resultAsDataPtrs );
+
+
+        if ( !d->blocking )
+        {
+            connect( qmi, SIGNAL(newResultReady(QString,Meta::AlbumList)), SIGNAL(newResultReady(QString,Meta::AlbumList)), Qt::DirectConnection );
+            connect( qmi, SIGNAL(newResultReady(QString,Meta::ArtistList)), SIGNAL(newResultReady(QString,Meta::ArtistList)), Qt::DirectConnection );
+            connect( qmi, SIGNAL(newResultReady(QString,Meta::GenreList)), SIGNAL(newResultReady(QString,Meta::GenreList)), Qt::DirectConnection );
+            connect( qmi, SIGNAL(newResultReady(QString,Meta::ComposerList)), SIGNAL(newResultReady(QString,Meta::ComposerList)), Qt::DirectConnection );
+            connect( qmi, SIGNAL(newResultReady(QString,Meta::YearList)), SIGNAL(newResultReady(QString,Meta::YearList)), Qt::DirectConnection );
+            connect( qmi, SIGNAL(newResultReady(QString,Meta::TrackList)), SIGNAL(newResultReady(QString,Meta::TrackList)), Qt::DirectConnection );
+            connect( qmi, SIGNAL(newResultReady(QString,Meta::DataList)), SIGNAL(newResultReady(QString,Meta::DataList)), Qt::DirectConnection );
+            connect( qmi, SIGNAL(newResultReady(QString,QStringList)), SIGNAL(newResultReady(QString,QStringList)), Qt::DirectConnection );
+            d->worker = new SqlWorkerThread( qmi );
+            connect( d->worker, SIGNAL( done( ThreadWeaver::Job* ) ), SLOT( done( ThreadWeaver::Job* ) ) );
+            ThreadWeaver::Weaver::instance()->enqueue( d->worker );
+        }
+        else //use it blocking
+        {
+            connect( qmi, SIGNAL(newResultReady(QString,Meta::AlbumList)), SLOT(blockingNewResultReady(QString,Meta::AlbumList)), Qt::DirectConnection );
+            connect( qmi, SIGNAL(newResultReady(QString,Meta::ArtistList)), SLOT(blockingNewResultReady(QString,Meta::ArtistList)), Qt::DirectConnection );
+            connect( qmi, SIGNAL(newResultReady(QString,Meta::GenreList)), SLOT(blockingNewResultReady(QString,Meta::GenreList)), Qt::DirectConnection );
+            connect( qmi, SIGNAL(newResultReady(QString,Meta::ComposerList)), SLOT(blockingNewResultReady(QString,Meta::ComposerList)), Qt::DirectConnection );
+            connect( qmi, SIGNAL(newResultReady(QString,Meta::YearList)), SLOT(blockingNewResultReady(QString,Meta::YearList)), Qt::DirectConnection );
+            connect( qmi, SIGNAL(newResultReady(QString,Meta::TrackList)), SLOT(blockingNewResultReady(QString,Meta::TrackList)), Qt::DirectConnection );
+            connect( qmi, SIGNAL(newResultReady(QString,Meta::DataList)), SLOT(blockingNewResultReady(QString,Meta::DataList)), Qt::DirectConnection );
+            connect( qmi, SIGNAL(newResultReady(QString,QStringList)), SLOT(blockingNewResultReady(QString,QStringList)), Qt::DirectConnection );
+            qmi->run();
+            delete qmi;
+        }
     }
     d->used = true;
 }
@@ -535,7 +590,7 @@ SqlQueryMaker::orderBy( qint64 value, bool descending )
 QueryMaker*
 SqlQueryMaker::orderByRandom()
 {
-    d->queryOrderBy = " ORDER BY " + m_collection->randomFunc();
+    d->queryOrderBy = " ORDER BY " + CollectionManager::instance()->sqlStorage()->randomFunc();
     return this;
 }
 
@@ -725,80 +780,10 @@ SqlQueryMaker::query()
 QStringList
 SqlQueryMaker::runQuery( const QString &query )
 {
-    return m_collection->query( query );
+    return CollectionManager::instance()->sqlStorage()->query( query );
 }
 
-void
-SqlQueryMaker::handleResult( const QStringList &result )
-{
-    if( !result.isEmpty() )
-    {
-        switch( d->queryType ) {
-        case QueryMaker::Custom:
-            emit newResultReady( m_collection->collectionId(), result );
-            break;
-        case QueryMaker::Track:
-            handleTracks( result );
-            break;
-        case QueryMaker::Artist:
-            handleArtists( result );
-            break;
-        case QueryMaker::Album:
-            handleAlbums( result );
-            break;
-        case QueryMaker::Genre:
-            handleGenres( result );
-            break;
-        case QueryMaker::Composer:
-            handleComposers( result );
-            break;
-        case QueryMaker::Year:
-            handleYears( result );
-            break;
-
-        case QueryMaker::None:
-            debug() << "Warning: queryResult with queryType == NONE";
-        }
-    }
-    else
-    {
-        if( d->resultAsDataPtrs )
-        {
-            emit newResultReady( m_collection->collectionId(), Meta::DataList() );
-        }
-        else
-        {
-            switch( d->queryType ) {
-                case QueryMaker::Custom:
-                    emit newResultReady( m_collection->collectionId(), QStringList() );
-                    break;
-                case QueryMaker::Track:
-                    emit newResultReady( m_collection->collectionId(), Meta::TrackList() );
-                    break;
-                case QueryMaker::Artist:
-                    emit newResultReady( m_collection->collectionId(), Meta::ArtistList() );
-                    break;
-                case QueryMaker::Album:
-                    emit newResultReady( m_collection->collectionId(), Meta::AlbumList() );
-                    break;
-                case QueryMaker::Genre:
-                    emit newResultReady( m_collection->collectionId(), Meta::GenreList() );
-                    break;
-                case QueryMaker::Composer:
-                    emit newResultReady( m_collection->collectionId(), Meta::ComposerList() );
-                    break;
-                case QueryMaker::Year:
-                    emit newResultReady( m_collection->collectionId(), Meta::YearList() );
-                    break;
-
-            case QueryMaker::None:
-                debug() << "Warning: queryResult with queryType == NONE";
-            }
-        }
-    }
 
-    //queryDone will be emitted in done(Job*)
-}
 
 void
 SqlQueryMaker::setBlocking( bool enabled )
@@ -817,115 +802,57 @@ SqlQueryMaker::collectionIds() const
 Meta::DataList
 SqlQueryMaker::data( const QString &id ) const
 {
-    if ( d->blocking && d->used && d->resultAsDataPtrs && m_collection->collectionId() == id )
-        return d->data;
-    else
-        return Meta::DataList();
+    Q_UNUSED( id );
+    return d->blockingData;
 }
 
 Meta::TrackList
 SqlQueryMaker::tracks( const QString &id ) const
 {
-    if ( d->blocking && d->used && d->queryType == QueryMaker::Track && m_collection->collectionId() == id  )
-    {
-        Meta::TrackList list;
-        foreach( DataPtr p, d->data )
-        {
-            list << Meta::TrackPtr::staticCast( p );
-        }
-        return list;
-    }
-    else
-        return Meta::TrackList();
+    Q_UNUSED( id );
+    return d->blockingTracks;
 }
 
 Meta::AlbumList
 SqlQueryMaker::albums( const QString &id ) const
 {
-    if ( d->blocking && d->used && d->queryType == QueryMaker::Album && m_collection->collectionId() == id  )
-    {
-        Meta::AlbumList list;
-        foreach( DataPtr p, d->data )
-        {
-            list << Meta::AlbumPtr::staticCast( p );
-        }
-        return list;
-    }
-    else
-        return Meta::AlbumList();
+    Q_UNUSED( id );
+    return d->blockingAlbums;
 }
 
 Meta::ArtistList
 SqlQueryMaker::artists( const QString &id ) const
 {
-    if ( d->blocking && d->used && d->queryType == QueryMaker::Artist && m_collection->collectionId() == id  )
-    {
-        Meta::ArtistList list;
-        foreach( DataPtr p, d->data )
-        {
-            list << Meta::ArtistPtr::staticCast( p );
-        }
-        return list;
-    }
-    else
-        return Meta::ArtistList();
+    Q_UNUSED( id );
+    return d->blockingArtists;
 }
 
 Meta::GenreList
 SqlQueryMaker::genres( const QString &id ) const
 {
-    if ( d->blocking && d->used && d->queryType == QueryMaker::Genre && m_collection->collectionId() == id  )
-    {
-        Meta::GenreList list;
-        foreach( DataPtr p, d->data )
-        {
-            list << Meta::GenrePtr::staticCast( p );
-        }
-        return list;
-    }
-    else
-        return Meta::GenreList();
+    Q_UNUSED( id );
+    return d->blockingGenres;
 }
 
 Meta::ComposerList
 SqlQueryMaker::composers( const QString &id ) const
 {
-    if ( d->blocking && d->used && d->queryType == QueryMaker::Composer && m_collection->collectionId() == id  )
-    {
-        Meta::ComposerList list;
-        foreach( DataPtr p, d->data )
-        {
-            list << Meta::ComposerPtr::staticCast( p );
-        }
-        return list;
-    }
-    else
-        return Meta::ComposerList();
+    Q_UNUSED( id );
+    return d->blockingComposers;
 }
 
 Meta::YearList
 SqlQueryMaker::years( const QString &id ) const
 {
-    if ( d->blocking && d->used && d->queryType == QueryMaker::Year && m_collection->collectionId() == id  )
-    {
-        Meta::YearList list;
-        foreach( DataPtr p, d->data )
-        {
-            list << Meta::YearPtr::staticCast( p );
-        }
-        return list;
-    }
-    else
-        return Meta::YearList();
+    Q_UNUSED( id );
+    return d->blockingYears;
 }
 
 QStringList
 SqlQueryMaker::customData( const QString &id ) const
 {
-    AMAROK_NOTIMPLEMENTED
-    Q_UNUSED( id )
-    // not implemented yet
-    return QStringList();
+    Q_UNUSED( id );
+    return d->blockingCustomData;
 }
 
 QString
@@ -1016,116 +943,10 @@ SqlQueryMaker::andOr() const
     return d->andStack.top() ? " AND " : " OR ";
 }
 
-// What's worse, a bunch of almost identical repeated code, or a not so obvious macro? :-)
-// The macro below will emit the proper result signal. If m_resultAsDataPtrs is true,
-// it'll emit the signal that takes a list of DataPtrs. Otherwise, it'll call the
-// signal that takes the list of the specific class.
-// If qm is used blocking it only stores the result ptrs into data as DataPtrs
-
-#define emitOrStoreProperResult( PointerType, list ) { \
-            if ( d->resultAsDataPtrs || d->blocking ) { \
-                foreach( PointerType p, list ) { \
-                    d->data << DataPtr::staticCast( p ); \
-                } \
-                if ( !d->blocking ) \
-                emit newResultReady( m_collection->collectionId(), d->data ); \
-            } \
-            else { \
-                emit newResultReady( m_collection->collectionId(), list ); \
-            } \
-       }
-
-void
-SqlQueryMaker::handleTracks( const QStringList &result )
-{
-    TrackList tracks;
-    SqlRegistry* reg = m_collection->registry();
-    //there are 29 columns in the result set as generated by startTrackQuery()
-    int returnCount = SqlTrack::getTrackReturnValueCount();
-    int resultRows = result.size() / returnCount;
-    for( int i = 0; i < resultRows; i++ )
-    {
-        QStringList row = result.mid( i*returnCount, returnCount );
-        tracks.append( reg->getTrack( row ) );
-    }
-    emitOrStoreProperResult( TrackPtr, tracks );
-}
-
-void
-SqlQueryMaker::handleArtists( const QStringList &result )
-{
-    ArtistList artists;
-    SqlRegistry* reg = m_collection->registry();
-    for( QStringListIterator iter( result ); iter.hasNext(); )
-    {
-        QString name = iter.next();
-        QString id = iter.next();
-        artists.append( reg->getArtist( name, id.toInt() ) );
-    }
-    emitOrStoreProperResult( ArtistPtr, artists );
-}
-
-void
-SqlQueryMaker::handleAlbums( const QStringList &result )
-{
-    AlbumList albums;
-    SqlRegistry* reg = m_collection->registry();
-    for( QStringListIterator iter( result ); iter.hasNext(); )
-    {
-        QString name = iter.next();
-        QString id = iter.next();
-        QString artist = iter.next();
-        albums.append( reg->getAlbum( name, id.toInt(), artist.toInt() ) );
-    }
-    emitOrStoreProperResult( AlbumPtr, albums );
-}
-
-void
-SqlQueryMaker::handleGenres( const QStringList &result )
-{
-    GenreList genres;
-    SqlRegistry* reg = m_collection->registry();
-    for( QStringListIterator iter( result ); iter.hasNext(); )
-    {
-        QString name = iter.next();
-        QString id = iter.next();
-        genres.append( reg->getGenre( name, id.toInt() ) );
-    }
-    emitOrStoreProperResult( GenrePtr, genres );
-}
-
-void
-SqlQueryMaker::handleComposers( const QStringList &result )
-{
-    ComposerList composers;
-    SqlRegistry* reg = m_collection->registry();
-    for( QStringListIterator iter( result ); iter.hasNext(); )
-    {
-        QString name = iter.next();
-        QString id = iter.next();
-        composers.append( reg->getComposer( name, id.toInt() ) );
-    }
-    emitOrStoreProperResult( ComposerPtr, composers );
-}
-
-void
-SqlQueryMaker::handleYears( const QStringList &result )
-{
-    YearList years;
-    SqlRegistry* reg = m_collection->registry();
-    for( QStringListIterator iter( result ); iter.hasNext(); )
-    {
-        QString name = iter.next();
-        QString id = iter.next();
-        years.append( reg->getYear( name, id.toInt() ) );
-    }
-    emitOrStoreProperResult( YearPtr, years );
-}
-
 QString
 SqlQueryMaker::escape( QString text ) const           //krazy:exclude=constref
 {
-    return m_collection->escape( text );
+    return CollectionManager::instance()->sqlStorage()->escape( text );
 }
 
 QString
@@ -1167,5 +988,61 @@ SqlQueryMaker::likeCondition( const QString &text, bool anyBegin, bool anyEnd )
     }
 }
 
+void
+SqlQueryMaker::blockingNewResultReady(const QString &id, const Meta::AlbumList &albums)
+{
+    Q_UNUSED( id );
+    d->blockingAlbums = albums;
+}
+
+void
+SqlQueryMaker::blockingNewResultReady(const QString &id, const Meta::ArtistList &artists)
+{
+    Q_UNUSED( id );
+    d->blockingArtists = artists;
+}
+
+void
+SqlQueryMaker::blockingNewResultReady(const QString &id, const Meta::GenreList &genres)
+{
+    Q_UNUSED( id );
+    d->blockingGenres = genres;
+}
+
+void
+SqlQueryMaker::blockingNewResultReady(const QString &id, const Meta::ComposerList &composers)
+{
+    Q_UNUSED( id );
+    d->blockingComposers = composers;
+}
+
+void
+SqlQueryMaker::blockingNewResultReady(const QString &id, const Meta::YearList &years)
+{
+    Q_UNUSED( id );
+    d->blockingYears = years;
+}
+
+void
+SqlQueryMaker::blockingNewResultReady(const QString &id, const Meta::TrackList &tracks)
+{
+    Q_UNUSED( id );
+    d->blockingTracks = tracks;
+}
+
+void
+SqlQueryMaker::blockingNewResultReady(const QString &id, const Meta::DataList &data)
+{
+    Q_UNUSED( id );
+    d->blockingData = data;
+}
+
+void
+SqlQueryMaker::blockingNewResultReady(const QString &id, const QStringList &customData)
+{
+    Q_UNUSED( id );
+    d->blockingCustomData = customData;
+}
+
 #include "SqlQueryMaker.moc"
 
diff --git a/src/collection/sqlcollection/SqlQueryMaker.h b/src/collection/sqlcollection/SqlQueryMaker.h
index c20766e..b58b86c 100644
--- a/src/collection/sqlcollection/SqlQueryMaker.h
+++ b/src/collection/sqlcollection/SqlQueryMaker.h
@@ -17,7 +17,7 @@
 #ifndef AMAROK_COLLECTION_SQLQUERYMAKER_H
 #define AMAROK_COLLECTION_SQLQUERYMAKER_H
 
-#include "QueryMaker.h"
+#include "collection/QueryMaker.h"
 
 #include "amarok_export.h"
 
@@ -103,6 +103,14 @@ class /*AMAROK_EXPORT*/ SqlQueryMaker : public QueryMaker
 
     public slots:
         void done( ThreadWeaver::Job * job );
+        void blockingNewResultReady( const QString &id, const QStringList &customData );
+        void blockingNewResultReady( const QString &id, const Meta::AlbumList &albums );
+        void blockingNewResultReady( const QString &id, const Meta::ArtistList &artists );
+        void blockingNewResultReady( const QString &id, const Meta::GenreList &genres );
+        void blockingNewResultReady( const QString &id, const Meta::ComposerList &composers );
+        void blockingNewResultReady( const QString &id, const Meta::YearList &years );
+        void blockingNewResultReady( const QString &id, const Meta::TrackList &tracks );
+        void blockingNewResultReady( const QString &id, const Meta::DataList &data );
 
     private:
 
@@ -112,13 +120,6 @@ class /*AMAROK_EXPORT*/ SqlQueryMaker : public QueryMaker
         QString nameForValue( qint64 value );
         QString andOr() const;
 
-        void handleTracks( const QStringList &result );
-        void handleArtists( const QStringList &result );
-        void handleAlbums( const QStringList &result );
-        void handleGenres( const QStringList &result );
-        void handleComposers( const QStringList &result );
-        void handleYears( const QStringList &result );
-
         SqlCollection *m_collection;
 
         struct Private;
@@ -126,4 +127,10 @@ class /*AMAROK_EXPORT*/ SqlQueryMaker : public QueryMaker
 
 };
 
+class SqlQueryMakerFactory
+{
+public:
+    virtual SqlQueryMaker* createQueryMaker() const = 0;
+};
+
 #endif /* AMAROK_COLLECTION_SQLQUERYMAKER_H */
diff --git a/src/collection/sqlcollection/SqlQueryMakerInternal.cpp b/src/collection/sqlcollection/SqlQueryMakerInternal.cpp
new file mode 100644
index 0000000..db999d1
--- /dev/null
+++ b/src/collection/sqlcollection/SqlQueryMakerInternal.cpp
@@ -0,0 +1,250 @@
+/****************************************************************************************
+ * Copyright (c) 2009 Maximilian Kossick <maximilian.kossick at googlemail.com>            *
+ *                                                                                      *
+ * This program is free software; you can redistribute it and/or modify it under        *
+ * the terms of the GNU General Public License as published by the Free Software        *
+ * Foundation; either version 2 of the License, or (at your option) any later           *
+ * version.                                                                             *
+ *                                                                                      *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
+ *                                                                                      *
+ * You should have received a copy of the GNU General Public License along with         *
+ * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
+ ****************************************************************************************/
+
+#include "SqlQueryMakerInternal.h"
+
+#include "collection/SqlStorage.h"
+#include "Debug.h"
+#include "SqlCollection.h"
+#include "SqlMeta.h"
+#include "SqlRegistry.h"
+
+#include <QStringList>
+
+SqlQueryMakerInternal::SqlQueryMakerInternal( SqlCollection *collection )
+    : QObject()
+    , m_collection( collection )
+    , m_resultAsDataPtrs( false )
+    , m_queryType( QueryMaker::None )
+{
+}
+
+SqlQueryMakerInternal::~ SqlQueryMakerInternal()
+{
+    disconnect();
+}
+
+void
+SqlQueryMakerInternal::run()
+{
+    Q_ASSERT( !m_query.isEmpty() );
+    if( m_collection )
+    {
+        QStringList result = m_collection->query( m_query );
+        handleResult( result );
+    }
+    else
+    {
+        deleteLater();
+    }
+
+}
+
+void
+SqlQueryMakerInternal::setQuery( const QString &query )
+{
+    m_query = query;
+}
+
+void
+SqlQueryMakerInternal::setResultAsDataPtrs( bool value )
+{
+    m_resultAsDataPtrs = value;
+}
+
+void
+SqlQueryMakerInternal::setQueryType( QueryMaker::QueryType type )
+{
+    m_queryType = type;
+}
+
+void
+SqlQueryMakerInternal::handleResult( const QStringList &result )
+{
+    if( !result.isEmpty() )
+    {
+        switch( m_queryType ) {
+        case QueryMaker::Custom:
+            emit newResultReady( m_collection->collectionId(), result );
+            break;
+        case QueryMaker::Track:
+            handleTracks( result );
+            break;
+        case QueryMaker::Artist:
+            handleArtists( result );
+            break;
+        case QueryMaker::Album:
+            handleAlbums( result );
+            break;
+        case QueryMaker::Genre:
+            handleGenres( result );
+            break;
+        case QueryMaker::Composer:
+            handleComposers( result );
+            break;
+        case QueryMaker::Year:
+            handleYears( result );
+            break;
+
+        case QueryMaker::None:
+            debug() << "Warning: queryResult with queryType == NONE";
+        }
+    }
+    else
+    {
+        if( m_resultAsDataPtrs )
+        {
+            emit newResultReady( m_collection->collectionId(), Meta::DataList() );
+        }
+        else
+        {
+            switch( m_queryType ) {
+                case QueryMaker::Custom:
+                    emit newResultReady( m_collection->collectionId(), QStringList() );
+                    break;
+                case QueryMaker::Track:
+                    emit newResultReady( m_collection->collectionId(), Meta::TrackList() );
+                    break;
+                case QueryMaker::Artist:
+                    emit newResultReady( m_collection->collectionId(), Meta::ArtistList() );
+                    break;
+                case QueryMaker::Album:
+                    emit newResultReady( m_collection->collectionId(), Meta::AlbumList() );
+                    break;
+                case QueryMaker::Genre:
+                    emit newResultReady( m_collection->collectionId(), Meta::GenreList() );
+                    break;
+                case QueryMaker::Composer:
+                    emit newResultReady( m_collection->collectionId(), Meta::ComposerList() );
+                    break;
+                case QueryMaker::Year:
+                    emit newResultReady( m_collection->collectionId(), Meta::YearList() );
+                    break;
+
+            case QueryMaker::None:
+                debug() << "Warning: queryResult with queryType == NONE";
+            }
+        }
+    }
+
+    //queryDone will be emitted in done(Job*)
+}
+
+// What's worse, a bunch of almost identical repeated code, or a not so obvious macro? :-)
+// The macro below will emit the proper result signal. If m_resultAsDataPtrs is true,
+// it'll emit the signal that takes a list of DataPtrs. Otherwise, it'll call the
+// signal that takes the list of the specific class.
+// If qm is used blocking it only stores the result ptrs into data as DataPtrs
+
+#define emitOrStoreProperResult( PointerType, list ) { \
+            if ( m_resultAsDataPtrs ) { \
+                Meta::DataList data; \
+                foreach( PointerType p, list ) { \
+                    data << Meta::DataPtr::staticCast( p ); \
+                } \
+                emit newResultReady( m_collection->collectionId(), data ); \
+            } \
+            else { \
+                emit newResultReady( m_collection->collectionId(), list ); \
+            } \
+       }
+
+void
+SqlQueryMakerInternal::handleTracks( const QStringList &result )
+{
+    Meta::TrackList tracks;
+    SqlRegistry* reg = m_collection->registry();
+    //there are 29 columns in the result set as generated by startTrackQuery()
+    int returnCount = Meta::SqlTrack::getTrackReturnValueCount();
+    int resultRows = result.size() / returnCount;
+    for( int i = 0; i < resultRows; i++ )
+    {
+        QStringList row = result.mid( i*returnCount, returnCount );
+        tracks.append( reg->getTrack( row ) );
+    }
+    emitOrStoreProperResult( Meta::TrackPtr, tracks );
+}
+
+void
+SqlQueryMakerInternal::handleArtists( const QStringList &result )
+{
+    Meta::ArtistList artists;
+    SqlRegistry* reg = m_collection->registry();
+    for( QStringListIterator iter( result ); iter.hasNext(); )
+    {
+        QString name = iter.next();
+        QString id = iter.next();
+        artists.append( reg->getArtist( name, id.toInt() ) );
+    }
+    emitOrStoreProperResult( Meta::ArtistPtr, artists );
+}
+
+void
+SqlQueryMakerInternal::handleAlbums( const QStringList &result )
+{
+    Meta::AlbumList albums;
+    SqlRegistry* reg = m_collection->registry();
+    for( QStringListIterator iter( result ); iter.hasNext(); )
+    {
+        QString name = iter.next();
+        QString id = iter.next();
+        QString artist = iter.next();
+        albums.append( reg->getAlbum( name, id.toInt(), artist.toInt() ) );
+    }
+    emitOrStoreProperResult( Meta::AlbumPtr, albums );
+}
+
+void
+SqlQueryMakerInternal::handleGenres( const QStringList &result )
+{
+    Meta::GenreList genres;
+    SqlRegistry* reg = m_collection->registry();
+    for( QStringListIterator iter( result ); iter.hasNext(); )
+    {
+        QString name = iter.next();
+        QString id = iter.next();
+        genres.append( reg->getGenre( name, id.toInt() ) );
+    }
+    emitOrStoreProperResult( Meta::GenrePtr, genres );
+}
+
+void
+SqlQueryMakerInternal::handleComposers( const QStringList &result )
+{
+    Meta::ComposerList composers;
+    SqlRegistry* reg = m_collection->registry();
+    for( QStringListIterator iter( result ); iter.hasNext(); )
+    {
+        QString name = iter.next();
+        QString id = iter.next();
+        composers.append( reg->getComposer( name, id.toInt() ) );
+    }
+    emitOrStoreProperResult( Meta::ComposerPtr, composers );
+}
+
+void
+SqlQueryMakerInternal::handleYears( const QStringList &result )
+{
+    Meta::YearList years;
+    SqlRegistry* reg = m_collection->registry();
+    for( QStringListIterator iter( result ); iter.hasNext(); )
+    {
+        QString name = iter.next();
+        QString id = iter.next();
+        years.append( reg->getYear( name, id.toInt() ) );
+    }
+    emitOrStoreProperResult( Meta::YearPtr, years );
+}
diff --git a/src/collection/sqlcollection/SqlQueryMakerInternal.h b/src/collection/sqlcollection/SqlQueryMakerInternal.h
new file mode 100644
index 0000000..b8ecda4
--- /dev/null
+++ b/src/collection/sqlcollection/SqlQueryMakerInternal.h
@@ -0,0 +1,68 @@
+/****************************************************************************************
+ * Copyright (c) 2009 Maximilian Kossick <maximilian.kossick at googlemail.com>            *
+ *                                                                                      *
+ * This program is free software; you can redistribute it and/or modify it under        *
+ * the terms of the GNU General Public License as published by the Free Software        *
+ * Foundation; either version 2 of the License, or (at your option) any later           *
+ * version.                                                                             *
+ *                                                                                      *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
+ *                                                                                      *
+ * You should have received a copy of the GNU General Public License along with         *
+ * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
+ ****************************************************************************************/
+
+#ifndef SQLQUERYMAKERINTERNAL_H
+#define SQLQUERYMAKERINTERNAL_H
+
+#include "collection/QueryMaker.h"
+#include "meta/Meta.h"
+
+#include <QObject>
+#include <QPointer>
+#include <QString>
+
+class SqlCollection;
+
+class SqlQueryMakerInternal : public QObject
+{
+Q_OBJECT
+public:
+    explicit SqlQueryMakerInternal( SqlCollection *collection );
+    virtual ~ SqlQueryMakerInternal();
+
+    void run();
+    void setQuery( const QString &query );
+    void setQueryType( QueryMaker::QueryType type );
+    void setResultAsDataPtrs( bool value );
+
+signals:
+    void newResultReady( QString collectionId, Meta::TrackList );
+    void newResultReady( QString collectionId, Meta::ArtistList );
+    void newResultReady( QString collectionId, Meta::AlbumList );
+    void newResultReady( QString collectionId, Meta::GenreList );
+    void newResultReady( QString collectionId, Meta::ComposerList );
+    void newResultReady( QString collectionId, Meta::YearList );
+    void newResultReady( QString collectionId, Meta::DataList );
+    void newResultReady( QString collectionId, QStringList );
+
+private:
+    void handleResult( const QStringList &result );
+    void handleTracks( const QStringList &result );
+    void handleArtists( const QStringList &result );
+    void handleAlbums( const QStringList &result );
+    void handleGenres( const QStringList &result );
+    void handleComposers( const QStringList &result );
+    void handleYears( const QStringList &result );
+
+private:
+    QPointer<SqlCollection> m_collection;
+    bool m_resultAsDataPtrs;
+    QueryMaker::QueryType m_queryType;
+    QString m_query;
+
+};
+
+#endif // SQLQUERYMAKERINTERNAL_H


More information about the Amarok-devel mailing list