Rework DB session + transactions. Now can handle multiple read only transactions in parallel

This commit is contained in:
emeric
2019-07-16 13:17:23 +02:00
parent 2a1fb0182b
commit 38ce2600d2
79 changed files with 1840 additions and 1369 deletions
+35 -18
View File
@@ -25,6 +25,7 @@
#include "Cluster.hpp"
#include "Release.hpp"
#include "SqlQuery.hpp"
#include "Session.hpp"
#include "Track.hpp"
#include "User.hpp"
@@ -40,34 +41,44 @@ _MBID {MBID}
}
std::vector<Artist::pointer>
Artist::getByName(Wt::Dbo::Session& session, const std::string& name)
Artist::getByName(Session& session, const std::string& name)
{
Wt::Dbo::collection<Artist::pointer> res = session.find<Artist>().where("name = ?").bind( std::string{name, 0, _maxNameLength} );
session.checkSharedLocked();
Wt::Dbo::collection<Artist::pointer> res = session.getDboSession().find<Artist>().where("name = ?").bind( std::string{name, 0, _maxNameLength} );
return std::vector<Artist::pointer>(res.begin(), res.end());
}
Artist::pointer
Artist::getByMBID(Wt::Dbo::Session& session, const std::string& mbid)
Artist::getByMBID(Session& session, const std::string& mbid)
{
return session.find<Artist>().where("mbid = ?").bind(mbid);
session.checkSharedLocked();
return session.getDboSession().find<Artist>().where("mbid = ?").bind(mbid);
}
Artist::pointer
Artist::getById(Wt::Dbo::Session& session, IdType id)
Artist::getById(Session& session, IdType id)
{
return session.find<Artist>().where("id = ?").bind(id);
session.checkSharedLocked();
return session.getDboSession().find<Artist>().where("id = ?").bind(id);
}
Artist::pointer
Artist::create(Wt::Dbo::Session& session, const std::string& name, const std::string& MBID)
Artist::create(Session& session, const std::string& name, const std::string& MBID)
{
return session.add(std::make_unique<Artist>(name, MBID));
session.checkUniqueLocked();
Artist::pointer res {session.getDboSession().add(std::make_unique<Artist>(name, MBID))};
session.getDboSession().flush();
return res;
}
std::vector<Artist::pointer>
Artist::getAll(Wt::Dbo::Session& session, boost::optional<std::size_t> offset, boost::optional<std::size_t> size)
Artist::getAll(Session& session, boost::optional<std::size_t> offset, boost::optional<std::size_t> size)
{
Wt::Dbo::collection<pointer> res = session.find<Artist>()
session.checkSharedLocked();
Wt::Dbo::collection<pointer> res = session.getDboSession().find<Artist>()
.offset(offset ? static_cast<int>(*offset) : -1)
.limit(size ? static_cast<int>(*size) : -1)
.orderBy("sort_name COLLATE NOCASE");
@@ -76,19 +87,22 @@ Artist::getAll(Wt::Dbo::Session& session, boost::optional<std::size_t> offset, b
}
std::vector<Artist::pointer>
Artist::getAllOrphans(Wt::Dbo::Session& session)
Artist::getAllOrphans(Session& session)
{
Wt::Dbo::collection<Wt::Dbo::ptr<Artist>> res {session.query<Wt::Dbo::ptr<Artist>>("SELECT DISTINCT a FROM artist a WHERE NOT EXISTS(SELECT 1 FROM track t INNER JOIN track_artist_link t_a_l ON t_a_l.artist_id = a.id WHERE t.id = t_a_l.track_id)")};
session.checkSharedLocked();
Wt::Dbo::collection<Wt::Dbo::ptr<Artist>> res {session.getDboSession().query<Wt::Dbo::ptr<Artist>>("SELECT DISTINCT a FROM artist a WHERE NOT EXISTS(SELECT 1 FROM track t INNER JOIN track_artist_link t_a_l ON t_a_l.artist_id = a.id WHERE t.id = t_a_l.track_id)")};
return std::vector<pointer>(res.begin(), res.end());
}
static
Wt::Dbo::Query<Artist::pointer>
getQuery(Wt::Dbo::Session& session,
getQuery(Session& session,
const std::set<IdType>& clusterIds,
const std::vector<std::string>& keywords)
{
session.checkSharedLocked();
WhereClause where;
std::ostringstream oss;
@@ -115,7 +129,7 @@ getQuery(Wt::Dbo::Session& session,
oss << " ORDER BY a.sort_name COLLATE NOCASE";
Wt::Dbo::Query<Artist::pointer> query = session.query<Artist::pointer>( oss.str() );
Wt::Dbo::Query<Artist::pointer> query = session.getDboSession().query<Artist::pointer>( oss.str() );
for (const std::string& bindArg : where.getBindArgs())
{
@@ -126,20 +140,22 @@ getQuery(Wt::Dbo::Session& session,
}
std::vector<Artist::pointer>
Artist::getByFilter(Wt::Dbo::Session& session, const std::set<IdType>& clusters)
Artist::getByFilter(Session& session, const std::set<IdType>& clusters)
{
session.checkSharedLocked();
bool more;
return getByFilter(session, clusters, {}, {}, {}, more);
}
std::vector<Artist::pointer>
Artist::getByFilter(Wt::Dbo::Session& session,
Artist::getByFilter(Session& session,
const std::set<IdType>& clusters,
const std::vector<std::string>& keywords,
boost::optional<std::size_t> offset,
boost::optional<std::size_t> size,
bool& moreResults)
{
session.checkSharedLocked();
Wt::Dbo::collection<Artist::pointer> collection = getQuery(session, clusters, keywords)
.limit(size ? static_cast<int>(*size) + 1 : -1)
.offset(offset ? static_cast<int>(*offset) : -1);
@@ -158,9 +174,10 @@ Artist::getByFilter(Wt::Dbo::Session& session,
}
std::vector<Artist::pointer>
Artist::getLastAdded(Wt::Dbo::Session& session, Wt::WDateTime after, boost::optional<std::size_t> limit)
Artist::getLastAdded(Session& session, Wt::WDateTime after, boost::optional<std::size_t> limit)
{
Wt::Dbo::collection<Artist::pointer> res = session.query<Artist::pointer>("SELECT a from artist a INNER JOIN track_artist_link t_a_l ON t_a_l.artist_id = a.id INNER JOIN track t ON t.id = t_a_l.track_id")
session.checkSharedLocked();
Wt::Dbo::collection<Artist::pointer> res = session.getDboSession().query<Artist::pointer>("SELECT a from artist a INNER JOIN track_artist_link t_a_l ON t_a_l.artist_id = a.id INNER JOIN track t ON t.id = t_a_l.track_id")
.where("t.file_added > ?").bind(after)
.groupBy("a.id")
.orderBy("t.file_added DESC")
+10 -10
View File
@@ -36,6 +36,7 @@ namespace Database
class Cluster;
class ClusterType;
class Release;
class Session;
class Track;
class User;
@@ -49,21 +50,21 @@ class Artist : public Wt::Dbo::Dbo<Artist>
Artist(const std::string& name, const std::string& MBID = "");
// Accessors
static pointer getByMBID(Wt::Dbo::Session& session, const std::string& MBID);
static pointer getById(Wt::Dbo::Session& session, IdType id);
static std::vector<pointer> getByName(Wt::Dbo::Session& session, const std::string& name);
static std::vector<pointer> getByFilter(Wt::Dbo::Session& session,
static pointer getByMBID(Session& session, const std::string& MBID);
static pointer getById(Session& session, IdType id);
static std::vector<pointer> getByName(Session& session, const std::string& name);
static std::vector<pointer> getByFilter(Session& session,
const std::set<IdType>& clusters); // at least one track that belongs to these clusters
static std::vector<pointer> getByFilter(Wt::Dbo::Session& session,
static std::vector<pointer> getByFilter(Session& session,
const std::set<IdType>& clusters, // at least one track that belongs to these clusters
const std::vector<std::string>& keywords, // name must match all of these keywords
boost::optional<std::size_t> offset,
boost::optional<std::size_t> size,
bool& moreExpected);
static std::vector<pointer> getAll(Wt::Dbo::Session& session, boost::optional<std::size_t> offset = {}, boost::optional<std::size_t> size = {});
static std::vector<pointer> getAllOrphans(Wt::Dbo::Session& session); // No track related
static std::vector<pointer> getLastAdded(Wt::Dbo::Session& session, Wt::WDateTime after, boost::optional<std::size_t> size = {});
static std::vector<pointer> getAll(Session& session, boost::optional<std::size_t> offset = {}, boost::optional<std::size_t> size = {});
static std::vector<pointer> getAllOrphans(Session& session); // No track related
static std::vector<pointer> getLastAdded(Session& session, Wt::WDateTime after, boost::optional<std::size_t> size = {});
// Accessors
const std::string& getName(void) const { return _name; }
@@ -84,8 +85,7 @@ class Artist : public Wt::Dbo::Dbo<Artist>
void setSortName(const std::string& sortName);
// Create
static pointer create(Wt::Dbo::Session& session, const std::string& name, const std::string& MBID = "");
static pointer create(Session& session, const std::string& name, const std::string& MBID = "");
template<class Action>
void persist(Action& a)
+46 -18
View File
@@ -22,6 +22,7 @@
#include "Artist.hpp"
#include "Release.hpp"
#include "ScanSettings.hpp"
#include "Session.hpp"
#include "SqlQuery.hpp"
#include "Track.hpp"
@@ -38,31 +39,42 @@ Cluster::Cluster(Wt::Dbo::ptr<ClusterType> type, std::string name)
}
Cluster::pointer
Cluster::create(Wt::Dbo::Session& session, Wt::Dbo::ptr<ClusterType> type, std::string name)
Cluster::create(Session& session, Wt::Dbo::ptr<ClusterType> type, std::string name)
{
return session.add(std::make_unique<Cluster>(type, name));
session.checkUniqueLocked();
Cluster::pointer res {session.getDboSession().add(std::make_unique<Cluster>(type, name))};
session.getDboSession().flush();
return res;
}
std::vector<Cluster::pointer>
Cluster::getAll(Wt::Dbo::Session& session)
Cluster::getAll(Session& session)
{
Wt::Dbo::collection<Cluster::pointer> res = session.find<Cluster>();
session.checkSharedLocked();
Wt::Dbo::collection<Cluster::pointer> res {session.getDboSession().find<Cluster>()};
return std::vector<Cluster::pointer>(res.begin(), res.end());
}
std::vector<Cluster::pointer>
Cluster::getAllOrphans(Wt::Dbo::Session& session)
Cluster::getAllOrphans(Session& session)
{
Wt::Dbo::collection<Cluster::pointer> res {session.query<Cluster::pointer>("SELECT DISTINCT c FROM cluster c WHERE NOT EXISTS(SELECT 1 FROM track t INNER JOIN track_cluster t_c ON t.id = t_c.track_id)")};
session.checkSharedLocked();
Wt::Dbo::collection<Cluster::pointer> res {session.getDboSession().query<Cluster::pointer>("SELECT DISTINCT c FROM cluster c WHERE NOT EXISTS(SELECT 1 FROM track t INNER JOIN track_cluster t_c ON t.id = t_c.track_id)")};
return std::vector<Cluster::pointer>(res.begin(), res.end());
}
Cluster::pointer
Cluster::getById(Wt::Dbo::Session& session, IdType id)
Cluster::getById(Session& session, IdType id)
{
return session.find<Cluster>().where("id = ?").bind(id);
session.checkSharedLocked();
return session.getDboSession().find<Cluster>().where("id = ?").bind(id);
}
void
@@ -74,6 +86,9 @@ Cluster::addTrack(Wt::Dbo::ptr<Track> track)
std::vector<Wt::Dbo::ptr<Track>>
Cluster::getTracks(int offset, int limit) const
{
assert(session());
assert(IdIsValid(self()->id()));
Wt::Dbo::collection<Track::pointer> res = session()->query<Track::pointer>("SELECT t FROM track t INNER JOIN cluster c ON c.id = t_c.cluster_id INNER JOIN track_cluster t_c ON t_c.track_id = t.id")
.where("c.id = ?").bind(self()->id())
.offset(offset)
@@ -113,38 +128,51 @@ ClusterType::ClusterType(std::string name)
}
std::vector<ClusterType::pointer>
ClusterType::getAllOrphans(Wt::Dbo::Session& session)
ClusterType::getAllOrphans(Session& session)
{
Wt::Dbo::collection<pointer> res = session.query<Wt::Dbo::ptr<ClusterType>>("select c_t from cluster_type c_t LEFT OUTER JOIN cluster c ON c_t.id = c.cluster_type_id WHERE c.id IS NULL");
session.checkSharedLocked();
Wt::Dbo::collection<pointer> res = session.getDboSession().query<Wt::Dbo::ptr<ClusterType>>("select c_t from cluster_type c_t LEFT OUTER JOIN cluster c ON c_t.id = c.cluster_type_id WHERE c.id IS NULL");
return std::vector<pointer>(res.begin(), res.end());
}
ClusterType::pointer
ClusterType::getByName(Wt::Dbo::Session& session, std::string name)
ClusterType::getByName(Session& session, std::string name)
{
return session.find<ClusterType>().where("name = ?").bind(name);
session.checkSharedLocked();
return session.getDboSession().find<ClusterType>().where("name = ?").bind(name);
}
ClusterType::pointer
ClusterType::getById(Wt::Dbo::Session& session, IdType id)
ClusterType::getById(Session& session, IdType id)
{
return session.find<ClusterType>().where("id= ?").bind(id);
session.checkSharedLocked();
return session.getDboSession().find<ClusterType>().where("id= ?").bind(id);
}
std::vector<ClusterType::pointer>
ClusterType::getAll(Wt::Dbo::Session& session)
ClusterType::getAll(Session& session)
{
Wt::Dbo::collection<pointer> res = session.find<ClusterType>();
session.checkSharedLocked();
Wt::Dbo::collection<pointer> res = session.getDboSession().find<ClusterType>();
return std::vector<pointer>(res.begin(), res.end());
}
ClusterType::pointer
ClusterType::create(Wt::Dbo::Session& session, std::string name)
ClusterType::create(Session& session, std::string name)
{
return session.add(std::make_unique<ClusterType>(name));
session.checkUniqueLocked();
ClusterType::pointer res {session.getDboSession().add(std::make_unique<ClusterType>(name))};
session.getDboSession().flush();
return res;
}
Cluster::pointer
+11 -10
View File
@@ -33,6 +33,7 @@ namespace Database {
class Track;
class ClusterType;
class ScanSettings;
class Session;
class Cluster : public Wt::Dbo::Dbo<Cluster>
{
@@ -43,12 +44,12 @@ class Cluster : public Wt::Dbo::Dbo<Cluster>
Cluster(Wt::Dbo::ptr<ClusterType> type, std::string name);
// Find utility
static std::vector<pointer> getAll(Wt::Dbo::Session& session);
static std::vector<pointer> getAllOrphans(Wt::Dbo::Session& session);
static pointer getById(Wt::Dbo::Session& session, IdType id);
static std::vector<pointer> getAll(Session& session);
static std::vector<pointer> getAllOrphans(Session& session);
static pointer getById(Session& session, IdType id);
// Create utility
static pointer create(Wt::Dbo::Session& session, Wt::Dbo::ptr<ClusterType> type, std::string name);
static pointer create(Session& session, Wt::Dbo::ptr<ClusterType> type, std::string name);
// Accessors
const std::string& getName() const { return _name; }
@@ -89,13 +90,13 @@ class ClusterType : public Wt::Dbo::Dbo<ClusterType>
ClusterType() {}
ClusterType(std::string name);
static std::vector<pointer> getAllOrphans(Wt::Dbo::Session& session);
static pointer getByName(Wt::Dbo::Session& session, std::string name);
static pointer getById(Wt::Dbo::Session& session, IdType id);
static std::vector<pointer> getAll(Wt::Dbo::Session& session);
static std::vector<pointer> getAllOrphans(Session& session);
static pointer getByName(Session& session, std::string name);
static pointer getById(Session& session, IdType id);
static std::vector<pointer> getAll(Session& session);
static pointer create(Wt::Dbo::Session& session, std::string name);
static void remove(Wt::Dbo::Session& session, std::string name);
static pointer create(Session& session, std::string name);
static void remove(Session& session, std::string name);
// Accessors
const std::string& getName(void) const { return _name; }
+59
View File
@@ -0,0 +1,59 @@
/*
* Copyright (C) 2019 Emeric Poupon
*
* This file is part of LMS.
*
* LMS 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.
*
* LMS 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 LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Database.hpp"
#include <Wt/Dbo/FixedSqlConnectionPool.h>
#include <Wt/Dbo/backend/Sqlite3.h>
#include "utils/Logger.hpp"
#include "User.hpp"
namespace Database {
// Session living class handling the database and the login
Database::Database(const boost::filesystem::path& dbPath)
{
LMS_LOG(DB, INFO) << "Creating connection pool on file " << dbPath.string();
std::unique_ptr<Wt::Dbo::backend::Sqlite3> connection {std::make_unique<Wt::Dbo::backend::Sqlite3>(dbPath.string())};
connection->executeSql("pragma journal_mode=WAL");
// connection->setProperty("show-queries", "true");
auto connectionPool = std::make_unique<Wt::Dbo::FixedSqlConnectionPool>(std::move(connection), 10);
connectionPool->setTimeout(std::chrono::seconds(10));
_connectionPool = std::move(connectionPool);
{
auto session {createSession()};
session->prepareTables();
}
}
std::unique_ptr<Session>
Database::createSession()
{
return std::unique_ptr<Session>{new Session {_sharedMutex, *_connectionPool.get()}};
}
} // namespace Database
+48
View File
@@ -0,0 +1,48 @@
/*
* Copyright (C) 2019 Emeric Poupon
*
* This file is part of LMS.
*
* LMS 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.
*
* LMS 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 LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <shared_mutex>
#include <boost/filesystem.hpp>
#include <Wt/Dbo/SqlConnectionPool.h>
#include "Session.hpp"
namespace Database {
// Session living class handling the database and the login
class Database
{
public:
Database(const boost::filesystem::path& dbPath);
std::unique_ptr<Session> createSession();
private:
std::shared_timed_mutex _sharedMutex;
std::unique_ptr<Wt::Dbo::SqlConnectionPool> _connectionPool;
};
} // namespace Database
-76
View File
@@ -1,76 +0,0 @@
/*
* Copyright (C) 2013 Emeric Poupon
*
* This file is part of LMS.
*
* LMS 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.
*
* LMS 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 LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <boost/filesystem.hpp>
#include <memory>
#include <Wt/Dbo/Dbo.h>
#include <Wt/Dbo/SqlConnectionPool.h>
#include <Wt/Auth/Dbo/UserDatabase.h>
#include <Wt/Auth/Login.h>
#include <Wt/Auth/PasswordService.h>
#include "User.hpp"
namespace Database {
using UserDatabase = Wt::Auth::Dbo::UserDatabase<AuthInfo>;
// Session living class handling the database and the login
class Handler
{
public:
Handler(Wt::Dbo::SqlConnectionPool& connectionPool);
~Handler();
Wt::Dbo::Session& getSession() { return _session; }
void optimize();
Wt::Dbo::ptr<User> getCurrentUser(); // get the current user, may return empty
Wt::Dbo::ptr<User> getUser(const std::string& loginName);
Wt::Dbo::ptr<User> getUser(const Wt::Auth::User& authUser);
Wt::Dbo::ptr<User> createUser(const Wt::Auth::User& authUser);
Wt::Auth::AbstractUserDatabase& getUserDatabase();
Wt::Auth::Login& getLogin() { return _login; } // TODO move
// Long living shared associated services
static void configureAuth();
static const Wt::Auth::AuthService& getAuthService();
static const Wt::Auth::PasswordService& getPasswordService();
static std::unique_ptr<Wt::Dbo::SqlConnectionPool> createConnectionPool(boost::filesystem::path db);
private:
Wt::Dbo::Session _session;
UserDatabase* _users;
Wt::Auth::Login _login;
};
} // namespace Database
+46 -24
View File
@@ -23,6 +23,7 @@
#include "Artist.hpp"
#include "Cluster.hpp"
#include "Session.hpp"
#include "SqlQuery.hpp"
#include "Track.hpp"
#include "User.hpp"
@@ -38,41 +39,56 @@ _MBID(MBID)
}
std::vector<Release::pointer>
Release::getByName(Wt::Dbo::Session& session, const std::string& name)
Release::getByName(Session& session, const std::string& name)
{
Wt::Dbo::collection<Release::pointer> res = session.find<Release>().where("name = ?").bind( std::string(name, 0, _maxNameLength) );
session.checkUniqueLocked();
Wt::Dbo::collection<Release::pointer> res = session.getDboSession().find<Release>().where("name = ?").bind( std::string(name, 0, _maxNameLength) );
return std::vector<Release::pointer>(res.begin(), res.end());
}
Release::pointer
Release::getByMBID(Wt::Dbo::Session& session, const std::string& mbid)
Release::getByMBID(Session& session, const std::string& mbid)
{
return session.find<Release>().where("mbid = ?").bind(mbid);
session.checkSharedLocked();
return session.getDboSession().find<Release>().where("mbid = ?").bind(mbid);
}
Release::pointer
Release::getById(Wt::Dbo::Session& session, IdType id)
Release::getById(Session& session, IdType id)
{
return session.find<Release>().where("id = ?").bind(id);
session.checkSharedLocked();
return session.getDboSession().find<Release>().where("id = ?").bind(id);
}
Release::pointer
Release::create(Wt::Dbo::Session& session, const std::string& name, const std::string& MBID)
Release::create(Session& session, const std::string& name, const std::string& MBID)
{
return session.add(std::make_unique<Release>(name, MBID));
session.checkSharedLocked();
Release::pointer res {session.getDboSession().add(std::make_unique<Release>(name, MBID))};
session.getDboSession().flush();
return res;
}
std::size_t
Release::getCount(Wt::Dbo::Session& session)
Release::getCount(Session& session)
{
Wt::Dbo::collection<pointer> releases {session.find<Release>()};
session.checkSharedLocked();
Wt::Dbo::collection<pointer> releases {session.getDboSession().find<Release>()};
return releases.size();
}
std::vector<Release::pointer>
Release::getAll(Wt::Dbo::Session& session, boost::optional<std::size_t> offset, boost::optional<std::size_t> size)
Release::getAll(Session& session, boost::optional<std::size_t> offset, boost::optional<std::size_t> size)
{
Wt::Dbo::collection<pointer> res = session.find<Release>()
session.checkSharedLocked();
Wt::Dbo::collection<pointer> res = session.getDboSession().find<Release>()
.offset(offset ? static_cast<int>(*offset) : -1)
.limit(size ? static_cast<int>(*size) : -1)
.orderBy("name COLLATE NOCASE");
@@ -81,9 +97,11 @@ Release::getAll(Wt::Dbo::Session& session, boost::optional<std::size_t> offset,
}
std::vector<Release::pointer>
Release::getAllRandom(Wt::Dbo::Session& session, boost::optional<std::size_t> size)
Release::getAllRandom(Session& session, boost::optional<std::size_t> size)
{
Wt::Dbo::collection<pointer> res = session.find<Release>()
session.checkSharedLocked();
Wt::Dbo::collection<pointer> res = session.getDboSession().find<Release>()
.limit(size ? static_cast<int>(*size) : -1)
.orderBy("RANDOM()");
@@ -91,17 +109,21 @@ Release::getAllRandom(Wt::Dbo::Session& session, boost::optional<std::size_t> si
}
std::vector<Release::pointer>
Release::getAllOrphans(Wt::Dbo::Session& session)
Release::getAllOrphans(Session& session)
{
Wt::Dbo::collection<Release::pointer> res = session.query<Wt::Dbo::ptr<Release>>("select r from release r LEFT OUTER JOIN Track t ON r.id = t.release_id WHERE t.id IS NULL");
session.checkSharedLocked();
Wt::Dbo::collection<Release::pointer> res = session.getDboSession().query<Wt::Dbo::ptr<Release>>("select r from release r LEFT OUTER JOIN Track t ON r.id = t.release_id WHERE t.id IS NULL");
return std::vector<pointer>(res.begin(), res.end());
}
std::vector<Release::pointer>
Release::getLastAdded(Wt::Dbo::Session& session, Wt::WDateTime after, boost::optional<std::size_t> offset, boost::optional<std::size_t> limit)
Release::getLastAdded(Session& session, Wt::WDateTime after, boost::optional<std::size_t> offset, boost::optional<std::size_t> limit)
{
Wt::Dbo::collection<Release::pointer> res = session.query<Release::pointer>("SELECT r from release r INNER JOIN track t ON r.id = t.release_id")
session.checkSharedLocked();
Wt::Dbo::collection<Release::pointer> res = session.getDboSession().query<Release::pointer>("SELECT r from release r INNER JOIN track t ON r.id = t.release_id")
.where("t.file_added > ?").bind(after)
.groupBy("r.id")
.orderBy("t.file_added DESC")
@@ -113,9 +135,9 @@ Release::getLastAdded(Wt::Dbo::Session& session, Wt::WDateTime after, boost::opt
static
Wt::Dbo::Query<Release::pointer>
getQuery(Wt::Dbo::Session& session,
getQuery(Session& session,
const std::set<IdType>& clusterIds,
const std::vector<std::string> keywords)
const std::vector<std::string>& keywords)
{
WhereClause where;
@@ -144,7 +166,7 @@ getQuery(Wt::Dbo::Session& session,
oss << " ORDER BY r.name COLLATE NOCASE";
Wt::Dbo::Query<Release::pointer> query = session.query<Release::pointer>( oss.str() );
Wt::Dbo::Query<Release::pointer> query = session.getDboSession().query<Release::pointer>( oss.str() );
for (const std::string& bindArg : where.getBindArgs())
query.bind(bindArg);
@@ -153,16 +175,16 @@ getQuery(Wt::Dbo::Session& session,
}
std::vector<Release::pointer>
Release::getByFilter(Wt::Dbo::Session& session, const std::set<IdType>& clusterIds)
Release::getByFilter(Session& session, const std::set<IdType>& clusterIds)
{
bool moreResults;
return getByFilter(session, clusterIds, {}, {}, {}, moreResults);
}
std::vector<Release::pointer>
Release::getByFilter(Wt::Dbo::Session& session,
Release::getByFilter(Session& session,
const std::set<IdType>& clusterIds,
const std::vector<std::string> keywords,
const std::vector<std::string>& keywords,
boost::optional<std::size_t> offset,
boost::optional<std::size_t> size,
bool& moreResults)
+12 -12
View File
@@ -46,19 +46,19 @@ class Release : public Wt::Dbo::Dbo<Release>
Release(const std::string& name, const std::string& MBID = "");
// Accessors
static std::size_t getCount(Wt::Dbo::Session& session);
static pointer getByMBID(Wt::Dbo::Session& session, const std::string& MBID);
static std::vector<pointer> getByName(Wt::Dbo::Session& session, const std::string& name);
static pointer getById(Wt::Dbo::Session& session, IdType id);
static std::vector<pointer> getAllOrphans(Wt::Dbo::Session& session); // no track related
static std::vector<pointer> getAll(Wt::Dbo::Session& session, boost::optional<std::size_t> offset = {}, boost::optional<std::size_t> size = {});
static std::vector<pointer> getAllRandom(Wt::Dbo::Session& session, boost::optional<std::size_t> size = {});
static std::vector<pointer> getLastAdded(Wt::Dbo::Session& session, Wt::WDateTime after, boost::optional<std::size_t> offset = {}, boost::optional<std::size_t> size = {});
static std::size_t getCount(Session& session);
static pointer getByMBID(Session& session, const std::string& MBID);
static std::vector<pointer> getByName(Session& session, const std::string& name);
static pointer getById(Session& session, IdType id);
static std::vector<pointer> getAllOrphans(Session& session); // no track related
static std::vector<pointer> getAll(Session& session, boost::optional<std::size_t> offset = {}, boost::optional<std::size_t> size = {});
static std::vector<pointer> getAllRandom(Session& session, boost::optional<std::size_t> size = {});
static std::vector<pointer> getLastAdded(Session& session, Wt::WDateTime after, boost::optional<std::size_t> offset = {}, boost::optional<std::size_t> size = {});
static std::vector<pointer> getByFilter(Wt::Dbo::Session& session, const std::set<IdType>& clusters);
static std::vector<pointer> getByFilter(Wt::Dbo::Session& session,
static std::vector<pointer> getByFilter(Session& session, const std::set<IdType>& clusters);
static std::vector<pointer> getByFilter(Session& session,
const std::set<IdType>& clusters, // at least one track that belongs to these clusters
const std::vector<std::string> keywords, // name must match all of these keywords
const std::vector<std::string>& keywords, // name must match all of these keywords
boost::optional<std::size_t> offset,
boost::optional<std::size_t> size,
bool& moreExpected);
@@ -72,7 +72,7 @@ class Release : public Wt::Dbo::Dbo<Release>
std::vector<std::vector<Wt::Dbo::ptr<Cluster>>> getClusterGroups(std::vector<Wt::Dbo::ptr<ClusterType>> clusterTypes, std::size_t size) const;
// Create
static pointer create(Wt::Dbo::Session& session, const std::string& name, const std::string& MBID = "");
static pointer create(Session& session, const std::string& name, const std::string& MBID = "");
// Utility functions
boost::optional<int> getReleaseYear(bool originalDate = false) const; // 0 if unknown or various
+41 -20
View File
@@ -25,10 +25,11 @@
#include "utils/Utils.hpp"
#include "Cluster.hpp"
#include "Session.hpp"
namespace {
std::set<std::string> defaultClusterTypeNames =
const std::set<std::string> defaultClusterTypeNames =
{
"GENRE",
"ALBUMGROUPING",
@@ -40,31 +41,38 @@ std::set<std::string> defaultClusterTypeNames =
namespace Database {
void
ScanSettings::init(Session& session)
{
session.checkUniqueLocked();
pointer settings {get(session)};
if (settings)
return;
settings = session.getDboSession().add(std::make_unique<ScanSettings>());
settings.modify()->setClusterTypes(session, defaultClusterTypeNames );
}
ScanSettings::pointer
ScanSettings::get(Wt::Dbo::Session& session)
ScanSettings::get(Session& session)
{
pointer settings = session.find<ScanSettings>();
if (!settings)
{
settings = session.add(std::make_unique<ScanSettings>());
settings.modify()->setClusterTypes(defaultClusterTypeNames);
}
session.checkSharedLocked();
return settings;
return session.getDboSession().find<ScanSettings>();
}
std::set<boost::filesystem::path>
ScanSettings::getAudioFileExtensions() const
{
auto extensions = splitString(_audioFileExtensions, " ");
return std::set<boost::filesystem::path>(extensions.begin(), extensions.end());
return std::set<boost::filesystem::path>(std::cbegin(extensions), std::cend(extensions));
}
std::vector<ClusterType::pointer>
ScanSettings::getClusterTypes() const
{
return std::vector<ClusterType::pointer>(_clusterTypes.begin(), _clusterTypes.end());
return std::vector<ClusterType::pointer>(std::cbegin(_clusterTypes), std::cend(_clusterTypes));
}
void
@@ -73,20 +81,34 @@ ScanSettings::setMediaDirectory(boost::filesystem::path p)
_mediaDirectory = stringTrimEnd(p.string(), "/\\");
}
void
ScanSettings::setClusterTypes(const std::set<std::string>& clusterTypeNames)
template <typename It>
std::set<std::string> getNames(It begin, It end)
{
bool needRescan = false;
assert(session());
std::set<std::string> names;
std::transform(begin, end, std::inserter(names, std::begin(names)),
[](const ClusterType::pointer& clusterType)
{
return clusterType->getName();
});
return names;
}
void
ScanSettings::setClusterTypes(Session& session, const std::set<std::string>& clusterTypeNames)
{
session.checkUniqueLocked();
bool needRescan {};
// Create any missing cluster type
for (const auto& clusterTypeName : clusterTypeNames)
for (const std::string& clusterTypeName : clusterTypeNames)
{
auto clusterType = ClusterType::getByName(*session(), clusterTypeName);
auto clusterType {ClusterType::getByName(session, clusterTypeName)};
if (!clusterType)
{
LMS_LOG(DB, INFO) << "Creating cluster type " << clusterTypeName;
clusterType = ClusterType::create(*session(), clusterTypeName);
clusterType = ClusterType::create(session, clusterTypeName);
_clusterTypes.insert(clusterType);
needRescan = true;
@@ -94,7 +116,7 @@ ScanSettings::setClusterTypes(const std::set<std::string>& clusterTypeNames)
}
// Delete no longer existing cluster types
for (auto clusterType : _clusterTypes)
for (ClusterType::pointer& clusterType : _clusterTypes)
{
if (std::none_of(clusterTypeNames.begin(), clusterTypeNames.end(),
[clusterType](const std::string& name) { return name == clusterType->getName(); }))
@@ -108,6 +130,5 @@ ScanSettings::setClusterTypes(const std::set<std::string>& clusterTypeNames)
_scanVersion += 1;
}
} // namespace Database
+5 -2
View File
@@ -27,6 +27,7 @@
namespace Database {
class ClusterType;
class Session;
class ScanSettings : public Wt::Dbo::Dbo<ScanSettings>
{
@@ -40,7 +41,9 @@ class ScanSettings : public Wt::Dbo::Dbo<ScanSettings>
Monthly
};
static pointer get(Wt::Dbo::Session& session);
static void init(Session& session);
static pointer get(Session& session);
// Getters
std::size_t getScanVersion() const { return _scanVersion; }
@@ -54,7 +57,7 @@ class ScanSettings : public Wt::Dbo::Dbo<ScanSettings>
void setMediaDirectory(boost::filesystem::path p);
void setUpdateStartTime(Wt::WTime t) { _startTime = t; }
void setUpdatePeriod(UpdatePeriod p) { _updatePeriod = p; }
void setClusterTypes(const std::set<std::string>& clusterTypeNames);
void setClusterTypes(Session& session, const std::set<std::string>& clusterTypeNames);
void setAudioFileExtensions(std::set<boost::filesystem::path> fileExtensions);
template<class Action>
@@ -17,10 +17,7 @@
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include "DatabaseHandler.hpp"
#include <Wt/Dbo/FixedSqlConnectionPool.h>
#include <Wt/Dbo/backend/Sqlite3.h>
#include "Session.hpp"
#include <Wt/Auth/Dbo/AuthInfo.h>
#include <Wt/Auth/Dbo/UserDatabase.h>
@@ -43,6 +40,7 @@
#include "TrackArtistLink.hpp"
#include "TrackList.hpp"
#include "TrackFeatures.hpp"
#include "User.hpp"
namespace Database {
@@ -60,15 +58,24 @@ class VersionInfo
public:
using pointer = Wt::Dbo::ptr<VersionInfo>;
static VersionInfo::pointer get(Wt::Dbo::Session& session)
static VersionInfo::pointer getOrCreate(Session& session)
{
pointer versionInfo {session.find<VersionInfo>()};
session.checkUniqueLocked();
pointer versionInfo {session.getDboSession().find<VersionInfo>()};
if (!versionInfo)
versionInfo = session.add(std::make_unique<VersionInfo>());
return session.getDboSession().add(std::make_unique<VersionInfo>());
return versionInfo;
}
static VersionInfo::pointer get(Session& session)
{
session.checkSharedLocked();
return session.getDboSession().find<VersionInfo>();
}
Version getVersion() const { return _version; }
void setVersion(Version version) { _version = static_cast<int>(version); }
@@ -82,18 +89,18 @@ class VersionInfo
int _version {LMS_DATABASE_VERSION};
};
static
void
doDatabaseMigrationIfNeeded(Wt::Dbo::Session& session)
Session::doDatabaseMigrationIfNeeded()
{
Wt::Dbo::Transaction transaction {session};
auto uniqueTransaction {createUniqueTransaction()};
static const std::string outdatedMsg {"Outdated database, please rebuild it (delete the .db file and restart)"};
Version version;
try
{
version = VersionInfo::get(session)->getVersion();
version = VersionInfo::getOrCreate(*this)->getVersion();
LMS_LOG(DB, INFO) << "Database version = " << version;
if (version == LMS_DATABASE_VERSION)
return;
}
@@ -109,30 +116,30 @@ doDatabaseMigrationIfNeeded(Wt::Dbo::Session& session)
LMS_LOG(DB, INFO) << "Migrating database from version 3...";
session.execute(R"(CREATE TABLE IF NOT EXISTS "user_artist_starred" (
_session.execute(R"(CREATE TABLE IF NOT EXISTS "user_artist_starred" (
"user_id" bigint,
"artist_id" bigint,
primary key ("user_id", "artist_id"),
constraint "fk_user_artist_starred_key1" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred,
constraint "fk_user_artist_starred_key2" foreign key ("artist_id") references "artist" ("id") deferrable initially deferred);)");
session.execute(R"(CREATE INDEX "user_artist_starred_user" on "user_artist_starred" ("user_id");)");
session.execute(R"(CREATE INDEX "user_artist_starred_artist" on "user_artist_starred" ("artist_id");)");
session.execute(R"(CREATE TABLE IF NOT EXISTS "user_release_starred" (
_session.execute(R"(CREATE INDEX "user_artist_starred_user" on "user_artist_starred" ("user_id");)");
_session.execute(R"(CREATE INDEX "user_artist_starred_artist" on "user_artist_starred" ("artist_id");)");
_session.execute(R"(CREATE TABLE IF NOT EXISTS "user_release_starred" (
"user_id" bigint,
"release_id" bigint,
primary key ("user_id", "release_id"),
constraint "fk_user_release_starred_key1" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred,
constraint "fk_user_release_starred_key2" foreign key ("release_id") references "release" ("id") on delete cascade deferrable initially deferred);)");
session.execute(R"(CREATE INDEX "user_release_starred_user" on "user_release_starred" ("user_id");)");
session.execute(R"(CREATE INDEX "user_release_starred_release" on "user_release_starred" ("release_id");)");
session.execute(R"(CREATE TABLE IF NOT EXISTS "user_track_starred" (
_session.execute(R"(CREATE INDEX "user_release_starred_user" on "user_release_starred" ("user_id");)");
_session.execute(R"(CREATE INDEX "user_release_starred_release" on "user_release_starred" ("release_id");)");
_session.execute(R"(CREATE TABLE IF NOT EXISTS "user_track_starred" (
"user_id" bigint,
"track_id" bigint,
primary key ("user_id", "track_id"),
constraint "fk_user_track_starred_key1" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred,
constraint "fk_user_track_starred_key2" foreign key ("track_id") references "track" ("id") on delete cascade deferrable initially deferred);)");
session.execute(R"(CREATE INDEX "user_track_starred_user" on "user_track_starred" ("user_id");)");
session.execute(R"(CREATE INDEX "user_track_starred_track" on "user_track_starred" ("track_id");)");
_session.execute(R"(CREATE INDEX "user_track_starred_user" on "user_track_starred" ("user_id");)");
_session.execute(R"(CREATE INDEX "user_track_starred_track" on "user_track_starred" ("track_id");)");
break;
default:
@@ -140,12 +147,12 @@ doDatabaseMigrationIfNeeded(Wt::Dbo::Session& session)
throw LmsException {outdatedMsg};
}
VersionInfo::get(session).modify()->setVersion(LMS_DATABASE_VERSION);
VersionInfo::get(*this).modify()->setVersion(LMS_DATABASE_VERSION);
}
void
Handler::configureAuth(void)
Session::configureAuth(void)
{
authService.setEmailVerificationEnabled(false);
authService.setAuthTokensEnabled(true, "lmsauth");
@@ -176,19 +183,20 @@ Handler::configureAuth(void)
}
const Wt::Auth::AuthService&
Handler::getAuthService()
Session::getAuthService()
{
return authService;
}
const Wt::Auth::PasswordService&
Handler::getPasswordService()
Session::getPasswordService()
{
return passwordService;
}
Handler::Handler(Wt::Dbo::SqlConnectionPool& connectionPool)
Session::Session(std::shared_timed_mutex& mutex, Wt::Dbo::SqlConnectionPool& connectionPool)
: _mutex {mutex}
{
_session.setConnectionPool(connectionPool);
@@ -212,9 +220,76 @@ Handler::Handler(Wt::Dbo::SqlConnectionPool& connectionPool)
_session.mapClass<AuthInfo::AuthTokenType>("auth_token");
_session.mapClass<User>("user");
try {
Wt::Dbo::Transaction transaction {_session};
_users = std::make_unique<UserDatabase>(_session);
}
// TODO make this per database
static thread_local bool hasSharedLock {false};
static thread_local bool hasUniqueLock {false};
UniqueTransaction::UniqueTransaction(std::shared_timed_mutex& mutex, Wt::Dbo::Session& session)
: _lock {mutex},
_transaction {session}
{
assert(!hasSharedLock);
assert(!hasUniqueLock);
hasUniqueLock = true;
LMS_LOG(DB, DEBUG) << "UniqueTransaction ACQUIRED";
}
UniqueTransaction::~UniqueTransaction()
{
assert(hasUniqueLock);
hasUniqueLock = false;
LMS_LOG(DB, DEBUG) << "UniqueTransaction RELEASED";
}
SharedTransaction::SharedTransaction(std::shared_timed_mutex& mutex, Wt::Dbo::Session& session)
: _lock {mutex},
_transaction {session}
{
assert(!hasSharedLock);
assert(!hasUniqueLock);
hasSharedLock = true;
LMS_LOG(DB, DEBUG) << "SharedTransaction ACQUIRED";
}
SharedTransaction::~SharedTransaction()
{
assert(hasSharedLock);
hasSharedLock = false;
LMS_LOG(DB, DEBUG) << "SharedTransaction RELEASED";
}
void
Session::checkUniqueLocked()
{
assert(hasUniqueLock);
}
void
Session::checkSharedLocked()
{
assert(hasUniqueLock || hasSharedLock);
}
std::unique_ptr<UniqueTransaction>
Session::createUniqueTransaction()
{
return std::unique_ptr<UniqueTransaction>(new UniqueTransaction{_mutex, _session});
}
std::unique_ptr<SharedTransaction>
Session::createSharedTransaction()
{
return std::unique_ptr<SharedTransaction>(new SharedTransaction{_mutex, _session});
}
void
Session::prepareTables()
{
// Creation case
try {
_session.createTables();
LMS_LOG(DB, INFO) << "Tables created";
@@ -224,12 +299,11 @@ Handler::Handler(Wt::Dbo::SqlConnectionPool& connectionPool)
LMS_LOG(DB, ERROR) << "Cannot create tables: " << e.what();
}
doDatabaseMigrationIfNeeded(_session);
doDatabaseMigrationIfNeeded();
// Indexes
{
Wt::Dbo::Transaction transaction {_session};
// Indexes
auto uniqueTransaction {createUniqueTransaction()};
_session.execute("CREATE INDEX IF NOT EXISTS artist_name_idx ON artist(name)");
_session.execute("CREATE INDEX IF NOT EXISTS artist_sort_name_nocase_idx ON artist(sort_name COLLATE NOCASE)");
_session.execute("CREATE INDEX IF NOT EXISTS artist_mbid_idx ON artist(mbid)");
@@ -255,29 +329,101 @@ Handler::Handler(Wt::Dbo::SqlConnectionPool& connectionPool)
_session.execute("CREATE INDEX IF NOT EXISTS track_artist_link_type_idx ON track_artist_link(type)");
}
_users = new UserDatabase(_session);
}
// Initial settings tables
{
auto uniqueTransaction {createUniqueTransaction()};
Handler::~Handler()
{
delete _users;
ScanSettings::init(*this);
SimilaritySettings::init(*this);
}
}
void
Handler::optimize()
Session::optimize()
{
Wt::Dbo::Transaction transaction {_session};
auto uniqueTransaction {createUniqueTransaction()};
_session.execute("ANALYZE");
}
std::string
Session::getUserLoginName(Wt::Dbo::ptr<User> user)
{
const Wt::Auth::User authUser {_users->findWithId(std::to_string(user.id()))};
if (!authUser.isValid())
throw LmsException {"Invalid user state"};
return authUser.identity(Wt::Auth::Identity::LoginName).toUTF8();
}
bool
Session::checkUserPassword(const std::string& loginName, const std::string& password)
{
auto transaction {createUniqueTransaction()};
auto authUser {_users->findWithIdentity(Wt::Auth::Identity::LoginName, loginName)};
if (!authUser.isValid())
return false; // TODO const time?
return passwordService.verifyPassword(authUser, password) == Wt::Auth::PasswordResult::PasswordValid;
}
void
Session::updateUserPassword(Wt::Dbo::ptr<User> user, const std::string& password)
{
const Wt::Auth::User authUser {_users->findWithId(std::to_string(user.id()))};
if (!authUser.isValid())
throw LmsException {"Bad user state"};
passwordService.updatePassword(authUser, password);
}
Wt::WDateTime
Session::getUserLastLoginAttempt(Wt::Dbo::ptr<User> user)
{
const Wt::Auth::User authUser {_users->findWithId(std::to_string(user.id()))};
if (!authUser.isValid())
throw LmsException {"Bad user state"};
return authUser.lastLoginAttempt();
}
void
Session::removeUser(Database::User::pointer user)
{
checkUniqueLocked();
auto authUser = _users->findWithId(std::to_string(user.id()));
_users->deleteUser(authUser);
user.remove();
}
Wt::Auth::AbstractUserDatabase&
Handler::getUserDatabase()
Session::getUserDatabase()
{
return *_users;
}
User::pointer
Handler::getCurrentUser()
Session::createUser(const std::string& loginName, const std::string& password)
{
Wt::Auth::User authUser {_users->registerNew()};
if (!authUser.isValid())
{
LMS_LOG(DB, ERROR) << "Invalid authUser";
return {};
}
User::pointer user {User::create(*this)};
Wt::Dbo::ptr<AuthInfo> authInfo = _users->find(authUser);
authInfo.modify()->setUser(user);
authUser.setIdentity(Wt::Auth::Identity::LoginName, loginName);
passwordService.updatePassword(authUser, password);
return user;
}
User::pointer
Session::getLoggedUser()
{
if (_login.loggedIn())
return getUser(_login.user());
@@ -286,10 +432,10 @@ Handler::getCurrentUser()
}
User::pointer
Handler::getUser(const Wt::Auth::User& authUser)
Session::getUser(const Wt::Auth::User& authUser)
{
if (!authUser.isValid()) {
LMS_LOG(DB, ERROR) << "Handler::getUser: invalid authUser";
LMS_LOG(DB, ERROR) << "Session::getUser: invalid authUser";
return User::pointer();
}
@@ -299,7 +445,7 @@ Handler::getUser(const Wt::Auth::User& authUser)
}
User::pointer
Handler::getUser(const std::string& loginName)
Session::getUser(const std::string& loginName)
{
auto authUser {getUserDatabase().findWithIdentity(Wt::Auth::Identity::LoginName, loginName)};
if (!authUser.isValid())
@@ -308,36 +454,4 @@ Handler::getUser(const std::string& loginName)
return getUser(authUser);
}
User::pointer
Handler::createUser(const Wt::Auth::User& authUser)
{
if (!authUser.isValid())
{
LMS_LOG(DB, ERROR) << "Handler::getUser: invalid authUser";
return User::pointer();
}
User::pointer user = _session.add(std::make_unique<User>());
Wt::Dbo::ptr<AuthInfo> authInfo = _users->find(authUser);
authInfo.modify()->setUser(user);
return user;
}
std::unique_ptr<Wt::Dbo::SqlConnectionPool>
Handler::createConnectionPool(boost::filesystem::path p)
{
LMS_LOG(DB, INFO) << "Creating connection pool on file " << p.string();
auto connection = std::make_unique<Wt::Dbo::backend::Sqlite3>(p.string());
connection->executeSql("pragma journal_mode=WAL");
// connection->setProperty("show-queries", "true");
auto pool = std::make_unique<Wt::Dbo::FixedSqlConnectionPool>(std::move(connection), 1);
pool->setTimeout(std::chrono::seconds(10));
return pool;
}
} // namespace Database
+123
View File
@@ -0,0 +1,123 @@
/*
* Copyright (C) 2013 Emeric Poupon
*
* This file is part of LMS.
*
* LMS 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.
*
* LMS 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 LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <shared_mutex>
#include <mutex>
#include <boost/filesystem.hpp>
#include <memory>
#include <Wt/Dbo/Dbo.h>
#include <Wt/Dbo/SqlConnectionPool.h>
#include <Wt/Auth/Dbo/AuthInfo.h>
#include <Wt/Auth/Dbo/UserDatabase.h>
#include <Wt/Auth/Login.h>
#include <Wt/Auth/PasswordService.h>
#include "User.hpp"
namespace Database {
using AuthInfo = Wt::Auth::Dbo::AuthInfo<User>;
using UserDatabase = Wt::Auth::Dbo::UserDatabase<AuthInfo>;
class UniqueTransaction
{
public:
~UniqueTransaction();
private:
friend class Session;
UniqueTransaction(std::shared_timed_mutex& mutex, Wt::Dbo::Session& session);
std::unique_lock<std::shared_timed_mutex> _lock;
Wt::Dbo::Transaction _transaction;
};
class SharedTransaction
{
public:
~SharedTransaction();
private:
friend class Session;
SharedTransaction(std::shared_timed_mutex& mutex, Wt::Dbo::Session& session);
std::shared_lock<std::shared_timed_mutex> _lock;
Wt::Dbo::Transaction _transaction;
};
class Session
{
public:
Session(const Session&) = delete;
Session(Session&&) = delete;
Session& operator=(const Session&) = delete;
Session& operator=(Session&&) = delete;
std::unique_ptr<UniqueTransaction> createUniqueTransaction();
std::unique_ptr<SharedTransaction> createSharedTransaction();
void checkUniqueLocked();
void checkSharedLocked();
void optimize();
// User management
Wt::Dbo::ptr<User> getLoggedUser(); // get the current user, may return empty
Wt::Dbo::ptr<User> getUser(const std::string& loginName);
std::string getUserLoginName(Wt::Dbo::ptr<User> user);
Wt::Dbo::ptr<User> createUser(const std::string& loginName, const std::string& password);
void removeUser(Wt::Dbo::ptr<User> user);
bool checkUserPassword(const std::string& loginName, const std::string& password);
void updateUserPassword(Wt::Dbo::ptr<User> user, const std::string& password);
Wt::WDateTime getUserLastLoginAttempt(Wt::Dbo::ptr<User> user);
Wt::Auth::AbstractUserDatabase& getUserDatabase();
Wt::Auth::Login& getLogin() { return _login; } // TODO move
// Long living shared associated services
static void configureAuth();
static const Wt::Auth::AuthService& getAuthService();
static const Wt::Auth::PasswordService& getPasswordService();
Wt::Dbo::Session& getDboSession() { return _session; }
private:
friend class Database;
Session(std::shared_timed_mutex& mutex, Wt::Dbo::SqlConnectionPool& connectionPool);
void doDatabaseMigrationIfNeeded();
void prepareTables(); // need to run only once at startup
Wt::Dbo::ptr<User> getUser(const Wt::Auth::User& authUser);
std::shared_timed_mutex& _mutex;
Wt::Dbo::Session _session;
std::unique_ptr<UserDatabase> _users;
Wt::Auth::Login _login;
};
} // namespace Database
+27 -13
View File
@@ -22,6 +22,7 @@
#include "utils/Logger.hpp"
#include "utils/Utils.hpp"
#include "Session.hpp"
#include "TrackFeatures.hpp"
namespace Database {
@@ -33,7 +34,7 @@ struct TrackFeatureInfo
double weight;
};
static std::vector<TrackFeatureInfo> defaultFeatures =
static const std::vector<TrackFeatureInfo> defaultFeatures =
{
{ "lowlevel.spectral_contrast_coeffs.median", 6, 1. },
{ "lowlevel.erbbands.median", 40, 1. },
@@ -53,24 +54,37 @@ _settings(settings)
}
SimilaritySettingsFeature::pointer
SimilaritySettingsFeature::create(Wt::Dbo::Session& session, Wt::Dbo::ptr<SimilaritySettings> settings, const std::string& name, std::size_t nbDimensions, double weight)
SimilaritySettingsFeature::create(Session& session, Wt::Dbo::ptr<SimilaritySettings> settings, const std::string& name, std::size_t nbDimensions, double weight)
{
return session.add(std::make_unique<SimilaritySettingsFeature>(settings, name, nbDimensions, weight));
session.checkUniqueLocked();
SimilaritySettingsFeature::pointer res {session.getDboSession().add(std::make_unique<SimilaritySettingsFeature>(settings, name, nbDimensions, weight))};
session.getDboSession().flush();
return res;
}
SimilaritySettings::pointer
SimilaritySettings::get(Wt::Dbo::Session& session)
void
SimilaritySettings::init(Session& session)
{
pointer settings = session.find<SimilaritySettings>();
if (!settings)
{
settings = session.add(std::make_unique<SimilaritySettings>());
session.checkUniqueLocked();
for (const auto& feature : defaultFeatures)
SimilaritySettingsFeature::create(session, settings, feature.name, feature.nbDimensions, feature.weight);
}
pointer settings {session.getDboSession().find<SimilaritySettings>()};
if (settings)
return;
return settings;
settings = session.getDboSession().add(std::make_unique<SimilaritySettings>());
for (const auto& feature : defaultFeatures)
SimilaritySettingsFeature::create(session, settings, feature.name, feature.nbDimensions, feature.weight);
}
SimilaritySettings::pointer
SimilaritySettings::get(Session& session)
{
session.checkSharedLocked();
return session.getDboSession().find<SimilaritySettings>();
}
std::vector<Wt::Dbo::ptr<SimilaritySettingsFeature>>
+4 -2
View File
@@ -23,6 +23,7 @@
namespace Database {
class Session;
class SimilaritySettings;
class SimilaritySettingsFeature : public Wt::Dbo::Dbo<SimilaritySettingsFeature>
@@ -33,7 +34,7 @@ class SimilaritySettingsFeature : public Wt::Dbo::Dbo<SimilaritySettingsFeature
SimilaritySettingsFeature() = default;
SimilaritySettingsFeature(Wt::Dbo::ptr<SimilaritySettings> settings, const std::string& name, std::size_t nbDimensions, double weight);
static pointer create(Wt::Dbo::Session& session, Wt::Dbo::ptr<SimilaritySettings> settings, const std::string& name, std::size_t nbDimensions, double weight = 1);
static pointer create(Session& session, Wt::Dbo::ptr<SimilaritySettings> settings, const std::string& name, std::size_t nbDimensions, double weight = 1);
const std::string& getName() const { return _name; } ;
std::size_t getNbDimensions() const { return static_cast<std::size_t>(_nbDimensions); }
@@ -70,7 +71,8 @@ class SimilaritySettings : public Wt::Dbo::Dbo<SimilaritySettings>
using pointer = Wt::Dbo::ptr<SimilaritySettings>;
// Utils
static pointer get(Wt::Dbo::Session& session);
static void init(Session& session);
static pointer get(Session& session);
// Accessors Read
std::size_t getVersion() const { return _settingsVersion; }
+70 -38
View File
@@ -27,6 +27,7 @@
#include "Cluster.hpp"
#include "Release.hpp"
#include "TrackFeatures.hpp"
#include "Session.hpp"
#include "SqlQuery.hpp"
namespace Database {
@@ -38,18 +39,22 @@ _filePath( p.string() )
}
std::vector<Track::pointer>
Track::getAll(Wt::Dbo::Session& session, boost::optional<std::size_t> limit)
Track::getAll(Session& session, boost::optional<std::size_t> limit)
{
Wt::Dbo::collection<Track::pointer> res {session.find<Track>()
session.checkSharedLocked();
Wt::Dbo::collection<Track::pointer> res {session.getDboSession().find<Track>()
.limit(limit ? static_cast<int>(*limit) : -1)};
return std::vector<Track::pointer>(std::cbegin(res), std::cend(res));
}
std::vector<Track::pointer>
Track::getAllRandom(Wt::Dbo::Session& session, boost::optional<std::size_t> limit)
Track::getAllRandom(Session& session, boost::optional<std::size_t> limit)
{
Wt::Dbo::collection<Track::pointer> res {session.find<Track>()
session.checkSharedLocked();
Wt::Dbo::collection<Track::pointer> res {session.getDboSession().find<Track>()
.limit(limit ? static_cast<int>(*limit) : -1)
.orderBy("RANDOM()")};
@@ -57,67 +62,88 @@ Track::getAllRandom(Wt::Dbo::Session& session, boost::optional<std::size_t> limi
}
std::vector<IdType>
Track::getAllIds(Wt::Dbo::Session& session)
Track::getAllIds(Session& session)
{
Wt::Dbo::Transaction transaction(session);
Wt::Dbo::collection<IdType> res = session.query<IdType>("SELECT id from track");
session.checkSharedLocked();
Wt::Dbo::collection<IdType> res = session.getDboSession().query<IdType>("SELECT id FROM track");
return std::vector<IdType>(res.begin(), res.end());
}
Track::pointer
Track::getByPath(Wt::Dbo::Session& session, const boost::filesystem::path& p)
Track::getByPath(Session& session, const boost::filesystem::path& p)
{
return session.find<Track>().where("file_path = ?").bind(p.string());
session.checkSharedLocked();
return session.getDboSession().find<Track>().where("file_path = ?").bind(p.string());
}
Track::pointer
Track::getById(Wt::Dbo::Session& session, IdType id)
Track::getById(Session& session, IdType id)
{
return session.find<Track>().where("id = ?").bind(id);
session.checkSharedLocked();
return session.getDboSession().find<Track>()
.where("id = ?").bind(id);
}
Track::pointer
Track::getByMBID(Wt::Dbo::Session& session, const std::string& mbid)
Track::getByMBID(Session& session, const std::string& mbid)
{
return session.find<Track>().where("mbid = ?").bind(mbid);
session.checkSharedLocked();
return session.getDboSession().find<Track>()
.where("mbid = ?").bind(mbid);
}
Track::pointer
Track::create(Wt::Dbo::Session& session, const boost::filesystem::path& p)
Track::create(Session& session, const boost::filesystem::path& p)
{
return session.add(std::make_unique<Track>(p));
session.checkUniqueLocked();
Track::pointer res {session.getDboSession().add(std::make_unique<Track>(p))};
session.getDboSession().flush();
return res;
}
std::vector<boost::filesystem::path>
Track::getAllPaths(Wt::Dbo::Session& session)
Track::getAllPaths(Session& session)
{
Wt::Dbo::Transaction transaction(session);
Wt::Dbo::collection<std::string> res = session.query<std::string>("SELECT file_path from track");
session.checkSharedLocked();
Wt::Dbo::collection<std::string> res = session.getDboSession().query<std::string>("SELECT file_path FROM track");
return std::vector<boost::filesystem::path>(res.begin(), res.end());
}
std::vector<Track::pointer>
Track::getMBIDDuplicates(Wt::Dbo::Session& session)
Track::getMBIDDuplicates(Session& session)
{
Wt::Dbo::collection<pointer> res = session.query<pointer>( "SELECT track FROM track WHERE mbid in (SELECT mbid FROM track WHERE mbid <> '' GROUP BY mbid HAVING COUNT (*) > 1)").orderBy("track.release_id,track.disc_number,track.track_number,track.mbid");
session.checkSharedLocked();
Wt::Dbo::collection<pointer> res = session.getDboSession().query<pointer>( "SELECT track FROM track WHERE mbid in (SELECT mbid FROM track WHERE mbid <> '' GROUP BY mbid HAVING COUNT (*) > 1)").orderBy("track.release_id,track.disc_number,track.track_number,track.mbid");
return std::vector<pointer>(res.begin(), res.end());
}
std::vector<Track::pointer>
Track::getLastAdded(Wt::Dbo::Session& session, Wt::WDateTime after, int limit)
Track::getLastAdded(Session& session, const Wt::WDateTime& after, boost::optional<std::size_t> limit)
{
Wt::Dbo::collection<Track::pointer> res = session.find<Track>()
session.checkSharedLocked();
Wt::Dbo::collection<Track::pointer> res = session.getDboSession().find<Track>()
.where("file_added > ?").bind(after)
.orderBy("file_added DESC")
.limit(limit);
.limit(limit ? static_cast<int>(*limit) : -1);
return std::vector<pointer>(res.begin(), res.end());
}
std::vector<Track::pointer>
Track::getAllWithMBIDAndMissingFeatures(Wt::Dbo::Session& session)
Track::getAllWithMBIDAndMissingFeatures(Session& session)
{
Wt::Dbo::collection<pointer> res = session.query<pointer>
session.checkSharedLocked();
Wt::Dbo::collection<pointer> res = session.getDboSession().query<pointer>
("SELECT t FROM track t")
.where("LENGTH(t.mbid) > 0")
.where("NOT EXISTS (SELECT * FROM track_features t_f WHERE t_f.track_id = t.id)");
@@ -125,14 +151,14 @@ Track::getAllWithMBIDAndMissingFeatures(Wt::Dbo::Session& session)
}
std::vector<IdType>
Track::getAllIdsWithFeatures(Wt::Dbo::Session& session, boost::optional<std::size_t> limit)
Track::getAllIdsWithFeatures(Session& session, boost::optional<std::size_t> limit)
{
int size {limit ? static_cast<int>(*limit) : -1};
session.checkSharedLocked();
Wt::Dbo::collection<IdType> res = session.query<IdType>
Wt::Dbo::collection<IdType> res = session.getDboSession().query<IdType>
("SELECT t.id FROM track t")
.where("EXISTS (SELECT * from track_features t_f WHERE t_f.track_id = t.id)")
.limit(size);
.limit(limit ? static_cast<int>(*limit) : -1);
return std::vector<IdType>(res.begin(), res.end());
}
@@ -153,10 +179,12 @@ Track::hasTrackFeatures() const
static
Wt::Dbo::Query< Track::pointer >
getQuery(Wt::Dbo::Session& session,
getQuery(Session& session,
const std::set<IdType>& clusterIds,
const std::vector<std::string> keywords)
const std::vector<std::string>& keywords)
{
session.checkSharedLocked();
WhereClause where;
std::ostringstream oss;
@@ -184,7 +212,7 @@ getQuery(Wt::Dbo::Session& session,
oss << " ORDER BY t.name COLLATE NOCASE";
Wt::Dbo::Query<Track::pointer> query = session.query<Track::pointer>( oss.str() );
Wt::Dbo::Query<Track::pointer> query = session.getDboSession().query<Track::pointer>( oss.str() );
for (const std::string& bindArg : where.getBindArgs())
query.bind(bindArg);
@@ -193,13 +221,15 @@ getQuery(Wt::Dbo::Session& session,
}
std::vector<Track::pointer>
Track::getByFilter(Wt::Dbo::Session& session,
Track::getByFilter(Session& session,
const std::set<IdType>& clusterIds,
const std::vector<std::string> keywords,
const std::vector<std::string>& keywords,
boost::optional<std::size_t> offset,
boost::optional<std::size_t> size,
bool& moreResults)
{
session.checkSharedLocked();
Wt::Dbo::collection<pointer> collection = getQuery(session, clusterIds, keywords)
.limit(size ? static_cast<int>(*size) + 1 : -1)
.offset(offset ? static_cast<int>(*offset) : -1);
@@ -218,16 +248,18 @@ Track::getByFilter(Wt::Dbo::Session& session,
}
std::vector<Track::pointer>
Track::getByFilter(Wt::Dbo::Session& session,
Track::getByFilter(Session& session,
const std::set<IdType>& clusters)
{
session.checkSharedLocked();
bool moreResults;
return getByFilter(session,
clusters,
std::vector<std::string> {},
boost::optional<std::size_t> {},
boost::optional<std::size_t> {},
{},
{},
{},
moreResults);
}
+16 -16
View File
@@ -53,29 +53,29 @@ class Track : public Wt::Dbo::Dbo<Track>
Track(const boost::filesystem::path& p);
// Find utility functions
static pointer getByPath(Wt::Dbo::Session& session, const boost::filesystem::path& p);
static pointer getById(Wt::Dbo::Session& session, IdType id);
static pointer getByMBID(Wt::Dbo::Session& session, const std::string& MBID);
static std::vector<pointer> getByFilter(Wt::Dbo::Session& session,
static pointer getByPath(Session& session, const boost::filesystem::path& p);
static pointer getById(Session& session, IdType id);
static pointer getByMBID(Session& session, const std::string& MBID);
static std::vector<pointer> getByFilter(Session& session,
const std::set<IdType>& clusters); // tracks that belong to these clusters
static std::vector<pointer> getByFilter(Wt::Dbo::Session& session,
const std::set<IdType>& clusters, // tracks that belong to these clusters
const std::vector<std::string> keywords, // name must match all of these keywords
static std::vector<pointer> getByFilter(Session& session,
const std::set<IdType>& clusters, // tracks that belong to these clusters
const std::vector<std::string>& keywords, // name must match all of these keywords
boost::optional<std::size_t> offset,
boost::optional<std::size_t> size,
bool& moreExpected);
static std::vector<pointer> getAll(Wt::Dbo::Session& session, boost::optional<std::size_t> limit = {});
static std::vector<pointer> getAllRandom(Wt::Dbo::Session& session, boost::optional<std::size_t> limit = {});
static std::vector<IdType> getAllIds(Wt::Dbo::Session& session); // nested transaction
static std::vector<boost::filesystem::path> getAllPaths(Wt::Dbo::Session& session); // nested transaction
static std::vector<pointer> getMBIDDuplicates(Wt::Dbo::Session& session);
static std::vector<pointer> getLastAdded(Wt::Dbo::Session& session, Wt::WDateTime after, int size = 1);
static std::vector<pointer> getAllWithMBIDAndMissingFeatures(Wt::Dbo::Session& session); // nested transaction
static std::vector<IdType> getAllIdsWithFeatures(Wt::Dbo::Session& session, boost::optional<std::size_t> limit = {}); // nested transaction
static std::vector<pointer> getAll(Session& session, boost::optional<std::size_t> limit = {});
static std::vector<pointer> getAllRandom(Session& session, boost::optional<std::size_t> limit = {});
static std::vector<IdType> getAllIds(Session& session); // nested transaction
static std::vector<boost::filesystem::path> getAllPaths(Session& session); // nested transaction
static std::vector<pointer> getMBIDDuplicates(Session& session);
static std::vector<pointer> getLastAdded(Session& session, const Wt::WDateTime& after, boost::optional<std::size_t> size = 1);
static std::vector<pointer> getAllWithMBIDAndMissingFeatures(Session& session);
static std::vector<IdType> getAllIdsWithFeatures(Session& session, boost::optional<std::size_t> limit = {});
// Create utility
static pointer create(Wt::Dbo::Session& session, const boost::filesystem::path& p);
static pointer create(Session& session, const boost::filesystem::path& p);
// Accessors
void setScanVersion(std::size_t version) { _scanVersion = version; }
+8 -2
View File
@@ -20,6 +20,7 @@
#include "TrackArtistLink.hpp"
#include "Artist.hpp"
#include "Session.hpp"
#include "Track.hpp"
namespace Database {
@@ -32,9 +33,14 @@ _artist {artist}
}
TrackArtistLink::pointer
TrackArtistLink::create(Wt::Dbo::Session& session, Wt::Dbo::ptr<Track> track, Wt::Dbo::ptr<Artist> artist,Type type)
TrackArtistLink::create(Session& session, Wt::Dbo::ptr<Track> track, Wt::Dbo::ptr<Artist> artist,Type type)
{
return session.add(std::make_unique<TrackArtistLink>(track, artist, type));
session.checkUniqueLocked();
TrackArtistLink::pointer res {session.getDboSession().add(std::make_unique<TrackArtistLink>(track, artist, type))};
session.getDboSession().flush();
return res;
}
}
+2 -1
View File
@@ -26,6 +26,7 @@
namespace Database {
class Artist;
class Session;
class Track;
class TrackArtistLink
@@ -51,7 +52,7 @@ class TrackArtistLink
TrackArtistLink() = default;
TrackArtistLink(Wt::Dbo::ptr<Track> track, Wt::Dbo::ptr<Artist> artist, Type type);
static pointer create(Wt::Dbo::Session& session, Wt::Dbo::ptr<Track> track, Wt::Dbo::ptr<Artist> artist,Type type);
static pointer create(Session& session, Wt::Dbo::ptr<Track> track, Wt::Dbo::ptr<Artist> artist,Type type);
Wt::Dbo::ptr<Track> getTrack() const { return _track; }
Wt::Dbo::ptr<Artist> getArtist() const { return _artist; }
+4 -2
View File
@@ -23,6 +23,7 @@
#include <boost/property_tree/json_parser.hpp>
#include "utils/Logger.hpp"
#include "Session.hpp"
#include "Track.hpp"
namespace Database {
@@ -34,9 +35,10 @@ _track(track)
}
TrackFeatures::pointer
TrackFeatures::create(Wt::Dbo::Session& session, Wt::Dbo::ptr<Track> track, const std::string& jsonEncodedFeatures)
TrackFeatures::create(Session& session, Wt::Dbo::ptr<Track> track, const std::string& jsonEncodedFeatures)
{
return session.add(std::make_unique<TrackFeatures>(track, jsonEncodedFeatures));
session.checkUniqueLocked();
return session.getDboSession().add(std::make_unique<TrackFeatures>(track, jsonEncodedFeatures));
}
std::vector<double>
+2 -1
View File
@@ -27,6 +27,7 @@
namespace Database {
class Session;
class Track;
class TrackFeatures : public Wt::Dbo::Dbo<TrackFeatures>
@@ -39,7 +40,7 @@ class TrackFeatures : public Wt::Dbo::Dbo<TrackFeatures>
TrackFeatures(Wt::Dbo::ptr<Track> track, const std::string& jsonEncodedFeatures);
// Create utility
static pointer create(Wt::Dbo::Session& session, Wt::Dbo::ptr<Track> track, const std::string& jsonEncodedFeatures);
static pointer create(Session& session, Wt::Dbo::ptr<Track> track, const std::string& jsonEncodedFeatures);
std::vector<double> getFeatures(const std::string& featureNode) const;
bool getFeatures(std::map<std::string /*featureNode*/, std::vector<double> /*values*/>& featureNodes) const;
+30 -46
View File
@@ -26,6 +26,7 @@
#include "Artist.hpp"
#include "Cluster.hpp"
#include "Release.hpp"
#include "Session.hpp"
#include "User.hpp"
#include "Track.hpp"
@@ -41,38 +42,35 @@ TrackList::TrackList(const std::string& name, Type type, bool isPublic, Wt::Dbo:
}
TrackList::pointer
TrackList::create(Wt::Dbo::Session& session, const std::string& name, Type type, bool isPublic, Wt::Dbo::ptr<User> user)
TrackList::create(Session& session, const std::string& name, Type type, bool isPublic, Wt::Dbo::ptr<User> user)
{
session.checkUniqueLocked();
assert(user);
auto res = session.add( std::make_unique<TrackList>(name, type, isPublic, user) );
session.flush();
auto res = session.getDboSession().add( std::make_unique<TrackList>(name, type, isPublic, user) );
session.getDboSession().flush();
return res;
}
TrackListEntry::pointer
TrackList::add(IdType trackId)
{
assert(session());
assert(self());
return TrackListEntry::create(*session(), Database::Track::getById(*session(), trackId), self());
}
TrackList::pointer
TrackList::get(Wt::Dbo::Session& session, const std::string& name, Type type, Wt::Dbo::ptr<User> user)
TrackList::get(Session& session, const std::string& name, Type type, Wt::Dbo::ptr<User> user)
{
return session.find<TrackList>()
session.checkSharedLocked();
assert(user);
return session.getDboSession().find<TrackList>()
.where("name = ?").bind(name)
.where("type = ?").bind(type)
.where("user_id = ?").bind(user.id());
}
std::vector<TrackList::pointer>
TrackList::getAll(Wt::Dbo::Session& session, Wt::Dbo::ptr<User> user)
TrackList::getAll(Session& session, Wt::Dbo::ptr<User> user)
{
Wt::Dbo::collection<TrackList::pointer> res = session.find<TrackList>()
session.checkSharedLocked();
Wt::Dbo::collection<TrackList::pointer> res = session.getDboSession().find<TrackList>()
.where("user_id = ?").bind(user.id())
.orderBy("name COLLATE NOCASE");
@@ -80,9 +78,11 @@ TrackList::getAll(Wt::Dbo::Session& session, Wt::Dbo::ptr<User> user)
}
std::vector<TrackList::pointer>
TrackList::getAll(Wt::Dbo::Session& session, Wt::Dbo::ptr<User> user, Type type)
TrackList::getAll(Session& session, Wt::Dbo::ptr<User> user, Type type)
{
Wt::Dbo::collection<TrackList::pointer> res = session.find<TrackList>()
session.checkSharedLocked();
Wt::Dbo::collection<TrackList::pointer> res = session.getDboSession().find<TrackList>()
.where("user_id = ?").bind(user.id())
.where("type = ?").bind(type)
.orderBy("name COLLATE NOCASE");
@@ -91,9 +91,11 @@ TrackList::getAll(Wt::Dbo::Session& session, Wt::Dbo::ptr<User> user, Type type)
}
TrackList::pointer
TrackList::getById(Wt::Dbo::Session& session, IdType id)
TrackList::getById(Session& session, IdType id)
{
return session.find<TrackList>().where("id = ?").bind(id);
session.checkSharedLocked();
return session.getDboSession().find<TrackList>().where("id = ?").bind(id);
}
@@ -200,23 +202,6 @@ TrackList::getDuration() const
return query.resultValue();
}
void
TrackList::shuffle()
{
assert(session());
auto entries = getEntries();
auto now = std::chrono::system_clock::now();
std::mt19937 randGenerator(std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count());
std::shuffle(entries.begin(), entries.end(), randGenerator);
clear();
for (auto entry : entries)
TrackListEntry::create(*session(), entry->getTrack(), self());
}
std::vector<Artist::pointer>
TrackList::getTopArtists(std::size_t limit) const
{
@@ -269,26 +254,25 @@ TrackListEntry::TrackListEntry(Wt::Dbo::ptr<Track> track, Wt::Dbo::ptr<TrackList
}
TrackListEntry::TrackListEntry()
{
}
TrackListEntry::pointer
TrackListEntry::create(Wt::Dbo::Session& session, Wt::Dbo::ptr<Track> track, Wt::Dbo::ptr<TrackList> tracklist)
TrackListEntry::create(Session& session, Wt::Dbo::ptr<Track> track, Wt::Dbo::ptr<TrackList> tracklist)
{
session.checkUniqueLocked();
assert(track);
assert(tracklist);
auto res = session.add( std::make_unique<TrackListEntry>( track, tracklist) );
session.flush();
auto res = session.getDboSession().add( std::make_unique<TrackListEntry>( track, tracklist) );
session.getDboSession().flush();
return res;
}
TrackListEntry::pointer
TrackListEntry::getById(Wt::Dbo::Session& session, IdType id)
TrackListEntry::getById(Session& session, IdType id)
{
return session.find<TrackListEntry>().where("id = ?").bind(id);
session.checkSharedLocked();
return session.getDboSession().find<TrackListEntry>().where("id = ?").bind(id);
}
} // namespace Database
+12 -13
View File
@@ -30,11 +30,12 @@
namespace Database {
class Artist;
class Cluster;
class Release;
class User;
class Session;
class Track;
class TrackListEntry;
class Cluster;
class User;
class TrackList : public Wt::Dbo::Dbo<TrackList>
{
@@ -56,13 +57,13 @@ class TrackList : public Wt::Dbo::Dbo<TrackList>
std::vector<Wt::Dbo::ptr<Track>> getTopTracks(std::size_t limit = 1) const;
// Search utility
static pointer get(Wt::Dbo::Session& session, const std::string& name, Type type, Wt::Dbo::ptr<User> user);
static pointer getById(Wt::Dbo::Session& session, IdType tracklistId);
static std::vector<pointer> getAll(Wt::Dbo::Session& session, Wt::Dbo::ptr<User> user);
static std::vector<pointer> getAll(Wt::Dbo::Session& session, Wt::Dbo::ptr<User> user, Type type);
static pointer get(Session& session, const std::string& name, Type type, Wt::Dbo::ptr<User> user);
static pointer getById(Session& session, IdType tracklistId);
static std::vector<pointer> getAll(Session& session, Wt::Dbo::ptr<User> user);
static std::vector<pointer> getAll(Session& session, Wt::Dbo::ptr<User> user, Type type);
// Create utility
static pointer create(Wt::Dbo::Session& session, const std::string& name, Type type, bool isPublic, Wt::Dbo::ptr<User> user);
static pointer create(Session& session, const std::string& name, Type type, bool isPublic, Wt::Dbo::ptr<User> user);
// Accessors
std::string getName() const { return _name; }
@@ -73,9 +74,7 @@ class TrackList : public Wt::Dbo::Dbo<TrackList>
// Modifiers
void setName(const std::string& name) { _name = name; }
void setIsPublic(bool isPublic) { _isPublic = isPublic; }
Wt::Dbo::ptr<TrackListEntry> add(IdType trackId);
void clear() { _entries.clear(); }
void shuffle();
void clear() { _entries.clear(); }
// Get tracks, ordered by position
std::size_t getCount() const;
@@ -120,13 +119,13 @@ class TrackListEntry : public Wt::Dbo::Dbo<TrackListEntry>
using pointer = Wt::Dbo::ptr<TrackListEntry>;
TrackListEntry();
TrackListEntry() = default;
TrackListEntry(Wt::Dbo::ptr<Track> track, Wt::Dbo::ptr<TrackList> tracklist);
static pointer getById(Wt::Dbo::Session& session, IdType id);
static pointer getById(Session& session, IdType id);
// Create utility
static pointer create(Wt::Dbo::Session& session, Wt::Dbo::ptr<Track> track, Wt::Dbo::ptr<TrackList> tracklist);
static pointer create(Session& session, Wt::Dbo::ptr<Track> track, Wt::Dbo::ptr<TrackList> tracklist);
// Accessors
Wt::Dbo::ptr<Track> getTrack() const { return _track; }
+1 -1
View File
@@ -19,7 +19,7 @@
#pragma once
#include <Wt/Dbo/Dbo.h>
#include <Wt/Dbo/ptr.h>
namespace Database {
using IdType = Wt::Dbo::dbo_default_traits::IdType;
+32 -29
View File
@@ -21,11 +21,15 @@
#include "Artist.hpp"
#include "Release.hpp"
#include "Session.hpp"
#include "Track.hpp"
#include "TrackList.hpp"
namespace Database {
static const std::string playedListName {"__played_tracks__"};
static const std::string queuedListName {"__queued_tracks__"};
const std::set<Bitrate>
User::audioTranscodeAllowedBitrates =
{
@@ -37,35 +41,48 @@ User::audioTranscodeAllowedBitrates =
};
User::User()
: _maxAudioTranscodeBitrate{static_cast<int>(*audioTranscodeAllowedBitrates.rbegin())}
: _maxAudioTranscodeBitrate {static_cast<int>(*audioTranscodeAllowedBitrates.rbegin())}
{
}
std::vector<User::pointer>
User::getAll(Wt::Dbo::Session& session)
User::getAll(Session& session)
{
Wt::Dbo::collection<pointer> res = session.find<User>();
session.checkSharedLocked();
Wt::Dbo::collection<pointer> res = session.getDboSession().find<User>();
return std::vector<pointer>(res.begin(), res.end());
}
User::pointer
User::getDemo(Wt::Dbo::Session& session)
User::getDemo(Session& session)
{
pointer res = session.find<User>().where("type = ?").bind(Type::DEMO);
session.checkSharedLocked();
pointer res = session.getDboSession().find<User>().where("type = ?").bind(Type::DEMO);
return res;
}
User::pointer
User::create(Wt::Dbo::Session& session)
User::create(Session& session)
{
return session.add(std::make_unique<User>());
session.checkUniqueLocked();
User::pointer user {session.getDboSession().add(std::make_unique<User>())};
TrackList::create(session, playedListName, TrackList::Type::Internal, false, user);
TrackList::create(session, queuedListName, TrackList::Type::Internal, false, user);
session.getDboSession().flush();
return user;
}
User::pointer
User::getById(Wt::Dbo::Session& session, IdType id)
User::getById(Session& session, IdType id)
{
return session.find<User>().where("id = ?").bind( id );
return session.getDboSession().find<User>().where("id = ?").bind( id );
}
void
@@ -95,35 +112,21 @@ User::getMaxAudioTranscodeBitrate(void) const
}
Wt::Dbo::ptr<TrackList>
User::getPlayedTrackList() const
User::getPlayedTrackList(Session& session) const
{
static const std::string listName = "__played_tracks__";
assert(self());
assert(IdIsValid(self()->id()));
assert(session());
session.checkSharedLocked();
auto res = TrackList::get(*session(), listName, TrackList::Type::Internal, self());
if (!res)
res = TrackList::create(*session(), listName, TrackList::Type::Internal, false, self());
return res;
return TrackList::get(session, playedListName, TrackList::Type::Internal, self());
}
Wt::Dbo::ptr<TrackList>
User::getQueuedTrackList() const
User::getQueuedTrackList(Session& session) const
{
static const std::string listName = "__queued_tracks__";
assert(self());
assert(IdIsValid(self()->id()));
assert(session());
session.checkSharedLocked();
auto res = TrackList::get(*session(), listName, TrackList::Type::Internal, self());
if (!res)
res = TrackList::create(*session(), listName, TrackList::Type::Internal, false, self());
return res;
return TrackList::get(session, queuedListName, TrackList::Type::Internal, self());
}
void
+8 -9
View File
@@ -22,17 +22,15 @@
#include <vector>
#include <Wt/Dbo/Dbo.h>
#include <Wt/Auth/Dbo/AuthInfo.h>
#include "Types.hpp"
namespace Database {
class User;
using AuthInfo = Wt::Auth::Dbo::AuthInfo<User>;
class Artist;
class Release;
class Session;
class TrackList;
class Track;
@@ -69,12 +67,13 @@ class User : public Wt::Dbo::Dbo<User>
User();
// utility
static pointer create(Wt::Dbo::Session& session);
static pointer create(Session& session);
// accessors
static pointer getById(Wt::Dbo::Session& session, IdType id);
static std::vector<pointer> getAll(Wt::Dbo::Session& session);
static pointer getDemo(Wt::Dbo::Session& session);
static pointer getById(Session& session, IdType id);
static pointer getByLoginName(const std::string& loginName);
static std::vector<pointer> getAll(Session& session);
static pointer getDemo(Session& session);
// write
void setType(Type type) { _type = type; }
@@ -97,8 +96,8 @@ class User : public Wt::Dbo::Dbo<User>
bool isRepeatAllSet() const { return _repeatAll; }
bool isRadioSet() const { return _radio; }
Wt::Dbo::ptr<TrackList> getQueuedTrackList() const;
Wt::Dbo::ptr<TrackList> getPlayedTrackList() const;
Wt::Dbo::ptr<TrackList> getPlayedTrackList(Session& session) const;
Wt::Dbo::ptr<TrackList> getQueuedTrackList(Session& session) const;
void starArtist(Wt::Dbo::ptr<Artist> artist);
void unstarArtist(Wt::Dbo::ptr<Artist> artist);