/* * Strawberry Music Player * This file was part of Clementine. * Copyright 2010, David Sansome * Copyright 2018-2024, Jonas Kvinge * * Strawberry 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 3 of the License, or * (at your option) any later version. * * Strawberry 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 Strawberry. If not, see . * */ #include "config.h" #include #include #include #include #include #include #include #include #include "core/sqlquery.h" #include "core/song.h" #include "collectionquery.h" #include "collectionfilteroptions.h" using namespace Qt::Literals::StringLiterals; CollectionQuery::CollectionQuery(const QSqlDatabase &db, const QString &songs_table, const CollectionFilterOptions &filter_options) : SqlQuery(db), songs_table_(songs_table), include_unavailable_(false), duplicates_only_(false), limit_(-1) { if (filter_options.max_age() != -1) { qint64 cutoff = QDateTime::currentSecsSinceEpoch() - filter_options.max_age(); where_clauses_ << u"ctime > ?"_s; bound_values_ << cutoff; } duplicates_only_ = filter_options.filter_mode() == CollectionFilterOptions::FilterMode::Duplicates; if (filter_options.filter_mode() == CollectionFilterOptions::FilterMode::Untagged) { where_clauses_ << u"(artist = '' OR album = '' OR title ='')"_s; } } void CollectionQuery::AddWhere(const QString &column, const QVariant &value, const QString &op) { // Ignore 'literal' for IN if (op.compare("IN"_L1, Qt::CaseInsensitive) == 0) { const QStringList values = value.toStringList(); QStringList final_values; final_values.reserve(values.count()); for (const QString &single_value : values) { final_values.append(u"?"_s); bound_values_ << single_value; } where_clauses_ << QStringLiteral("%1 IN (%2)").arg(column, final_values.join(u',')); } else { // Do integers inline - sqlite seems to get confused when you pass integers to bound parameters if (value.metaType().id() == QMetaType::Int) { where_clauses_ << QStringLiteral("%1 %2 %3").arg(column, op, value.toString()); } else if (value.metaType().id() == QMetaType::QString && value.toString().isNull()) { where_clauses_ << QStringLiteral("%1 %2 ?").arg(column, op); bound_values_ << ""_L1; } else { where_clauses_ << QStringLiteral("%1 %2 ?").arg(column, op); bound_values_ << value; } } } void CollectionQuery::AddCompilationRequirement(const bool compilation) { // The unary + is added to prevent sqlite from using the index idx_comp_artist. where_clauses_ << QStringLiteral("+compilation_effective = %1").arg(compilation ? 1 : 0); } QString CollectionQuery::GetInnerQuery() const { return duplicates_only_ ? QStringLiteral(" INNER JOIN (select * from duplicated_songs) dsongs " "ON (%songs_table.artist = dsongs.dup_artist " "AND %songs_table.album = dsongs.dup_album " "AND %songs_table.title = dsongs.dup_title) ") : QString(); } bool CollectionQuery::Exec() { QString sql = QStringLiteral("SELECT %1 FROM %2 %3").arg(column_spec_, songs_table_, GetInnerQuery()); QStringList where_clauses(where_clauses_); if (!include_unavailable_) { where_clauses << u"unavailable = 0"_s; } if (!where_clauses.isEmpty()) sql += " WHERE "_L1 + where_clauses.join(" AND "_L1); if (!order_by_.isEmpty()) sql += " ORDER BY "_L1 + order_by_; if (limit_ != -1) sql += " LIMIT "_L1 + QString::number(limit_); sql.replace("%songs_table"_L1, songs_table_); if (!QSqlQuery::prepare(sql)) return false; // Bind values for (const QVariant &value : std::as_const(bound_values_)) { QSqlQuery::addBindValue(value); } return QSqlQuery::exec(); } bool CollectionQuery::Next() { return QSqlQuery::next(); } QVariant CollectionQuery::Value(const int column) const { return QSqlQuery::value(column); }