Commit c89a3e8a authored by Hugo Beauzée-Luyssen's avatar Hugo Beauzée-Luyssen

Rework object creation & insertion to cache to avoid invalid states.

Objects were always created and added to cache, regardless of their
status in DB
parent 65095748
#ifndef CACHE_H #ifndef CACHE_H
#define CACHE_H #define CACHE_H
#include <cassert>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <unordered_map> #include <unordered_map>
...@@ -86,22 +87,6 @@ class Cache ...@@ -86,22 +87,6 @@ class Cache
return inst; return inst;
} }
/*
* Create a new instance of the cache class.
* This doesn't check for the existence of a similar entity already cached.
*/
template <typename... Args>
static std::shared_ptr<IMPL> create( Args&&... args )
{
auto res = std::make_shared<IMPL>( std::forward<Args>( args )... );
typename CACHEPOLICY::KeyType cacheKey = CACHEPOLICY::key( res );
std::lock_guard<std::mutex> lock( Mutex );
Store[cacheKey] = res;
return res;
}
static bool destroy( sqlite3* dbConnection, const typename CACHEPOLICY::KeyType& key ) static bool destroy( sqlite3* dbConnection, const typename CACHEPOLICY::KeyType& key )
{ {
std::lock_guard<std::mutex> lock( Mutex ); std::lock_guard<std::mutex> lock( Mutex );
...@@ -125,6 +110,26 @@ class Cache ...@@ -125,6 +110,26 @@ class Cache
Store.clear(); Store.clear();
} }
protected:
/*
* Create a new instance of the cache class.
*/
template <typename... Args>
static unsigned int insert( sqlite3* dbConnection, std::shared_ptr<IMPL> self, const char* req, const Args&... args )
{
unsigned int pKey = SqliteTools::insert( dbConnection, req, args... );
if ( pKey == 0 )
return 0;
auto cacheKey = CACHEPOLICY::key( self );
std::lock_guard<std::mutex> lock( Mutex );
// We expect the cache column to be PRIMARY KEY / UNIQUE, so an insertion with
// a duplicated key should have been rejected by sqlite. This indicates an invalid state
assert( Store.find( cacheKey ) == Store.end() );
Store[cacheKey] = self;
return pKey;
}
private: private:
static std::unordered_map<typename CACHEPOLICY::KeyType, std::shared_ptr<IMPL> > Store; static std::unordered_map<typename CACHEPOLICY::KeyType, std::shared_ptr<IMPL> > Store;
static std::mutex Mutex; static std::mutex Mutex;
......
...@@ -36,18 +36,18 @@ File::File( const std::string& mrl ) ...@@ -36,18 +36,18 @@ File::File( const std::string& mrl )
{ {
} }
bool File::insert( sqlite3* dbConnection ) FilePtr File::create( sqlite3* dbConnection, const std::string& mrl )
{ {
assert( m_dbConnection == NULL ); auto self = std::make_shared<File>( mrl );
assert( m_id == 0 );
static const std::string req = "INSERT INTO " + policy::FileTable::Name + static const std::string req = "INSERT INTO " + policy::FileTable::Name +
" VALUES(NULL, ?, ?, ?, ?, ?, ?)"; " VALUES(NULL, ?, ?, ?, ?, ?, ?)";
if ( SqliteTools::executeRequest( dbConnection, req.c_str(), (int)m_type, m_duration, auto pKey = _Cache::insert( dbConnection, self, req.c_str(), (int)self->m_type, self->m_duration,
m_albumTrackId, m_playCount, m_showEpisodeId, m_mrl ) == false ) self->m_albumTrackId, self->m_playCount, self->m_showEpisodeId, self->m_mrl );
return false; if ( pKey == 0 )
m_id = sqlite3_last_insert_rowid( dbConnection ); return nullptr;
m_dbConnection = dbConnection; self->m_id = pKey;
return true; self->m_dbConnection = dbConnection;
return self;
} }
std::shared_ptr<IAlbumTrack> File::albumTrack() std::shared_ptr<IAlbumTrack> File::albumTrack()
......
...@@ -30,6 +30,8 @@ struct FileCache ...@@ -30,6 +30,8 @@ struct FileCache
class File : public IFile, public Cache<File, IFile, policy::FileTable, policy::FileCache> class File : public IFile, public Cache<File, IFile, policy::FileTable, policy::FileCache>
{ {
private:
typedef Cache<File, IFile, policy::FileTable, policy::FileCache> _Cache;
public: public:
enum Type enum Type
{ {
...@@ -47,7 +49,7 @@ class File : public IFile, public Cache<File, IFile, policy::FileTable, policy:: ...@@ -47,7 +49,7 @@ class File : public IFile, public Cache<File, IFile, policy::FileTable, policy::
File(sqlite3* dbConnection , sqlite3_stmt* stmt); File(sqlite3* dbConnection , sqlite3_stmt* stmt);
File( const std::string& mrl ); File( const std::string& mrl );
bool insert(sqlite3* dbConnection); static FilePtr create(sqlite3* dbConnection, const std::string& mrl );
static bool createTable(sqlite3* connection); static bool createTable(sqlite3* connection);
virtual unsigned int id() const; virtual unsigned int id() const;
......
...@@ -49,16 +49,16 @@ std::vector<FilePtr>& Label::files() ...@@ -49,16 +49,16 @@ std::vector<FilePtr>& Label::files()
return *m_files; return *m_files;
} }
bool Label::insert( sqlite3* dbConnection ) LabelPtr Label::create( sqlite3* dbConnection, const std::string& name )
{ {
assert( m_dbConnection == nullptr ); auto self = std::make_shared<Label>( name );
assert( m_id == 0 );
const char* req = "INSERT INTO Label VALUES(NULL, ?)"; const char* req = "INSERT INTO Label VALUES(NULL, ?)";
if ( SqliteTools::executeRequest( dbConnection, req, m_name ) == false ) auto pKey = _Cache::insert( dbConnection, self, req, self->m_name );
return false; if ( pKey == 0 )
m_dbConnection = dbConnection; return nullptr;
m_id = sqlite3_last_insert_rowid( dbConnection ); self->m_dbConnection = dbConnection;
return true; self->m_id = pKey;
return self;
} }
bool Label::createTable(sqlite3* dbConnection) bool Label::createTable(sqlite3* dbConnection)
......
...@@ -29,8 +29,9 @@ struct LabelCachePolicy ...@@ -29,8 +29,9 @@ struct LabelCachePolicy
class Label : public ILabel, public Cache<Label, ILabel, policy::LabelTable, policy::LabelCachePolicy> class Label : public ILabel, public Cache<Label, ILabel, policy::LabelTable, policy::LabelCachePolicy>
{ {
private:
typedef Cache<Label, ILabel, policy::LabelTable, policy::LabelCachePolicy> _Cache;
public: public:
Label(sqlite3* dbConnection, sqlite3_stmt* stmt); Label(sqlite3* dbConnection, sqlite3_stmt* stmt);
Label( const std::string& name ); Label( const std::string& name );
...@@ -38,8 +39,8 @@ class Label : public ILabel, public Cache<Label, ILabel, policy::LabelTable, pol ...@@ -38,8 +39,8 @@ class Label : public ILabel, public Cache<Label, ILabel, policy::LabelTable, pol
virtual unsigned int id() const; virtual unsigned int id() const;
virtual const std::string& name(); virtual const std::string& name();
virtual std::vector<FilePtr>& files(); virtual std::vector<FilePtr>& files();
bool insert( sqlite3* dbConnection );
static LabelPtr create(sqlite3* dbConnection, const std::string& name );
static bool createTable( sqlite3* dbConnection ); static bool createTable( sqlite3* dbConnection );
private: private:
sqlite3* m_dbConnection; sqlite3* m_dbConnection;
......
...@@ -50,12 +50,7 @@ FilePtr MediaLibrary::file( const std::string& path ) ...@@ -50,12 +50,7 @@ FilePtr MediaLibrary::file( const std::string& path )
FilePtr MediaLibrary::addFile( const std::string& path ) FilePtr MediaLibrary::addFile( const std::string& path )
{ {
auto f = File::create( path ); return File::create( m_dbConnection, path );
if ( f->insert( m_dbConnection ) == false )
{
return nullptr;
}
return f;
} }
bool MediaLibrary::deleteFile( const std::string& mrl ) bool MediaLibrary::deleteFile( const std::string& mrl )
...@@ -70,12 +65,7 @@ bool MediaLibrary::deleteFile( FilePtr file ) ...@@ -70,12 +65,7 @@ bool MediaLibrary::deleteFile( FilePtr file )
LabelPtr MediaLibrary::createLabel( const std::string& label ) LabelPtr MediaLibrary::createLabel( const std::string& label )
{ {
auto l = Label::create( label ); return Label::create( m_dbConnection, label );
if ( l->insert( m_dbConnection ) == false )
{
return nullptr;
}
return l;
} }
bool MediaLibrary::deleteLabel( const std::string& text ) bool MediaLibrary::deleteLabel( const std::string& text )
......
...@@ -105,6 +105,18 @@ class SqliteTools ...@@ -105,6 +105,18 @@ class SqliteTools
return res == SQLITE_DONE; return res == SQLITE_DONE;
} }
/**
* Inserts a record to the DB and return the newly created primary key.
* Returns 0 (which is an invalid sqlite primary key) when insertion fails.
*/
template <typename... Args>
static unsigned int insert( sqlite3* dbConnection, const char* req, const Args&... args )
{
if ( executeRequest( dbConnection, req, args... ) == false )
return 0;
return sqlite3_last_insert_rowid( dbConnection );
}
private: private:
template <typename... Args> template <typename... Args>
static StmtPtr prepareRequest( sqlite3* dbConnection, const char* req, const Args&... args ) static StmtPtr prepareRequest( sqlite3* dbConnection, const char* req, const Args&... args )
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment