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

sqlite: Add transaction support

parent 9a3c2050
......@@ -158,7 +158,8 @@ std::vector<MediaPtr> Album::tracks() const
std::shared_ptr<AlbumTrack> Album::addTrack(std::shared_ptr<Media> media, unsigned int trackNb, unsigned int discNumber )
{
//FIXME: This MUST be executed as a transaction
auto t = m_dbConnection->newTransaction();
auto track = AlbumTrack::create( m_dbConnection, m_id, media.get(), trackNb, discNumber );
if ( track == nullptr )
return nullptr;
......@@ -174,6 +175,7 @@ std::shared_ptr<AlbumTrack> Album::addTrack(std::shared_ptr<Media> media, unsign
// Keeping the ordering consistent while adding items is going to be hard
// once we start to expose multiple sorting criteria
m_tracksCached = false;
t->commit();
return track;
}
......
......@@ -74,6 +74,7 @@ list(APPEND SRC_LIST ${HEADERS_LIST}
Utils.cpp
database/SqliteConnection.cpp
database/SqliteTransaction.cpp
logging/IostreamLogger.cpp
logging/Logger.cpp
......
......@@ -61,6 +61,21 @@ void SqliteConnection::release()
m_conns.erase( std::this_thread::get_id() );
}
std::unique_ptr<sqlite::Transaction> SqliteConnection::newTransaction()
{
sqlite::Transaction* ptr = nullptr;
try
{
ptr = new sqlite::Transaction( this );
return std::unique_ptr<sqlite::Transaction>{ ptr };
}
catch(...)
{
delete ptr;
return nullptr;
}
}
SqliteConnection::RequestContext SqliteConnection::acquireContext()
{
return RequestContext{ m_contextMutex };
......
......@@ -30,17 +30,20 @@
#include <thread>
#include <unordered_map>
#include "SqliteTransaction.h"
class SqliteConnection
{
private:
using RequestContext = std::unique_lock<std::mutex>;
public:
using RequestContext = std::unique_lock<std::mutex>;
explicit SqliteConnection( const std::string& dbPath );
// Returns the current thread's connection
// This will initiate a connection if required
sqlite3* getConn();
// Release the current thread's connection
void release();
std::unique_ptr<sqlite::Transaction> newTransaction();
RequestContext acquireContext();
private:
......
......@@ -174,6 +174,7 @@ class Tools
template <typename IMPL, typename INTF, typename... Args>
static std::vector<std::shared_ptr<INTF> > fetchAll( DBConnection dbConnection, const std::string& req, Args&&... args )
{
assert(Transaction::transactionInProgress() == false);
auto ctx = dbConnection->acquireContext();
auto chrono = std::chrono::steady_clock::now();
......@@ -195,6 +196,7 @@ class Tools
template <typename T, typename... Args>
static std::shared_ptr<T> fetchOne( DBConnection dbConnection, const std::string& req, Args&&... args )
{
assert(Transaction::transactionInProgress() == false);
auto ctx = dbConnection->acquireContext();
auto chrono = std::chrono::steady_clock::now();
......@@ -213,6 +215,9 @@ class Tools
template <typename... Args>
static bool executeRequest( DBConnection dbConnection, const std::string& req, Args&&... args )
{
// This is only used to create tables, assume there can't be a transaction
// running at the same time.
assert(Transaction::transactionInProgress() == false);
auto ctx = dbConnection->acquireContext();
return executeRequestLocked( dbConnection, req, std::forward<Args>( args )... );
}
......@@ -220,7 +225,9 @@ class Tools
template <typename... Args>
static bool executeDelete( DBConnection dbConnection, const std::string& req, Args&&... args )
{
auto ctx = dbConnection->acquireContext();
SqliteConnection::RequestContext ctx;
if (Transaction::transactionInProgress() == false)
ctx = dbConnection->acquireContext();
if ( executeRequestLocked( dbConnection, req, std::forward<Args>( args )... ) == false )
return false;
return sqlite3_changes( dbConnection->getConn() ) > 0;
......@@ -247,7 +254,9 @@ class Tools
template <typename... Args>
static unsigned int insert( DBConnection dbConnection, const std::string& req, Args&&... args )
{
auto ctx = dbConnection->acquireContext();
SqliteConnection::RequestContext ctx;
if (Transaction::transactionInProgress() == false)
ctx = dbConnection->acquireContext();
if ( executeRequestLocked( dbConnection, req, std::forward<Args>( args )... ) == false )
return 0;
return sqlite3_last_insert_rowid( dbConnection->getConn() );
......
/*****************************************************************************
* Media Library
*****************************************************************************
* Copyright (C) 2015 Hugo Beauzée-Luyssen, Videolabs
*
* Authors: Hugo Beauzée-Luyssen<hugo@beauzee.fr>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include "SqliteTransaction.h"
#include "SqliteTools.h"
namespace sqlite
{
thread_local Transaction* Transaction::CurrentTransaction = nullptr;
Transaction::Transaction(DBConnection dbConn)
: m_dbConn( dbConn )
{
assert( CurrentTransaction == nullptr );
LOG_DEBUG( "Starting SQLite transaction" );
Statement s( dbConn, "BEGIN" );
s.execute();
while ( s.row() != nullptr )
;
CurrentTransaction = this;
}
void Transaction::commit()
{
auto ctx = m_dbConn->acquireContext();
auto chrono = std::chrono::steady_clock::now();
Statement s( m_dbConn, "COMMIT" );
s.execute();
while ( s.row() != nullptr )
;
auto duration = std::chrono::steady_clock::now() - chrono;
LOG_DEBUG( "Flushed transaction in ",
std::chrono::duration_cast<std::chrono::microseconds>( duration ).count(), "µs" );
CurrentTransaction = nullptr;
}
bool Transaction::transactionInProgress()
{
return CurrentTransaction != nullptr;
}
Transaction::~Transaction()
{
if ( CurrentTransaction != nullptr )
{
Statement s( m_dbConn, "ROLLBACK" );
s.execute();
while ( s.row() != nullptr )
;
CurrentTransaction = nullptr;
}
}
}
/*****************************************************************************
* Media Library
*****************************************************************************
* Copyright (C) 2015 Hugo Beauzée-Luyssen, Videolabs
*
* Authors: Hugo Beauzée-Luyssen<hugo@beauzee.fr>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#pragma once
#include "Types.h"
namespace sqlite
{
class Transaction
{
public:
Transaction( DBConnection dbConn );
void commit();
static bool transactionInProgress();
~Transaction();
private:
DBConnection m_dbConn;
static thread_local Transaction* CurrentTransaction;
};
}
......@@ -101,6 +101,7 @@ IMetadataService::Status VLCMetadataService::handleMediaMeta( std::shared_ptr<Me
LOG_ERROR( "Failed to fetch tracks" );
return Status::Fatal;
}
bool isAudio = true;
for ( auto& track : tracks )
{
......
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