Program Listing for File SessionDB.h¶
↰ Return to documentation for file (src/smartpeak/include/SmartPeak/io/SessionDB.h)
// --------------------------------------------------------------------------
// SmartPeak -- Fast and Accurate CE-, GC- and LC-MS(/MS) Data Processing
// --------------------------------------------------------------------------
// Copyright The SmartPeak Team -- Novo Nordisk Foundation
// Center for Biosustainability, Technical University of Denmark 2018-2021.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL ANY OF THE AUTHORS OR THE CONTRIBUTING
// INSTITUTIONS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// --------------------------------------------------------------------------
// $Maintainer: Douglas McCloskey, Bertrand Boudaud $
// $Authors: Douglas McCloskey, Bertrand Boudaud $
// --------------------------------------------------------------------------
#pragma once
#include <sqlite3.h>
#include <filesystem>
#include <string>
#include <map>
#include <vector>
#include <optional>
#include <plog/Log.h>
#include <SmartPeak/iface/IPropertiesHandler.h>
#include <OpenMS/DATASTRUCTURES/String.h>
namespace SmartPeak
{
class SessionDB
{
public:
struct DBContext
{
sqlite3* db = nullptr;
std::vector<std::string> columns;
std::string table;
sqlite3_stmt* stmt = nullptr;
};
void setDBFilePath(std::filesystem::path session_file_name)
{
session_file_name_ = session_file_name;
smartpeak_version_ = "Unknown";
session_info_logged_ = false;
}
const std::filesystem::path& getDBFilePath() const
{
return session_file_name_;
}
bool writePropertiesHandler(const IPropertiesHandler& properties_handler);
bool readPropertiesHandler(IPropertiesHandler& properties_handler);
template<typename Value, typename ...Args>
std::optional<DBContext> beginRead(const std::string& table_name, const Value& value, const Args& ...args);
template<typename WhereValue, typename Value, typename ...Args>
std::optional<SessionDB::DBContext> beginReadWhere(const std::string& table_name, const std::string& where_id, const WhereValue& where_value, const Value& value, const Args& ...args);
template<typename ...Args>
bool read(SessionDB::DBContext& db_context, Args& ...args);
void endRead(SessionDB::DBContext& db_context);
template<typename Value, typename ...Args>
std::optional<DBContext> beginWrite(const std::string& table_name, const Value& value, const char* value_type, const Args& ...args);
template<typename Value, typename ...Args>
void write(SessionDB::DBContext& db_context, const Value& value, const Args& ...args);
void endWrite(SessionDB::DBContext& db_context);
int64_t getLastInsertedRowId(SessionDB::DBContext& db_context) const;
protected:
template<typename Value, typename ...Args>
void beginRead(std::ostringstream& os, std::vector<std::string>& columns, const Value& value, const Args& ...args);
template<typename Value>
void beginRead(std::ostringstream& os, std::vector<std::string>& columns, const Value& value);
template<typename Value, typename ...Args>
void beginWrite(std::ostringstream& os, std::vector<std::string>& columns, const Value& value, const char* value_type, const Args& ...args);
template<typename Value>
void beginWrite(std::ostringstream& os, std::vector<std::string>& columns, const Value& value, const char* value_type);
template<typename Value, typename ...Args>
void write(std::ostringstream& os, SessionDB::DBContext& db_context, const Value& value, const Args& ...args);
template<typename Value>
void write(std::ostringstream& os, SessionDB::DBContext& db_context, const Value& value);
template<typename Value, typename ...Args>
void readRowFromDB(SessionDB::DBContext& db_context, int column, Value& value, Args& ...args);
template<typename Value>
void readRowFromDB(SessionDB::DBContext& db_context, int column, Value& value);
std::optional<sqlite3*> openSessionDB();
void closeSessionDB(sqlite3* db);
void updateSessionInfo(sqlite3* db);
void displaySessionInfo();
void writeSessionInfo(sqlite3* db);
void logSQLError(const std::string& error_message, const std::string& sql_command = "") const;
protected:
std::filesystem::path session_file_name_;
std::string smartpeak_version_ = "Unknown";
bool session_info_logged_ = false;
};
template<typename Value, typename ...Args>
std::optional<SessionDB::DBContext> SessionDB::beginWrite(const std::string& table_name, const Value& value, const char* value_type, const Args& ...args)
{
std::ostringstream os;
int rc;
char* zErrMsg = nullptr;
DBContext db_context;
// open DB
auto db = openSessionDB();
if (!db)
{
return std::nullopt;
}
updateSessionInfo(*db);
db_context.table = table_name;
db_context.db = *db;
// first, we delete table (to remove)
os.str("");
os << "DROP TABLE ";
os << table_name;
os << ";";
std::string sql = os.str();
rc = sqlite3_exec(*db, sql.c_str(), NULL, 0, &zErrMsg);
// -- ignore error
// create table
os.str("");
os << "CREATE TABLE ";
os << table_name;
os << " (";
os << "ID INTEGER PRIMARY KEY, ";
beginWrite(os, db_context.columns, value, value_type, args...);
os << "); ";
sql = os.str();
rc = sqlite3_exec(*db, sql.c_str(), NULL, 0, &zErrMsg);
if (rc != SQLITE_OK)
{
logSQLError(zErrMsg, sql);
sqlite3_free(zErrMsg);
return std::nullopt;
}
return db_context;
}
template<typename Value, typename ...Args>
void SessionDB::beginWrite(std::ostringstream& os, std::vector<std::string>& columns, const Value& value, const char* value_type, const Args& ...args)
{
beginWrite(os, columns, value, value_type);
os << ", ";
beginWrite(os, columns, args...);
}
template<typename Value>
void SessionDB::beginWrite(std::ostringstream& os, std::vector<std::string>& columns, const Value& value, const char* value_type)
{
columns.push_back(value);
os << value;
os << " ";
os << value_type;
os << " NOT NULL";
}
template<typename Value, typename ...Args>
void SessionDB::write(SessionDB::DBContext& db_context, const Value& value, const Args& ...args)
{
std::ostringstream os;
int rc;
char* zErrMsg = nullptr;
os << "INSERT INTO ";
os << db_context.table;
os << " (";
for (size_t i = 0; i < db_context.columns.size(); ++i)
{
const auto& column = db_context.columns[i];
os << column;
if (i < (db_context.columns.size() - 1))
{
os << ", ";
}
}
os << ") ";
os << "VALUES (";
write(os, db_context, value, args...);
os << "); ";
std::string sql = os.str();
rc = sqlite3_exec(db_context.db, sql.c_str(), NULL, 0, &zErrMsg);
if (rc != SQLITE_OK) {
logSQLError(zErrMsg, sql);
sqlite3_free(zErrMsg);
}
}
template<typename Value, typename ...Args>
void SessionDB::write(std::ostringstream& os, SessionDB::DBContext& db_context, const Value& value, const Args& ...args)
{
write(os, db_context, value);
os << ", ";
write(os, db_context, args...);
}
template<typename Value>
void SessionDB::write(std::ostringstream& os, SessionDB::DBContext& db_context, const Value& value)
{
os << "'" << value << "'";
}
template<typename Value, typename ...Args>
std::optional<SessionDB::DBContext> SessionDB::beginRead(const std::string& table_name, const Value& value, const Args& ...args)
{
std::ostringstream os;
int rc;
DBContext db_context;
// open DB
auto db = openSessionDB();
if (!db)
{
return std::nullopt;
}
updateSessionInfo(*db);
displaySessionInfo();
db_context.table = table_name;
db_context.db = *db;
os.str("");
os << "SELECT ";
beginRead(os, db_context.columns, value, args...);
os << " FROM ";
os << table_name;
os << "; ";
std::string sql = os.str();
rc = sqlite3_prepare(*db, sql.c_str(), sql.size(), &db_context.stmt, NULL);
if (rc != SQLITE_OK)
{
logSQLError(sqlite3_errmsg(*db), sql);
return std::nullopt;
}
return db_context;
}
template<typename WhereValue, typename Value, typename ...Args>
std::optional<SessionDB::DBContext> SessionDB::beginReadWhere(const std::string& table_name, const std::string& where_id, const WhereValue& where_value, const Value& value, const Args& ...args)
{
std::ostringstream os;
int rc;
DBContext db_context;
// open DB
auto db = openSessionDB();
if (!db)
{
return std::nullopt;
}
updateSessionInfo(*db);
displaySessionInfo();
db_context.table = table_name;
db_context.db = *db;
os.str("");
os << "SELECT ";
beginRead(os, db_context.columns, value, args...);
os << " FROM ";
os << table_name;
os << " ";
os << " WHERE ";
os << where_id << "=" << where_value << "; ";
std::string sql = os.str();
rc = sqlite3_prepare(*db, sql.c_str(), sql.size(), &db_context.stmt, NULL);
if (rc != SQLITE_OK)
{
logSQLError(sqlite3_errmsg(*db), sql);
return std::nullopt;
}
return db_context;
}
template<typename Value, typename ...Args>
void SessionDB::beginRead(std::ostringstream& os, std::vector<std::string>& columns, const Value& value, const Args& ...args)
{
beginRead(os, columns, value);
os << ", ";
beginRead(os, columns, args...);
}
template<typename Value>
void SessionDB::beginRead(std::ostringstream& os, std::vector<std::string>& columns, const Value& value)
{
columns.push_back(value);
os << value;
}
template<typename ...Args>
bool SessionDB::read(SessionDB::DBContext& db_context, Args& ...args)
{
switch (sqlite3_step(db_context.stmt))
{
case SQLITE_ROW:
{
readRowFromDB(db_context, 0, args...);
return true;
}
case SQLITE_DONE:
return false;
default:
logSQLError(sqlite3_errmsg(db_context.db));
return false;
}
}
template<typename Value, typename ...Args>
void SessionDB::readRowFromDB(SessionDB::DBContext& db_context, int column, Value& value, Args& ...args)
{
readRowFromDB(db_context, column++, value);
readRowFromDB(db_context, column++, args...);
}
template <>
void SessionDB::write<std::string>(std::ostringstream& os, SessionDB::DBContext& db_context, const std::string& value);
}