Warning, file /multimedia/amarok/src/services/ServiceSqlQueryMaker.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
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"