File indexing completed on 2024-05-05 04:49:17

0001 /****************************************************************************************
0002  * Copyright (c) 2007 Maximilian Kossick <maximilian.kossick@googlemail.com>            *
0003  * Copyright (c) 2007 Nikolaj Hald Nielsen <nhn@kde.org>                                *
0004  *                                                                                      *
0005  * This program is free software; you can redistribute it and/or modify it under        *
0006  * the terms of the GNU General Public License as published by the Free Software        *
0007  * Foundation; either version 2 of the License, or (at your option) any later           *
0008  * version.                                                                             *
0009  *                                                                                      *
0010  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0011  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0012  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0013  *                                                                                      *
0014  * You should have received a copy of the GNU General Public License along with         *
0015  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0016  ****************************************************************************************/
0017 
0018 #define DEBUG_PREFIX "ServiceSqlQueryMaker"
0019 
0020 #include "ServiceSqlQueryMaker.h"
0021 
0022 #include <core/storage/SqlStorage.h>
0023 #include "core/meta/support/MetaConstants.h"
0024 #include "core/support/Debug.h"
0025 #include "core-impl/storage/StorageManager.h"
0026 #include "ServiceSqlCollection.h"
0027 
0028 #include <QStack>
0029 #include <QSharedPointer>
0030 
0031 using namespace Collections;
0032 
0033 class ServiceSqlWorkerThread : public QObject, public ThreadWeaver::Job
0034 {
0035     Q_OBJECT
0036     public:
0037         ServiceSqlWorkerThread( ServiceSqlQueryMaker *queryMaker )
0038             : QObject()
0039             , ThreadWeaver::Job()
0040             , m_queryMaker( queryMaker )
0041             , m_aborted( false )
0042         {
0043             //nothing to do
0044         }
0045 
0046         void requestAbort() override
0047         {
0048             m_aborted = true;
0049         }
0050 
0051     Q_SIGNALS:
0052         /** This signal is emitted when this job is being processed by a thread. */
0053         void started(ThreadWeaver::JobPointer);
0054         /** This signal is emitted when the job has been finished (no matter if it succeeded or not). */
0055         void done(ThreadWeaver::JobPointer);
0056         /** This job has failed.
0057          * This signal is emitted when success() returns false after the job is executed. */
0058         void failed(ThreadWeaver::JobPointer);
0059 
0060     protected:
0061 
0062         void run(ThreadWeaver::JobPointer self = QSharedPointer<ThreadWeaver::Job>(), ThreadWeaver::Thread *thread = nullptr) override
0063         {
0064             Q_UNUSED(self);
0065             Q_UNUSED(thread);
0066             QString query = m_queryMaker->query();
0067             QStringList result = m_queryMaker->runQuery( query );
0068             if( !m_aborted )
0069                 m_queryMaker->handleResult( result );
0070 
0071             if( m_aborted )
0072                 setStatus(Status_Aborted);
0073             else
0074                 setStatus(Status_Running);
0075         }
0076 
0077         void defaultBegin(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread) override
0078         {
0079             Q_EMIT started(self);
0080             ThreadWeaver::Job::defaultBegin(self, thread);
0081         }
0082 
0083         void defaultEnd(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread) override
0084         {
0085             ThreadWeaver::Job::defaultEnd(self, thread);
0086             if (!self->success()) {
0087                 Q_EMIT failed(self);
0088             }
0089             Q_EMIT done(self);
0090         }
0091 
0092     private:
0093         ServiceSqlQueryMaker *m_queryMaker;
0094         bool m_aborted;
0095 };
0096 
0097 struct ServiceSqlQueryMaker::Private
0098 {
0099     enum { TRACKS_TABLE = 1, ALBUMS_TABLE = 2, ARTISTS_TABLE = 4, GENRE_TABLE = 8, ALBUMARTISTS_TABLE = 16 };
0100     int linkedTables;
0101     QueryMaker::QueryType queryType;
0102     QString query;
0103     QString queryReturnValues;
0104     QString queryFrom;
0105     QString queryMatch;
0106     QString queryFilter;
0107     QString queryOrderBy;
0108     //bool includedBuilder;
0109     //bool collectionRestriction;
0110     AlbumQueryMode albumMode;
0111     bool withoutDuplicates;
0112     int maxResultSize;
0113     QSharedPointer<ServiceSqlWorkerThread> worker;
0114     QStack<bool> andStack;
0115 };
0116 
0117 ServiceSqlQueryMaker::ServiceSqlQueryMaker( ServiceSqlCollection* collection, ServiceMetaFactory * metaFactory, ServiceSqlRegistry * registry )
0118     : QueryMaker()
0119     , m_collection( collection )
0120     , m_registry( registry )
0121     , m_metaFactory( metaFactory )
0122     , d( new Private )
0123 {
0124     //d->includedBuilder = true;
0125     //d->collectionRestriction = false;
0126     d->albumMode = AllAlbums;
0127 
0128     d->queryType = QueryMaker::None;
0129     d->linkedTables = 0;
0130     d->withoutDuplicates = false;
0131     d->maxResultSize = -1;
0132     d->andStack.push( true );
0133 }
0134 
0135 ServiceSqlQueryMaker::~ServiceSqlQueryMaker()
0136 {}
0137 
0138 void
0139 ServiceSqlQueryMaker::abortQuery()
0140 {
0141     if( d->worker )
0142         d->worker->requestAbort();
0143 }
0144 
0145 void
0146 ServiceSqlQueryMaker::run()
0147 {
0148     if( d->queryType == QueryMaker::None )
0149         return; //better error handling?
0150     if( d->worker && !d->worker->isFinished() )
0151     {
0152         //the worker thread seems to be running
0153         //TODO: wait for job to complete
0154 
0155     }
0156     else
0157     {
0158         d->worker.reset( new ServiceSqlWorkerThread( this ) );
0159         connect( d->worker.data(), &ServiceSqlWorkerThread::done, this, &ServiceSqlQueryMaker::done );
0160 
0161         ThreadWeaver::Queue::instance()->enqueue( QSharedPointer<ThreadWeaver::Job>(d->worker) );
0162     }
0163 }
0164 
0165 void
0166 ServiceSqlQueryMaker::done( ThreadWeaver::JobPointer job )
0167 {
0168     Q_UNUSED( job )
0169 
0170     d->worker.clear();
0171     Q_EMIT queryDone();
0172 }
0173 
0174 QueryMaker*
0175 ServiceSqlQueryMaker::setQueryType( QueryType type)
0176 {
0177     switch( type ) {
0178     case QueryMaker::Track:
0179         //make sure to keep this method in sync with handleTracks(QStringList) and the SqlTrack ctor
0180         if( d->queryType == QueryMaker::None )
0181         {
0182             QString prefix = m_metaFactory->tablePrefix();
0183             //d->queryFrom = ' ' + prefix + "_tracks";
0184 
0185             d->withoutDuplicates = true;
0186             d->queryFrom = ' ' + prefix + "_tracks";
0187             d->queryType = QueryMaker::Track;
0188             d->queryReturnValues =  m_metaFactory->getTrackSqlRows() + QLatin1Char(',') +
0189             m_metaFactory->getAlbumSqlRows() + QLatin1Char(',') +
0190             m_metaFactory->getArtistSqlRows() + QLatin1Char(',') +
0191             m_metaFactory->getGenreSqlRows();
0192 
0193             d->linkedTables |= Private::GENRE_TABLE;
0194             d->linkedTables |= Private::ARTISTS_TABLE;
0195             d->linkedTables |= Private::ALBUMS_TABLE;
0196 
0197             d->queryOrderBy += " GROUP BY " + prefix + "_tracks.id"; //fixes the same track being shown several times due to being in several genres
0198 
0199             if ( d->linkedTables & Private::ARTISTS_TABLE )
0200             {
0201                 d->queryOrderBy += " ORDER BY " + prefix + "_tracks.album_id"; //make sure items are added as album groups
0202             }
0203         }
0204         return this;
0205 
0206     case QueryMaker::Artist:
0207         if( d->queryType == QueryMaker::None )
0208         {
0209             QString prefix = m_metaFactory->tablePrefix();
0210             d->queryFrom = ' ' + prefix + "_tracks";
0211             d->linkedTables |= Private::ARTISTS_TABLE;
0212             d->linkedTables |= Private::ALBUMS_TABLE;
0213             d->queryType = QueryMaker::Artist;
0214             d->withoutDuplicates = true;
0215             d->queryReturnValues = m_metaFactory->getArtistSqlRows();
0216 
0217             d->queryOrderBy += " GROUP BY " + prefix + "_tracks.id"; //fixes the same track being shown several times due to being in several genres
0218         }
0219         return this;
0220 
0221     case QueryMaker::AlbumArtist:
0222         if( d->queryType == QueryMaker::None )
0223         {
0224             QString prefix = m_metaFactory->tablePrefix();
0225             d->queryFrom = ' ' + prefix + "_tracks";
0226             d->linkedTables |= Private::ALBUMARTISTS_TABLE;
0227             d->queryType = QueryMaker::AlbumArtist;
0228             d->withoutDuplicates = true;
0229             d->queryReturnValues = QStringLiteral( "albumartists.id, " ) +
0230                                             "albumartists.name, " +
0231                                             "albumartists.description ";
0232             d->queryOrderBy += " GROUP BY " + prefix + "_tracks.id"; //fixes the same track being shown several times due to being in several genres
0233         }
0234         return this;
0235 
0236     case QueryMaker::Album:
0237         if( d->queryType == QueryMaker::None )
0238         {
0239             QString prefix = m_metaFactory->tablePrefix();
0240             d->queryFrom = ' ' + prefix + "_tracks";
0241             d->queryType = QueryMaker::Album;
0242             d->linkedTables |= Private::ALBUMS_TABLE;
0243             d->linkedTables |= Private::ARTISTS_TABLE;
0244             d->withoutDuplicates = true;
0245             d->queryReturnValues = m_metaFactory->getAlbumSqlRows() + QLatin1Char(',') +
0246             m_metaFactory->getArtistSqlRows();
0247 
0248             d->queryOrderBy += " GROUP BY " + prefix + "_tracks.id"; //fixes the same track being shown several times due to being in several genres
0249         }
0250         return this;
0251 
0252     case QueryMaker::Composer:
0253         /* if( d->queryType == Private::NONE )
0254             {
0255                 d->queryType = QueryMaker::Composer;
0256                 d->withoutDuplicates = true;
0257                 d->linkedTables |= QueryMaker::Composer_TAB;
0258                 d->queryReturnValues = "composer.name, composer.id";
0259             }*/
0260         return this;
0261 
0262     case QueryMaker::Genre:
0263         if( d->queryType == QueryMaker::None )
0264         {
0265             QString prefix = m_metaFactory->tablePrefix();
0266             d->queryFrom = ' ' + prefix + "_genre";
0267             d->queryType = QueryMaker::Genre;
0268             //d->linkedTables |= QueryMaker::Albums_TABLE;
0269             //d->linkedTables |= QueryMaker::Genre_TABLE;
0270             d->withoutDuplicates = true;
0271             d->queryReturnValues = m_metaFactory->getGenreSqlRows();
0272             d->queryOrderBy = " GROUP BY " + prefix +"_genre.name"; // HAVING COUNT ( " + prefix +"_genre.name ) > 10 ";
0273         }
0274         return this;
0275 
0276     case QueryMaker::Year:
0277         /*if( d->queryType == Private::NONE )
0278         {
0279             d->queryType = Private::YEAR;
0280             d->withoutDuplicates = true;
0281             d->linkedTables |= Private::YEAR_TAB;
0282             d->queryReturnValues = "year.name, year.id";
0283         }*/
0284         return this;
0285 
0286     case QueryMaker::Custom:
0287         /* if( d->queryType == Private::NONE )
0288         d->queryType = Private::CUSTOM;*/
0289         return this;
0290     
0291     case QueryMaker::Label:
0292     case QueryMaker::None:
0293         return this;
0294     }
0295 
0296     return this;
0297 }
0298 
0299 QueryMaker*
0300 ServiceSqlQueryMaker::addMatch( const Meta::TrackPtr &track )
0301 {
0302     //DEBUG_BLOCK
0303     Q_UNUSED( track );
0304     //TODO still pondering this one...
0305     return this;
0306 }
0307 
0308 QueryMaker*
0309 ServiceSqlQueryMaker::addMatch( const Meta::ArtistPtr &artist, QueryMaker::ArtistMatchBehaviour behaviour )
0310 {
0311     QString prefix = m_metaFactory->tablePrefix();
0312 
0313     if( !d )
0314         return this;
0315 
0316     if( behaviour == AlbumArtists || behaviour == AlbumOrTrackArtists )
0317         d->linkedTables |= Private::ALBUMARTISTS_TABLE;
0318 
0319     //this should NOT be made into a static cast as this might get called with an incompatible type!
0320     const Meta::ServiceArtist * serviceArtist = dynamic_cast<const Meta::ServiceArtist *>( artist.data() );
0321     d->linkedTables |= Private::ARTISTS_TABLE;
0322     if( serviceArtist )
0323     {
0324         switch( behaviour )
0325         {
0326             case TrackArtists:
0327                  d->queryMatch += QString( " AND " + prefix + "_artists.id= '%1'" ).arg( serviceArtist->id() );
0328                  break;
0329             case AlbumArtists:
0330                  d->queryMatch += QStringLiteral( " AND albumartists.id= '%1'" ).arg( serviceArtist->id() );
0331                  break;
0332             case AlbumOrTrackArtists:
0333                  d->queryMatch += QString( " AND ( " + prefix + "_artists.id= '%1' OR albumartists.id= '%1' )" ).arg( serviceArtist->id() );
0334                  break;
0335         }
0336     }
0337     else
0338     {
0339         switch( behaviour )
0340         {
0341             case TrackArtists:
0342                  d->queryMatch += QString( " AND " + prefix + "_artists.name= '%1'" ).arg( escape( artist->name() ) );
0343                  break;
0344             case AlbumArtists:
0345                  d->queryMatch += QStringLiteral( " AND albumartists.name= '%1'" ).arg( escape( artist->name() ) );
0346                  break;
0347             case AlbumOrTrackArtists:
0348                  d->queryMatch += QString( " AND ( " + prefix + "_artists.name= '%1' OR albumartists.name= '%1' )" ).arg( escape( artist->name() ) );
0349                  break;
0350         }
0351     }
0352     return this;
0353 }
0354 
0355 QueryMaker*
0356 ServiceSqlQueryMaker::addMatch( const Meta::AlbumPtr &album )
0357 {
0358     QString prefix = m_metaFactory->tablePrefix();
0359 
0360     if( !d )
0361         return this;
0362 
0363     //this should NOT be made into a static cast as this might get called with an incompatible type!
0364     const Meta::ServiceAlbumPtr serviceAlbum = Meta::ServiceAlbumPtr::dynamicCast( album );
0365 
0366     d->linkedTables |= Private::ALBUMS_TABLE;
0367     d->linkedTables |= Private::ARTISTS_TABLE;
0368     if( d->queryType == QueryMaker::Genre )
0369         d->linkedTables |= Private::GENRE_TABLE;
0370     if( serviceAlbum )
0371     {
0372         d->queryMatch += QString( " AND " + prefix + "_albums.id = '%1'" ).arg( serviceAlbum->id() );
0373     }
0374     else
0375     {
0376         d->queryMatch += QString( " AND " + prefix + "_albums.name='%1'" ).arg( escape( album->name() ) );
0377     }
0378     return this;
0379 }
0380 
0381 QueryMaker*
0382 ServiceSqlQueryMaker::addMatch( const Meta::GenrePtr &genre )
0383 {
0384     QString prefix = m_metaFactory->tablePrefix();
0385 
0386     //this should NOT be made into a static cast as this might get called with an incompatible type!
0387     const Meta::ServiceGenre* serviceGenre = static_cast<const Meta::ServiceGenre *>( genre.data() );
0388     if( !d || !serviceGenre )
0389         return this;
0390 
0391     //genres link to albums in the database, so we need to start from here unless soig a track query
0392 
0393    // if (  d->queryType == Private::TRACK ) {
0394         //d->queryFrom = ' ' + prefix + "_tracks";
0395     d->linkedTables |= Private::ALBUMS_TABLE;
0396     //} else
0397         //d->queryFrom = ' ' + prefix + "_albums";
0398 
0399         //if ( d->queryType == Private::ARTIST )
0400     //d->linkedTables |= Private::ARTISTS_TABLE;
0401     d->linkedTables |= Private::GENRE_TABLE;
0402     d->queryMatch += QString( " AND " + prefix + "_genre.name = '%1'" ).arg( serviceGenre->name() );
0403 
0404     return this;
0405 }
0406 
0407 QueryMaker*
0408 ServiceSqlQueryMaker::addMatch( const Meta::ComposerPtr &composer )
0409 {
0410     Q_UNUSED( composer );
0411     //TODO
0412     return this;
0413 }
0414 
0415 QueryMaker*
0416 ServiceSqlQueryMaker::addMatch( const Meta::YearPtr &year )
0417 {
0418     Q_UNUSED( year );
0419     //TODO
0420     return this;
0421 }
0422 
0423 QueryMaker*
0424 ServiceSqlQueryMaker::addMatch( const Meta::LabelPtr &label )
0425 {
0426     Q_UNUSED( label );
0427     //TODO
0428     return this;
0429 }
0430 
0431 QueryMaker*
0432 ServiceSqlQueryMaker::addFilter( qint64 value, const QString &filter, bool matchBegin, bool matchEnd )
0433 {
0434     if( !isValidValue( value ) )
0435     {
0436         return this;
0437     }
0438     //a few hacks needed by some of the speedup code:
0439     if ( d->queryType == QueryMaker::Genre )
0440     {
0441         QString prefix = m_metaFactory->tablePrefix();
0442         d->queryFrom = ' ' + prefix + "_tracks";
0443         d->linkedTables |= Private::ALBUMS_TABLE;
0444         d->linkedTables |= Private::ARTISTS_TABLE;
0445         d->linkedTables |= Private::GENRE_TABLE;
0446     }
0447     QString like = likeCondition( filter, !matchBegin, !matchEnd );
0448     d->queryFilter += QStringLiteral( " %1 %2 %3 " ).arg( andOr(), nameForValue( value ), like );
0449     return this;
0450 }
0451 
0452 QueryMaker*
0453 ServiceSqlQueryMaker::excludeFilter( qint64 value, const QString &filter, bool matchBegin, bool matchEnd )
0454 {
0455 
0456     if( isValidValue( value ) )
0457     {
0458         QString like = likeCondition( filter, !matchBegin, !matchEnd );
0459         d->queryFilter += QStringLiteral( " %1 NOT %2 %3 " ).arg( andOr(), nameForValue( value ), like );
0460     }
0461     return this;
0462 }
0463 
0464 QueryMaker*
0465 ServiceSqlQueryMaker::addNumberFilter( qint64 value, qint64 filter, QueryMaker::NumberComparison compare )
0466 {
0467     AMAROK_NOTIMPLEMENTED
0468     Q_UNUSED( value )
0469     Q_UNUSED( filter )
0470     Q_UNUSED( compare )
0471     return this;
0472 }
0473 
0474 QueryMaker*
0475 ServiceSqlQueryMaker::excludeNumberFilter( qint64 value, qint64 filter, QueryMaker::NumberComparison compare )
0476 {
0477     AMAROK_NOTIMPLEMENTED
0478     Q_UNUSED( value )
0479     Q_UNUSED( filter )
0480     Q_UNUSED( compare )
0481     return this;
0482 }
0483 
0484 QueryMaker*
0485 ServiceSqlQueryMaker::addReturnValue( qint64 value )
0486 {
0487     Q_UNUSED( value );
0488     /*if( d->queryType == Private::CUSTOM )
0489     {
0490         if ( !d->queryReturnValues.isEmpty() )
0491             d->queryReturnValues += ',';
0492         d->queryReturnValues += nameForValue( value );
0493     }*/
0494     return this;
0495 }
0496 
0497 QueryMaker*
0498 ServiceSqlQueryMaker::addReturnFunction( ReturnFunction function, qint64 value )
0499 {
0500     Q_UNUSED( value )
0501     Q_UNUSED( function )
0502     return this;
0503 }
0504 
0505 QueryMaker*
0506 ServiceSqlQueryMaker::orderBy( qint64 value, bool descending )
0507 {
0508     Q_UNUSED( value );
0509     if ( d->queryOrderBy.isEmpty() )
0510     d->queryOrderBy = QStringLiteral(" ORDER BY name "); //TODO FIX!!
0511     d->queryOrderBy += QStringLiteral( " %1 " ).arg( descending ? "DESC" : "ASC" );
0512     return this;
0513 }
0514 
0515 QueryMaker*
0516 ServiceSqlQueryMaker::limitMaxResultSize( int size )
0517 {
0518     d->maxResultSize = size;
0519     return this;
0520 }
0521 
0522 void
0523 ServiceSqlQueryMaker::linkTables()
0524 {
0525     if( !d->linkedTables )
0526         return;
0527 
0528     QString prefix = m_metaFactory->tablePrefix();
0529 
0530     //d->queryFrom = ' ' + prefix + "_tracks";
0531 
0532     if( d->linkedTables & Private::ALBUMS_TABLE )
0533        d->queryFrom += " LEFT JOIN " + prefix + "_albums ON " + prefix + "_tracks.album_id = " + prefix + "_albums.id";
0534     if( d->linkedTables & Private::ARTISTS_TABLE )
0535        d->queryFrom += " LEFT JOIN " + prefix + "_artists ON " + prefix + "_albums.artist_id = " + prefix + "_artists.id";
0536     if( d->linkedTables & Private::ALBUMARTISTS_TABLE )
0537         d->queryFrom += " LEFT JOIN " + prefix + "_artists AS albumartists ON " + prefix + "_albums.artist_id = albumartists.id";
0538     if( d->linkedTables & Private::GENRE_TABLE )
0539        d->queryFrom += " LEFT JOIN " + prefix + "_genre ON " + prefix + "_genre.album_id = " + prefix + "_albums.id";
0540 }
0541 
0542 void
0543 ServiceSqlQueryMaker::buildQuery()
0544 {
0545     if( d->albumMode == OnlyCompilations )
0546         return;
0547 
0548     linkTables();
0549     QString query = QStringLiteral("SELECT ");
0550     if ( d->withoutDuplicates )
0551         query += QLatin1String("DISTINCT ");
0552     query += d->queryReturnValues;
0553     query += QLatin1String(" FROM ");
0554     query += d->queryFrom;
0555     query += QLatin1String(" WHERE 1 "); // oh... to not have to bother with the leadig "AND"
0556                           // that may or may not be needed
0557     query += d->queryMatch;
0558     if ( !d->queryFilter.isEmpty() )
0559     {
0560         query += QLatin1String(" AND ( 1 ");
0561         query += d->queryFilter;
0562         query += QLatin1String(" ) ");
0563     }
0564     //query += d->queryFilter;
0565     query += d->queryOrderBy;
0566     if ( d->maxResultSize > -1 )
0567         query += QStringLiteral( " LIMIT %1 OFFSET 0 " ).arg( d->maxResultSize );
0568     query += ';';
0569     d->query = query;
0570 }
0571 
0572 QString
0573 ServiceSqlQueryMaker::query()
0574 {
0575     if ( d->query.isEmpty() )
0576         buildQuery();
0577     return d->query;
0578 }
0579 
0580 QStringList
0581 ServiceSqlQueryMaker::runQuery( const QString &query )
0582 {
0583    if( d->albumMode == OnlyCompilations )
0584        return QStringList();
0585 
0586    return m_collection->query( query );
0587 }
0588 
0589 void
0590 ServiceSqlQueryMaker::handleResult( const QStringList &result )
0591 {
0592     if( !result.isEmpty() )
0593     {
0594         switch( d->queryType ) {
0595         /*case Private::CUSTOM:
0596             Q_EMIT newResultReady( result );
0597             break;*/
0598         case QueryMaker::Track:
0599             handleTracks( result );
0600             break;
0601         case QueryMaker::Artist:
0602         case QueryMaker::AlbumArtist:
0603             handleArtists( result );
0604             break;
0605         case QueryMaker::Album:
0606             handleAlbums( result );
0607             break;
0608         case QueryMaker::Genre:
0609             handleGenres( result );
0610             break;
0611       /*  case QueryMaker::Composer:
0612             handleComposers( result );
0613             break;
0614         case Private::YEAR:
0615             handleYears( result );
0616             break;*/
0617         case QueryMaker::None:
0618             debug() << "Warning: queryResult with queryType == NONE";
0619 
0620         default:
0621             break;
0622         }
0623     }
0624     else
0625     {
0626         switch( d->queryType )
0627         {
0628             case QueryMaker::Custom:
0629                 Q_EMIT newResultReady( QStringList() );
0630                 break;
0631             case QueryMaker::Track:
0632                 Q_EMIT newTracksReady( Meta::TrackList() );
0633                 break;
0634             case QueryMaker::Artist:
0635                 Q_EMIT newArtistsReady( Meta::ArtistList() );
0636                 break;
0637             case QueryMaker::Album:
0638                 Q_EMIT newAlbumsReady( Meta::AlbumList() );
0639                 break;
0640             case QueryMaker::AlbumArtist:
0641                 Q_EMIT newArtistsReady( Meta::ArtistList() );
0642                 break;
0643             case QueryMaker::Genre:
0644                 Q_EMIT newGenresReady( Meta::GenreList() );
0645                 break;
0646             case QueryMaker::Composer:
0647                 Q_EMIT newComposersReady( Meta::ComposerList() );
0648                 break;
0649             case QueryMaker::Year:
0650                 Q_EMIT newYearsReady( Meta::YearList() );
0651                 break;
0652             case QueryMaker::None:
0653                 debug() << "Warning: queryResult with queryType == NONE";
0654             default:
0655                 break;
0656         }
0657     }
0658 
0659     //queryDone will be emitted in done(Job*)
0660 }
0661 
0662 bool
0663 ServiceSqlQueryMaker::isValidValue( qint64 value )
0664 {
0665     return value == Meta::valTitle ||
0666            value == Meta::valArtist ||
0667            value == Meta::valAlbum ||
0668            value == Meta::valGenre;
0669 }
0670 
0671 QString
0672 ServiceSqlQueryMaker::nameForValue( qint64 value )
0673 {
0674     QString prefix = m_metaFactory->tablePrefix();
0675 
0676     switch( value )
0677     {
0678         case Meta::valTitle:
0679             d->linkedTables |= Private::TRACKS_TABLE;
0680             return prefix + "_tracks.name";
0681         case Meta::valArtist:
0682             d->linkedTables |= Private::ARTISTS_TABLE;
0683             return prefix + "_artists.name";
0684         case Meta::valAlbum:
0685             d->linkedTables |= Private::ALBUMS_TABLE;
0686             return prefix + "_albums.name";
0687         case Meta::valGenre:
0688             d->queryFrom = prefix + "_tracks";
0689             d->linkedTables |= Private::ALBUMS_TABLE;
0690             d->linkedTables |= Private::GENRE_TABLE;
0691             return prefix + "_genre.name";
0692         default:
0693             debug() << "ERROR: unknown value in ServiceSqlQueryMaker::nameForValue( qint64 ): value=" + QString::number( value );
0694             return QString();
0695     }
0696 }
0697 
0698 void
0699 ServiceSqlQueryMaker::handleTracks( const QStringList &result )
0700 {
0701     Meta::TrackList tracks;
0702     //SqlRegistry* reg = m_collection->registry();
0703     int rowCount = ( m_metaFactory->getTrackSqlRowCount() +
0704                    m_metaFactory->getAlbumSqlRowCount() +
0705                    m_metaFactory->getArtistSqlRowCount() +
0706                    m_metaFactory->getGenreSqlRowCount() );
0707 
0708     int resultRows = result.count() / rowCount;
0709 
0710     for( int i = 0; i < resultRows; i++ )
0711     {
0712         QStringList row = result.mid( i*rowCount, rowCount );
0713 
0714         Meta::TrackPtr trackptr =  m_registry->getTrack( row );
0715         tracks.append( trackptr );
0716     }
0717 
0718     Q_EMIT newTracksReady( tracks );
0719 }
0720 
0721 void
0722 ServiceSqlQueryMaker::handleArtists( const QStringList &result )
0723 {
0724     Meta::ArtistList artists;
0725    // SqlRegistry* reg = m_collection->registry();
0726     int rowCount = m_metaFactory->getArtistSqlRowCount();
0727     int resultRows = result.size() / rowCount;
0728     for( int i = 0; i < resultRows; i++ )
0729     {
0730         QStringList row = result.mid( i*rowCount, rowCount );
0731         artists.append( m_registry->getArtist( row ) );
0732     }
0733     Q_EMIT newArtistsReady( artists );
0734 }
0735 
0736 void
0737 ServiceSqlQueryMaker::handleAlbums( const QStringList &result )
0738 {
0739     Meta::AlbumList albums;
0740     int rowCount = m_metaFactory->getAlbumSqlRowCount() +  m_metaFactory->getArtistSqlRowCount();
0741     int resultRows = result.size() / rowCount;
0742 
0743     for( int i = 0; i < resultRows; i++ )
0744     {
0745         QStringList row = result.mid( i*rowCount, rowCount );
0746         albums.append( m_registry->getAlbum( row ) );
0747     }
0748     Q_EMIT newAlbumsReady( albums );
0749 }
0750 
0751 void
0752 ServiceSqlQueryMaker::handleGenres( const QStringList &result )
0753 {
0754     Meta::GenreList genres;
0755 
0756     int rowCount = m_metaFactory->getGenreSqlRowCount();
0757     int resultRows = result.size() / rowCount;
0758     for( int i = 0; i < resultRows; i++ )
0759     {
0760         QStringList row = result.mid( i*rowCount, rowCount );
0761         genres.append( m_registry->getGenre( row ) );
0762     }
0763     Q_EMIT newGenresReady( genres );
0764 }
0765 
0766 /*void
0767 ServiceSqlQueryMaker::handleComposers( const QStringList &result )
0768 {
0769     Meta::ComposerList composers;
0770     SqlRegistry* reg = m_collection->registry();
0771     for( QStringListIterator iter( result ); iter.hasNext(); )
0772     {
0773         QString name = iter.next();
0774         QString id = iter.next();
0775         composers.append( reg->getComposer( name, id.toInt() ) );
0776     }
0777     Q_EMIT newResultReady( composers );
0778 }
0779 
0780 void
0781 ServiceSqlQueryMaker::handleYears( const QStringList &result )
0782 {
0783     Meta::YearList years;
0784     SqlRegistry* reg = m_collection->registry();
0785     for( QStringListIterator iter( result ); iter.hasNext(); )
0786     {
0787         QString name = iter.next();
0788         QString id = iter.next();
0789         years.append( reg->getYear( name, id.toInt() ) );
0790     }
0791     Q_EMIT newResultReady( years );
0792 }*/
0793 
0794 QString
0795 ServiceSqlQueryMaker::escape( const QString &text ) const //krazy2:exclude=constref
0796 {
0797     auto storage = StorageManager::instance()->sqlStorage();
0798     if( storage )
0799         return storage->escape( text );
0800     else
0801         return QString();
0802 }
0803 
0804 QString
0805 ServiceSqlQueryMaker::likeCondition( const QString &text, bool anyBegin, bool anyEnd ) const
0806 {
0807     if( anyBegin || anyEnd )
0808     {
0809         QString escaped = text;
0810         escaped = escape( escaped );
0811         //see comments in SqlQueryMaker::likeCondition
0812         escaped.replace( '%', QLatin1String("/%") ).replace( '_', QLatin1String("/_") );
0813 
0814         QString ret = QStringLiteral(" LIKE ");
0815 
0816         ret += '\'';
0817         if ( anyBegin )
0818             ret += '%';
0819         ret += escaped;
0820         if ( anyEnd )
0821             ret += '%';
0822         ret += '\'';
0823 
0824         //Case insensitive collation for queries
0825         ret += QLatin1String(" COLLATE utf8_unicode_ci ");
0826 
0827         //Use / as the escape character
0828         ret += QLatin1String(" ESCAPE '/' ");
0829 
0830         return ret;
0831     }
0832     else
0833     {
0834         return QStringLiteral( " = '%1' " ).arg( escape( text ) );
0835     }
0836 }
0837 
0838 QueryMaker*
0839 ServiceSqlQueryMaker::beginAnd()
0840 {
0841     d->queryFilter += andOr();
0842     d->queryFilter += QLatin1String(" ( 1 ");
0843     d->andStack.push( true );
0844     return this;
0845 }
0846 
0847 QueryMaker*
0848 ServiceSqlQueryMaker::beginOr()
0849 {
0850     d->queryFilter += andOr();
0851     d->queryFilter += QLatin1String(" ( 0 ");
0852     d->andStack.push( false );
0853     return this;
0854 }
0855 
0856 QueryMaker*
0857 ServiceSqlQueryMaker::endAndOr()
0858 {
0859     d->queryFilter += ')';
0860     d->andStack.pop();
0861     return this;
0862 }
0863 
0864 QString
0865 ServiceSqlQueryMaker::andOr() const
0866 {
0867     return d->andStack.top() ? " AND " : " OR ";
0868 }
0869 
0870 QueryMaker *
0871 ServiceSqlQueryMaker::setAlbumQueryMode(AlbumQueryMode mode)
0872 {
0873     d->albumMode = mode;
0874     return this;
0875 }
0876 
0877 #include "ServiceSqlQueryMaker.moc"