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

Use a pair of shared_ptr/weak_ptr to monitor the database connection

This will avoid random crash due to dangling pointer in case the DB was
released
parent e7b5de70
......@@ -11,6 +11,7 @@ class IMetadataService;
class IMovie;
class IShow;
class IShowEpisode;
class sqlite3;
typedef std::shared_ptr<IFile> FilePtr;
typedef std::shared_ptr<ILabel> LabelPtr;
......@@ -19,5 +20,6 @@ typedef std::shared_ptr<IAlbumTrack> AlbumTrackPtr;
typedef std::shared_ptr<IShow> ShowPtr;
typedef std::shared_ptr<IShowEpisode> ShowEpisodePtr;
typedef std::shared_ptr<IMovie> MoviePtr;
typedef std::weak_ptr<sqlite3> DBConnection;
#endif // TYPES_H
......@@ -7,7 +7,7 @@ const std::string policy::AlbumTable::Name = "Album";
const std::string policy::AlbumTable::CacheColumn = "id_album";
unsigned int Album::* const policy::AlbumTable::PrimaryKey = &Album::m_id;
Album::Album(sqlite3* dbConnection, sqlite3_stmt* stmt)
Album::Album(DBConnection dbConnection, sqlite3_stmt* stmt)
: m_dbConnection( dbConnection )
{
m_id = sqlite3_column_int( stmt, 0 );
......@@ -20,8 +20,7 @@ Album::Album(sqlite3* dbConnection, sqlite3_stmt* stmt)
}
Album::Album( const std::string& id3tag )
: m_dbConnection( nullptr )
, m_id( 0 )
: m_id( 0 )
, m_releaseDate( 0 )
, m_lastSyncDate( 0 )
, m_id3tag( id3tag )
......@@ -123,7 +122,7 @@ bool Album::destroy()
return _Cache::destroy( m_dbConnection, this );
}
bool Album::createTable( sqlite3* dbConnection )
bool Album::createTable(DBConnection dbConnection )
{
static const std::string req = "CREATE TABLE IF NOT EXISTS " +
policy::AlbumTable::Name +
......@@ -139,7 +138,7 @@ bool Album::createTable( sqlite3* dbConnection )
return SqliteTools::executeRequest( dbConnection, req );
}
AlbumPtr Album::create( sqlite3* dbConnection, const std::string& id3Tag )
AlbumPtr Album::create(DBConnection dbConnection, const std::string& id3Tag )
{
auto album = std::make_shared<Album>( id3Tag );
static const std::string& req = "INSERT INTO " + policy::AlbumTable::Name +
......
......@@ -26,7 +26,7 @@ class Album : public IAlbum, public Cache<Album, IAlbum, policy::AlbumTable>
private:
typedef Cache<Album, IAlbum, policy::AlbumTable> _Cache;
public:
Album( sqlite3* dbConnection, sqlite3_stmt* stmt );
Album( DBConnection dbConnection, sqlite3_stmt* stmt );
Album( const std::string& id3tag );
virtual unsigned int id() const;
......@@ -43,11 +43,11 @@ class Album : public IAlbum, public Cache<Album, IAlbum, policy::AlbumTable>
virtual AlbumTrackPtr addTrack( const std::string& name, unsigned int trackNb );
virtual bool destroy();
static bool createTable( sqlite3* dbConnection );
static AlbumPtr create( sqlite3* dbConnection, const std::string& id3Tag );
static bool createTable( DBConnection dbConnection );
static AlbumPtr create( DBConnection dbConnection, const std::string& id3Tag );
protected:
sqlite3* m_dbConnection;
DBConnection m_dbConnection;
unsigned int m_id;
std::string m_name;
unsigned int m_releaseDate;
......
......@@ -7,7 +7,7 @@ const std::string policy::AlbumTrackTable::Name = "AlbumTrack";
const std::string policy::AlbumTrackTable::CacheColumn = "id_track";
unsigned int AlbumTrack::* const policy::AlbumTrackTable::PrimaryKey = &AlbumTrack::m_id;
AlbumTrack::AlbumTrack( sqlite3* dbConnection, sqlite3_stmt* stmt )
AlbumTrack::AlbumTrack( DBConnection dbConnection, sqlite3_stmt* stmt )
: m_dbConnection( dbConnection )
, m_album( nullptr )
{
......@@ -19,8 +19,7 @@ AlbumTrack::AlbumTrack( sqlite3* dbConnection, sqlite3_stmt* stmt )
}
AlbumTrack::AlbumTrack( const std::string& title, unsigned int trackNumber, unsigned int albumId )
: m_dbConnection( nullptr )
, m_id( 0 )
: m_id( 0 )
, m_title( title )
, m_trackNumber( trackNumber )
, m_albumId( albumId )
......@@ -33,7 +32,7 @@ unsigned int AlbumTrack::id() const
return m_id;
}
bool AlbumTrack::createTable(sqlite3* dbConnection)
bool AlbumTrack::createTable( DBConnection dbConnection )
{
const char* req = "CREATE TABLE IF NOT EXISTS AlbumTrack ("
"id_track INTEGER PRIMARY KEY AUTOINCREMENT,"
......@@ -46,7 +45,7 @@ bool AlbumTrack::createTable(sqlite3* dbConnection)
return SqliteTools::executeRequest( dbConnection, req );
}
AlbumTrackPtr AlbumTrack::create(sqlite3* dbConnection, unsigned int albumId, const std::string& name, unsigned int trackNb)
AlbumTrackPtr AlbumTrack::create(DBConnection dbConnection, unsigned int albumId, const std::string& name, unsigned int trackNb)
{
auto self = std::make_shared<AlbumTrack>( name, trackNb, albumId );
static const std::string req = "INSERT INTO " + policy::AlbumTrackTable::Name
......
......@@ -26,7 +26,7 @@ class AlbumTrack : public IAlbumTrack, public Cache<AlbumTrack, IAlbumTrack, pol
private:
typedef Cache<AlbumTrack, IAlbumTrack, policy::AlbumTrackTable> _Cache;
public:
AlbumTrack( sqlite3* dbConnection, sqlite3_stmt* stmt );
AlbumTrack( DBConnection dbConnection, sqlite3_stmt* stmt );
AlbumTrack( const std::string& title, unsigned int trackNumber, unsigned int albumId );
virtual unsigned int id() const;
......@@ -38,12 +38,12 @@ class AlbumTrack : public IAlbumTrack, public Cache<AlbumTrack, IAlbumTrack, pol
virtual bool destroy();
virtual bool files( std::vector<FilePtr>& files );
static bool createTable( sqlite3* dbConnection );
static AlbumTrackPtr create(sqlite3* dbConnection, unsigned int albumId,
static bool createTable( DBConnection dbConnection );
static AlbumTrackPtr create( DBConnection dbConnection, unsigned int albumId,
const std::string& name, unsigned int trackNb );
private:
sqlite3* m_dbConnection;
DBConnection m_dbConnection;
unsigned int m_id;
std::string m_title;
std::string m_genre;
......
......@@ -53,7 +53,7 @@ template <typename IMPL, typename INTF, typename TABLEPOLICY, typename CACHEPOLI
class Cache
{
public:
static std::shared_ptr<IMPL> fetch( sqlite3* dbConnection, const typename CACHEPOLICY::KeyType& key )
static std::shared_ptr<IMPL> fetch( DBConnection dbConnectionWeak, const typename CACHEPOLICY::KeyType& key )
{
Lock lock( Mutex );
auto it = Store.find( key );
......@@ -61,7 +61,7 @@ class Cache
return it->second;
static const std::string req = "SELECT * FROM " + TABLEPOLICY::Name +
" WHERE " + TABLEPOLICY::CacheColumn + " = ?";
auto res = SqliteTools::fetchOne<IMPL>( dbConnection, req.c_str(), key );
auto res = SqliteTools::fetchOne<IMPL>( dbConnectionWeak, req.c_str(), key );
Store[key] = res;
return res;
}
......@@ -72,13 +72,13 @@ class Cache
* @param res A reference to the result vector. All existing elements will
* be discarded.
*/
static bool fetchAll( sqlite3* dbConnection, std::vector<std::shared_ptr<INTF> >& res )
static bool fetchAll( DBConnection dbConnectionWeak, std::vector<std::shared_ptr<INTF> >& res )
{
static const std::string req = "SELECT * FROM " + TABLEPOLICY::Name;
return SqliteTools::fetchAll<IMPL, INTF>( dbConnection, req.c_str(), res );
return SqliteTools::fetchAll<IMPL, INTF>( dbConnectionWeak, req.c_str(), res );
}
static std::shared_ptr<IMPL> load( sqlite3* dbConnection, sqlite3_stmt* stmt )
static std::shared_ptr<IMPL> load( std::shared_ptr<sqlite3> dbConnection, sqlite3_stmt* stmt )
{
auto cacheKey = CACHEPOLICY::key( stmt );
......@@ -91,7 +91,7 @@ class Cache
return inst;
}
static bool destroy( sqlite3* dbConnection, const typename CACHEPOLICY::KeyType& key )
static bool destroy( DBConnection dbConnectionWeak, const typename CACHEPOLICY::KeyType& key )
{
Lock lock( Mutex );
auto it = Store.find( key );
......@@ -99,19 +99,19 @@ class Cache
Store.erase( it );
static const std::string req = "DELETE FROM " + TABLEPOLICY::Name + " WHERE " +
TABLEPOLICY::CacheColumn + " = ?";
return SqliteTools::executeDelete( dbConnection, req.c_str(), key );
return SqliteTools::executeDelete( dbConnectionWeak, req.c_str(), key );
}
static bool destroy( sqlite3* dbConnection, const std::shared_ptr<IMPL>& self )
static bool destroy( DBConnection dbConnectionWeak, const std::shared_ptr<IMPL>& self )
{
const auto& key = CACHEPOLICY::key( self );
return destroy( dbConnection, key );
return destroy( dbConnectionWeak, key );
}
static bool destroy( sqlite3* dbConnection, const IMPL* self )
static bool destroy( DBConnection dbConnectionWeak, const IMPL* self )
{
const auto& key = CACHEPOLICY::key( self );
return destroy( dbConnection, key );
return destroy( dbConnectionWeak, key );
}
static void clear()
......@@ -141,9 +141,9 @@ class Cache
* Create a new instance of the cache class.
*/
template <typename... Args>
static bool insert( sqlite3* dbConnection, std::shared_ptr<IMPL> self, const std::string& req, const Args&... args )
static bool insert( DBConnection dbConnectionWeak, std::shared_ptr<IMPL> self, const std::string& req, const Args&... args )
{
unsigned int pKey = SqliteTools::insert( dbConnection, req, args... );
unsigned int pKey = SqliteTools::insert( dbConnectionWeak, req, args... );
if ( pKey == 0 )
return false;
(self.get())->*TABLEPOLICY::PrimaryKey = pKey;
......
......@@ -14,7 +14,7 @@ const std::string policy::FileTable::Name = "File";
const std::string policy::FileTable::CacheColumn = "mrl";
unsigned int File::* const policy::FileTable::PrimaryKey = &File::m_id;
File::File( sqlite3* dbConnection, sqlite3_stmt* stmt )
File::File( DBConnection dbConnection, sqlite3_stmt* stmt )
: m_dbConnection( dbConnection )
{
m_id = sqlite3_column_int( stmt, 0 );
......@@ -28,8 +28,7 @@ File::File( sqlite3* dbConnection, sqlite3_stmt* stmt )
}
File::File( const std::string& mrl )
: m_dbConnection( nullptr )
, m_id( 0 )
: m_id( 0 )
, m_type( UnknownType )
, m_duration( 0 )
, m_albumTrackId( 0 )
......@@ -40,7 +39,7 @@ File::File( const std::string& mrl )
{
}
FilePtr File::create( sqlite3* dbConnection, const std::string& mrl )
FilePtr File::create( DBConnection dbConnection, const std::string& mrl )
{
auto self = std::make_shared<File>( mrl );
static const std::string req = "INSERT INTO " + policy::FileTable::Name +
......@@ -142,7 +141,7 @@ unsigned int File::id() const
return m_id;
}
bool File::createTable(sqlite3* connection)
bool File::createTable( DBConnection connection )
{
std::string req = "CREATE TABLE IF NOT EXISTS " + policy::FileTable::Name + "("
"id_file INTEGER PRIMARY KEY AUTOINCREMENT,"
......@@ -165,7 +164,7 @@ bool File::createTable(sqlite3* connection)
bool File::addLabel( LabelPtr label )
{
if ( m_dbConnection == nullptr || m_id == 0 || label->id() == 0)
if ( m_id == 0 || label->id() == 0 )
{
std::cerr << "Both file & label need to be inserted in database before being linked together" << std::endl;
return false;
......@@ -176,7 +175,7 @@ bool File::addLabel( LabelPtr label )
bool File::removeLabel( LabelPtr label )
{
if ( m_dbConnection == nullptr || m_id == 0 || label->id() == 0 )
if ( m_id == 0 || label->id() == 0 )
{
std::cerr << "Can't unlink a label/file not inserted in database" << std::endl;
return false;
......
......@@ -39,11 +39,11 @@ class File : public IFile, public Cache<File, IFile, policy::FileTable, policy::
// ::new (pv) T(std::forward(args)...)
// shall be well-formed, and private constructor would prevent that.
// There might be a way with a user-defined allocator, but we'll see that later...
File(sqlite3* dbConnection , sqlite3_stmt* stmt);
File(DBConnection dbConnection , sqlite3_stmt* stmt);
File( const std::string& mrl );
static FilePtr create(sqlite3* dbConnection, const std::string& mrl );
static bool createTable(sqlite3* connection);
static FilePtr create( DBConnection dbConnection, const std::string& mrl );
static bool createTable( DBConnection connection );
virtual unsigned int id() const;
virtual AlbumTrackPtr albumTrack();
......@@ -60,7 +60,7 @@ class File : public IFile, public Cache<File, IFile, policy::FileTable, policy::
virtual bool setMovie( MoviePtr movie );
private:
sqlite3* m_dbConnection;
DBConnection m_dbConnection;
// DB fields:
unsigned int m_id;
......
......@@ -10,7 +10,7 @@ const std::string policy::LabelTable::Name = "Label";
const std::string policy::LabelTable::CacheColumn = "name";
unsigned int Label::* const policy::LabelTable::PrimaryKey = &Label::m_id;
Label::Label( sqlite3* dbConnection, sqlite3_stmt* stmt )
Label::Label( DBConnection dbConnection, sqlite3_stmt* stmt )
: m_dbConnection( dbConnection )
{
m_id = sqlite3_column_int( stmt, 0 );
......@@ -18,8 +18,7 @@ Label::Label( sqlite3* dbConnection, sqlite3_stmt* stmt )
}
Label::Label( const std::string& name )
: m_dbConnection( nullptr )
, m_id( 0 )
: m_id( 0 )
, m_name( name )
{
}
......@@ -42,7 +41,7 @@ bool Label::files( std::vector<FilePtr>& files )
return SqliteTools::fetchAll<File>( m_dbConnection, req, files, m_id );
}
LabelPtr Label::create( sqlite3* dbConnection, const std::string& name )
LabelPtr Label::create(DBConnection dbConnection, const std::string& name )
{
auto self = std::make_shared<Label>( name );
const char* req = "INSERT INTO Label VALUES(NULL, ?)";
......@@ -52,7 +51,7 @@ LabelPtr Label::create( sqlite3* dbConnection, const std::string& name )
return self;
}
bool Label::createTable(sqlite3* dbConnection)
bool Label::createTable(DBConnection dbConnection)
{
std::string req = "CREATE TABLE IF NOT EXISTS " + policy::LabelTable::Name + "("
"id_label INTEGER PRIMARY KEY AUTOINCREMENT, "
......
......@@ -33,7 +33,7 @@ class Label : public ILabel, public Cache<Label, ILabel, policy::LabelTable, pol
private:
typedef Cache<Label, ILabel, policy::LabelTable, policy::LabelCachePolicy> _Cache;
public:
Label(sqlite3* dbConnection, sqlite3_stmt* stmt);
Label( DBConnection dbConnection, sqlite3_stmt* stmt);
Label( const std::string& name );
public:
......@@ -41,10 +41,10 @@ class Label : public ILabel, public Cache<Label, ILabel, policy::LabelTable, pol
virtual const std::string& name();
virtual bool files( std::vector<FilePtr>& files );
static LabelPtr create(sqlite3* dbConnection, const std::string& name );
static bool createTable( sqlite3* dbConnection );
static LabelPtr create( DBConnection dbConnection, const std::string& name );
static bool createTable( DBConnection dbConnection );
private:
sqlite3* m_dbConnection;
DBConnection m_dbConnection;
unsigned int m_id;
std::string m_name;
......
......@@ -13,7 +13,6 @@
#include "ShowEpisode.h"
MediaLibrary::MediaLibrary()
: m_dbConnection( nullptr )
{
}
......@@ -26,15 +25,16 @@ MediaLibrary::~MediaLibrary()
Show::clear();
ShowEpisode::clear();
Movie::clear();
sqlite3_close( m_dbConnection );
}
bool MediaLibrary::initialize(const std::string& dbPath)
{
int res = sqlite3_open( dbPath.c_str(), &m_dbConnection );
sqlite3* dbConnection;
int res = sqlite3_open( dbPath.c_str(), &dbConnection );
if ( res != SQLITE_OK )
return false;
if ( SqliteTools::executeRequest( m_dbConnection, "PRAGMA foreign_keys = ON" ) == false )
m_dbConnection.reset( dbConnection, &sqlite3_close );
if ( SqliteTools::executeRequest( DBConnection(m_dbConnection), "PRAGMA foreign_keys = ON" ) == false )
return false;
return ( File::createTable( m_dbConnection ) &&
Label::createTable( m_dbConnection ) &&
......@@ -91,7 +91,7 @@ AlbumPtr MediaLibrary::album( const std::string& id3Tag )
// We can't use Cache helper, since albums are cached by primary keys
static const std::string req = "SELECT * FROM " + policy::AlbumTable::Name +
" WHERE id3tag = ?";
return SqliteTools::fetchOne<Album>( m_dbConnection, req, id3Tag );
return SqliteTools::fetchOne<Album>( DBConnection( m_dbConnection ), req, id3Tag );
}
AlbumPtr MediaLibrary::createAlbum( const std::string& id3Tag )
......
......@@ -33,7 +33,7 @@ class MediaLibrary : public IMediaLibrary
virtual void addMetadataService( IMetadataService* service );
private:
sqlite3* m_dbConnection;
std::shared_ptr<sqlite3> m_dbConnection;
std::vector<std::unique_ptr<IMetadataService>> m_mdServices;
};
#endif // MEDIALIBRARY_H
......@@ -6,7 +6,7 @@ const std::string policy::MovieTable::Name = "Movie";
const std::string policy::MovieTable::CacheColumn = "id_movie";
unsigned int Movie::* const policy::MovieTable::PrimaryKey = &Movie::m_id;
Movie::Movie( sqlite3* dbConnection, sqlite3_stmt* stmt )
Movie::Movie( DBConnection dbConnection, sqlite3_stmt* stmt )
: m_dbConnection( dbConnection )
{
m_id = Traits<unsigned int>::Load( stmt, 0 );
......@@ -18,8 +18,7 @@ Movie::Movie( sqlite3* dbConnection, sqlite3_stmt* stmt )
}
Movie::Movie( const std::string& title )
: m_dbConnection( nullptr )
, m_id( 0 )
: m_id( 0 )
, m_title( title )
, m_releaseDate( 0 )
{
......@@ -115,7 +114,7 @@ bool Movie::files( std::vector<FilePtr>& files )
return SqliteTools::fetchAll<File>( m_dbConnection, req, files, m_id );
}
bool Movie::createTable( sqlite3* dbConnection )
bool Movie::createTable( DBConnection dbConnection )
{
static const std::string req = "CREATE TABLE IF NOT EXISTS " + policy::MovieTable::Name
+ "("
......@@ -129,7 +128,7 @@ bool Movie::createTable( sqlite3* dbConnection )
return SqliteTools::executeRequest( dbConnection, req );
}
MoviePtr Movie::create( sqlite3* dbConnection, const std::string& title )
MoviePtr Movie::create(DBConnection dbConnection, const std::string& title )
{
auto movie = std::make_shared<Movie>( title );
static const std::string req = "INSERT INTO " + policy::MovieTable::Name
......
......@@ -20,7 +20,7 @@ struct MovieTable
class Movie : public IMovie, public Cache<Movie, IMovie, policy::MovieTable>
{
public:
Movie( sqlite3* dbConnection, sqlite3_stmt* stmt );
Movie( DBConnection dbConnection, sqlite3_stmt* stmt );
Movie( const std::string& title );
virtual unsigned int id() const;
......@@ -36,11 +36,11 @@ class Movie : public IMovie, public Cache<Movie, IMovie, policy::MovieTable>
virtual bool destroy();
virtual bool files( std::vector<FilePtr>& files );
static bool createTable( sqlite3* dbConnection );
static MoviePtr create( sqlite3* dbConnection, const std::string& title );
static bool createTable( DBConnection dbConnection );
static MoviePtr create( DBConnection dbConnection, const std::string& title );
private:
sqlite3* m_dbConnection;
DBConnection m_dbConnection;
unsigned int m_id;
std::string m_title;
time_t m_releaseDate;
......
......@@ -6,7 +6,7 @@ const std::string policy::ShowTable::Name = "Show";
const std::string policy::ShowTable::CacheColumn = "id_show";
unsigned int Show::* const policy::ShowTable::PrimaryKey = &Show::m_id;
Show::Show(sqlite3* dbConnection, sqlite3_stmt* stmt)
Show::Show(DBConnection dbConnection, sqlite3_stmt* stmt)
: m_dbConnection( dbConnection )
{
m_id = Traits<unsigned int>::Load( stmt, 0 );
......@@ -126,7 +126,7 @@ bool Show::destroy()
return _Cache::destroy( m_dbConnection, this );
}
bool Show::createTable(sqlite3* dbConnection)
bool Show::createTable(DBConnection dbConnection)
{
const std::string req = "CREATE TABLE IF NOT EXISTS " + policy::ShowTable::Name + "("
"id_show INTEGER PRIMARY KEY AUTOINCREMENT,"
......@@ -140,7 +140,7 @@ bool Show::createTable(sqlite3* dbConnection)
return SqliteTools::executeRequest( dbConnection, req );
}
ShowPtr Show::create( sqlite3* dbConnection, const std::string& name )
ShowPtr Show::create(DBConnection dbConnection, const std::string& name )
{
auto show = std::make_shared<Show>( name );
static const std::string req = "INSERT INTO " + policy::ShowTable::Name
......
......@@ -22,7 +22,7 @@ struct ShowTable
class Show : public IShow, public Cache<Show, IShow, policy::ShowTable>
{
public:
Show( sqlite3* dbConnection, sqlite3_stmt* stmt );
Show( DBConnection dbConnection, sqlite3_stmt* stmt );
Show( const std::string& name );
virtual unsigned int id() const;
......@@ -40,11 +40,11 @@ class Show : public IShow, public Cache<Show, IShow, policy::ShowTable>
virtual bool episodes( std::vector<ShowEpisodePtr>& episodes );
virtual bool destroy();
static bool createTable( sqlite3* dbConnection );
static ShowPtr create(sqlite3* dbConnection, const std::string& name );
static bool createTable( DBConnection dbConnection );
static ShowPtr create( DBConnection dbConnection, const std::string& name );
protected:
sqlite3* m_dbConnection;
DBConnection m_dbConnection;
unsigned int m_id;
std::string m_name;
time_t m_releaseDate;
......
......@@ -7,7 +7,7 @@ const std::string policy::ShowEpisodeTable::Name = "ShowEpisode";
const std::string policy::ShowEpisodeTable::CacheColumn = "show_id";
unsigned int ShowEpisode::* const policy::ShowEpisodeTable::PrimaryKey = &ShowEpisode::m_id;
ShowEpisode::ShowEpisode( sqlite3* dbConnection, sqlite3_stmt* stmt )
ShowEpisode::ShowEpisode( DBConnection dbConnection, sqlite3_stmt* stmt )
: m_dbConnection( dbConnection )
{
m_id = Traits<unsigned int>::Load( stmt, 0 );
......@@ -21,9 +21,8 @@ ShowEpisode::ShowEpisode( sqlite3* dbConnection, sqlite3_stmt* stmt )
m_showId = Traits<unsigned int>::Load( stmt, 8 );
}
ShowEpisode::ShowEpisode(const std::string& name, unsigned int episodeNumber, unsigned int showId )
: m_dbConnection( nullptr )
, m_id( 0 )
ShowEpisode::ShowEpisode( const std::string& name, unsigned int episodeNumber, unsigned int showId )
: m_id( 0 )
, m_episodeNumber( episodeNumber )
, m_name( name )
, m_seasonNumber( 0 )
......@@ -137,7 +136,7 @@ bool ShowEpisode::destroy()
return _Cache::destroy( m_dbConnection, this );
}
bool ShowEpisode::createTable(sqlite3* dbConnection)
bool ShowEpisode::createTable( DBConnection dbConnection )
{
const std::string req = "CREATE TABLE IF NOT EXISTS " + policy::ShowEpisodeTable::Name
+ "("
......@@ -155,7 +154,7 @@ bool ShowEpisode::createTable(sqlite3* dbConnection)
return SqliteTools::executeRequest( dbConnection, req );
}
ShowEpisodePtr ShowEpisode::create( sqlite3* dbConnection, const std::string& title, unsigned int episodeNumber, unsigned int showId )
ShowEpisodePtr ShowEpisode::create( DBConnection dbConnection, const std::string& title, unsigned int episodeNumber, unsigned int showId )
{
auto episode = std::make_shared<ShowEpisode>( title, episodeNumber, showId );
static const std::string req = "INSERT INTO " + policy::ShowEpisodeTable::Name
......
......@@ -24,7 +24,7 @@ struct ShowEpisodeTable
class ShowEpisode : public IShowEpisode, public Cache<ShowEpisode, IShowEpisode, policy::ShowEpisodeTable>
{
public:
ShowEpisode(sqlite3* dbConnection, sqlite3_stmt* stmt);
ShowEpisode( DBConnection dbConnection, sqlite3_stmt* stmt );
ShowEpisode(const std::string& name, unsigned int episodeNumber, unsigned int showId );
virtual unsigned int id() const;
......@@ -43,11 +43,11 @@ class ShowEpisode : public IShowEpisode, public Cache<ShowEpisode, IShowEpisode,
virtual bool files( std::vector<FilePtr>& files );
virtual bool destroy();
static bool createTable( sqlite3* dbConnection );
static ShowEpisodePtr create(sqlite3* dbConnection, const std::string& title, unsigned int episodeNumber, unsigned int showId );
static bool createTable( DBConnection dbConnection );
static ShowEpisodePtr create( DBConnection dbConnection, const std::string& title, unsigned int episodeNumber, unsigned int showId );
private:
sqlite3* m_dbConnection;
DBConnection m_dbConnection;
unsigned int m_id;
std::string m_artworkUrl;
unsigned int m_episodeNumber;
......
#ifndef SQLITETOOLS_H
#define SQLITETOOLS_H
#include <cassert>
#include <cstring>
#include <memory>
#include <sqlite3.h>
......@@ -8,6 +9,8 @@
#include <vector>
#include <iostream>
#include "Types.h"
// Have a base case for integral type only
// Use specialization to define other cases, and fail for the rest.
template <typename T>
......@@ -53,8 +56,12 @@ class SqliteTools
* be discarded.
*/
template <typename IMPL, typename INTF, typename... Args>
static bool fetchAll( sqlite3* dbConnection, const std::string& req, std::vector<std::shared_ptr<INTF> >& results, const Args&... args )
static bool fetchAll( DBConnection dbConnectionWeak, const std::string& req, std::vector<std::shared_ptr<INTF> >& results, const Args&... args )
{
auto dbConnection = dbConnectionWeak.lock();
if ( dbConnection == nullptr )
return false;
results.clear();
auto stmt = prepareRequest( dbConnection, req, args...);
if ( stmt == nullptr )
......@@ -70,8 +77,12 @@ class SqliteTools
}
template <typename T, typename... Args>
static std::shared_ptr<T> fetchOne( sqlite3* dbConnection, const std::string& req, const Args&... args )
static std::shared_ptr<T> fetchOne( DBConnection dbConnectionWeak, const std::string& req, const Args&... args )
{
auto dbConnection = dbConnectionWeak.lock();
if ( dbConnection == nullptr )
return false;
std::shared_ptr<T> result;
auto stmt = prepareRequest( dbConnection, req, args... );
if ( stmt == nullptr )
......@@ -83,43 +94,30 @@ class SqliteTools
}
template <typename... Args>
static bool executeDelete( sqlite3* dbConnection, const std::string& req, const Args&... args )
static bool executeDelete( DBConnection dbConnectionWeak, const std::string& req, const Args&... args )
{
auto dbConnection = dbConnectionWeak.lock();
if ( dbConnection == nullptr )
return false;
if ( executeRequest( dbConnection, req, args... ) == false )
return false;
return sqlite3_changes( dbConnection ) > 0;
return sqlite3_changes( dbConnection.get() ) > 0;
}
template <typename... Args>
static bool executeUpdate( sqlite3* dbConnection, const std::string& req, const Args&... args )
static bool executeUpdate( DBConnection dbConnectionWeak, const std::string& req, const Args&... args )
{
// The code would be exactly the same, do not freak out because it call delete :)
return executeDelete( dbConnection, req, args... );
return executeDelete( dbConnectionWeak, req, args... );
}
template <typename... Args>
static bool executeRequest( sqlite3* dbConnection, const std::string& req, const Args&... args )
static bool executeRequest( DBConnection dbConnectionWeak, const std::string& req, const Args&... args )
{
auto stmt = prepareRequest( dbConnection, req, args... );
if ( stmt == nullptr )
auto dbConnection = dbConnectionWeak.lock();
if ( dbConnection == nullptr )
return false;
int res;
do
{
res = sqlite3_step( stmt.get() );
} while ( res == SQLITE_ROW );
if ( res != SQLITE_DONE )
{
std::cerr << "Invalid result: " <<
#if SQLITE_VERSION_NUMBER >= 3007015
sqlite3_errstr( res )
#else
res
#endif
<< ": " << sqlite3_errmsg( dbConnection ) << std::endl;
return false;
}
return true;
return executeRequest( dbConnection, req, args... );
}
/**
......@@ -127,40 +125,70 @@ class SqliteTools
* Returns 0 (which is an invalid sqlite primary key) when insertion fails.
*/
template <typename... Args>
static unsigned int insert( sqlite3* dbConnection, const std::string& req, const Args&... args )
static unsigned int insert( DBConnection dbConnectionWeak, const std::string& req, const Args&... args )
{
auto dbConnection = dbConnectionWeak.lock();
if ( dbConnection == nullptr )
return false;
if ( executeRequest( dbConnection, req, args... ) == false )
return 0;
return sqlite3_last_insert_rowid( dbConnection );
return sqlite3_last_insert_rowid( dbConnection.get() );
}
private:
template <typename... Args>
static StmtPtr prepareRequest( sqlite3* dbConnection, const std::string& req, const Args&... args )
static StmtPtr prepareRequest( std::shared_ptr<sqlite3> dbConnectionPtr, const std::string& req, const Args&... args )
{
return _prepareRequest<1>( dbConnection, req, args... );
return _prepareRequest<1>( dbConnectionPtr, req, args... );
}
template <unsigned int>
static StmtPtr _prepareRequest( sqlite3* dbConnection, const std::string& req )
static StmtPtr _prepareRequest( std::shared_ptr<sqlite3> dbConnection, const std::string& req )
{
assert( dbConnection != nullptr );
sqlite3_stmt* stmt = nullptr;
int res = sqlite3_prepare_v2( dbConnection, req.c_str(), -1, &stmt, NULL );
int res = sqlite3_prepare_v2( dbConnection.get(), req.c_str(), -1, &stmt, NULL );
if ( res != SQLITE_OK )
{
std::cerr << "Failed to execute request: " << req << std::endl;
std::cerr << sqlite3_errmsg( dbConnection ) << std::endl;
std::cerr << sqlite3_errmsg( dbConnection.get() ) << std::endl;
}
return StmtPtr( stmt, &sqlite3_finalize );
}
template <unsigned int COLIDX, typename T, typename... Args>
static StmtPtr _prepareRequest( sqlite3* dbConnection, const std::string& req, const T& arg, const Args&... args )
static StmtPtr _prepareRequest( std::shared_ptr<sqlite3> dbConnectionPtr, const std::string& req, const T& arg, const Args&... args )
{
auto stmt = _prepareRequest<COLIDX + 1>( dbConnection, req, args... );