summaryrefslogtreecommitdiff
path: root/src/database
diff options
context:
space:
mode:
Diffstat (limited to 'src/database')
-rw-r--r--src/database/column.hpp6
-rw-r--r--src/database/database.cpp160
-rw-r--r--src/database/database.hpp37
-rw-r--r--src/database/delete_query.hpp33
-rw-r--r--src/database/insert_query.hpp17
-rw-r--r--src/database/postgresql_engine.cpp13
-rw-r--r--src/database/postgresql_engine.hpp3
-rw-r--r--src/database/postgresql_statement.hpp20
-rw-r--r--src/database/query.cpp3
-rw-r--r--src/database/query.hpp7
-rw-r--r--src/database/row.hpp40
-rw-r--r--src/database/save.hpp31
-rw-r--r--src/database/select_query.hpp10
-rw-r--r--src/database/sqlite3_engine.cpp1
-rw-r--r--src/database/sqlite3_engine.hpp3
-rw-r--r--src/database/sqlite3_statement.hpp1
-rw-r--r--src/database/table.hpp11
-rw-r--r--src/database/update_query.hpp11
18 files changed, 312 insertions, 95 deletions
diff --git a/src/database/column.hpp b/src/database/column.hpp
index 1f16bcf..50c9c14 100644
--- a/src/database/column.hpp
+++ b/src/database/column.hpp
@@ -13,10 +13,14 @@ struct Column
T value{};
};
+struct ForeignKey: Column<std::size_t> {
+ static constexpr auto name = "fk_";
+};
+
struct Id: Column<std::size_t> {
static constexpr std::size_t unset_value = static_cast<std::size_t>(-1);
static constexpr auto name = "id_";
static constexpr auto options = "PRIMARY KEY";
- Id(): Column<std::size_t>(-1) {}
+ Id(): Column<std::size_t>(unset_value) {}
};
diff --git a/src/database/database.cpp b/src/database/database.cpp
index 3622963..3b3e890 100644
--- a/src/database/database.cpp
+++ b/src/database/database.cpp
@@ -1,10 +1,12 @@
#include "biboumi.h"
#ifdef USE_DATABASE
+#include <database/select_query.hpp>
+#include <database/save.hpp>
#include <database/database.hpp>
-#include <uuid/uuid.h>
#include <utils/get_first_non_empty.hpp>
#include <utils/time.hpp>
+#include <utils/uuid.hpp>
#include <config/config.hpp>
#include <database/sqlite3_engine.hpp>
@@ -21,6 +23,7 @@ Database::GlobalOptionsTable Database::global_options("globaloptions_");
Database::IrcServerOptionsTable Database::irc_server_options("ircserveroptions_");
Database::IrcChannelOptionsTable Database::irc_channel_options("ircchanneloptions_");
Database::RosterTable Database::roster("roster");
+Database::AfterConnectionCommandsTable Database::after_connection_commands("after_connection_commands_");
std::map<Database::CacheKey, Database::EncodingIn::real_type> Database::encoding_in_cache{};
Database::GlobalPersistent::GlobalPersistent():
@@ -53,57 +56,80 @@ void Database::open(const std::string& filename)
Database::irc_channel_options.upgrade(*Database::db);
Database::roster.create(*Database::db);
Database::roster.upgrade(*Database::db);
+ Database::after_connection_commands.create(*Database::db);
+ Database::after_connection_commands.upgrade(*Database::db);
create_index<Database::Owner, Database::IrcChanName, Database::IrcServerName>(*Database::db, "archive_index", Database::muc_log_lines.get_name());
}
Database::GlobalOptions Database::get_global_options(const std::string& owner)
{
- auto request = Database::global_options.select();
+ auto request = select(Database::global_options);
request.where() << Owner{} << "=" << owner;
- Database::GlobalOptions options{Database::global_options.get_name()};
auto result = request.execute(*Database::db);
if (result.size() == 1)
- options = result.front();
- else
- options.col<Owner>() = owner;
+ return result.front();
+ Database::GlobalOptions options{Database::global_options.get_name()};
+ options.col<Owner>() = owner;
return options;
}
Database::IrcServerOptions Database::get_irc_server_options(const std::string& owner, const std::string& server)
{
- auto request = Database::irc_server_options.select();
+ auto request = select(Database::irc_server_options);
request.where() << Owner{} << "=" << owner << " and " << Server{} << "=" << server;
- Database::IrcServerOptions options{Database::irc_server_options.get_name()};
auto result = request.execute(*Database::db);
if (result.size() == 1)
- options = result.front();
- else
+ return result.front();
+ Database::IrcServerOptions options{Database::irc_server_options.get_name()};
+ options.col<Owner>() = owner;
+ options.col<Server>() = server;
+ return options;
+}
+
+Database::AfterConnectionCommands Database::get_after_connection_commands(const IrcServerOptions& server_options)
+{
+ const auto id = server_options.col<Id>();
+ if (id == Id::unset_value)
+ return {};
+ auto request = select(Database::after_connection_commands);
+ request.where() << ForeignKey{} << "=" << id;
+ return request.execute(*Database::db);
+}
+
+void Database::set_after_connection_commands(const Database::IrcServerOptions& server_options, Database::AfterConnectionCommands& commands)
+{
+ const auto id = server_options.col<Id>();
+ if (id == Id::unset_value)
+ return ;
+
+ Transaction transaction;
+ auto query = Database::after_connection_commands.del();
+ query.where() << ForeignKey{} << "=" << id;
+ query.execute(*Database::db);
+
+ for (auto& command: commands)
{
- options.col<Owner>() = owner;
- options.col<Server>() = server;
+ command.col<ForeignKey>() = server_options.col<Id>();
+ save(command, *Database::db);
}
- return options;
}
Database::IrcChannelOptions Database::get_irc_channel_options(const std::string& owner, const std::string& server, const std::string& channel)
{
- auto request = Database::irc_channel_options.select();
+ auto request = select(Database::irc_channel_options);
request.where() << Owner{} << "=" << owner <<\
" and " << Server{} << "=" << server <<\
" and " << Channel{} << "=" << channel;
- Database::IrcChannelOptions options{Database::irc_channel_options.get_name()};
auto result = request.execute(*Database::db);
if (result.size() == 1)
- options = result.front();
- else
- {
- options.col<Owner>() = owner;
- options.col<Server>() = server;
- options.col<Channel>() = channel;
- }
+ return result.front();
+ Database::IrcChannelOptions options{Database::irc_channel_options.get_name()};
+ options.col<Owner>() = owner;
+ options.col<Server>() = server;
+ options.col<Channel>() = channel;
return options;
}
@@ -159,15 +185,18 @@ std::string Database::store_muc_message(const std::string& owner, const std::str
line.col<Body>() = body;
line.col<Nick>() = nick;
- line.save(Database::db);
+ save(line, *Database::db);
return uuid;
}
std::vector<Database::MucLogLine> Database::get_muc_logs(const std::string& owner, const std::string& chan_name, const std::string& server,
- int limit, const std::string& start, const std::string& end)
+ int limit, const std::string& start, const std::string& end, const Id::real_type reference_record_id, Database::Paging paging)
{
- auto request = Database::muc_log_lines.select();
+ if (limit == 0)
+ return {};
+
+ auto request = select(Database::muc_log_lines);
request.where() << Database::Owner{} << "=" << owner << \
" and " << Database::IrcChanName{} << "=" << chan_name << \
" and " << Database::IrcServerName{} << "=" << server;
@@ -184,15 +213,59 @@ std::vector<Database::MucLogLine> Database::get_muc_logs(const std::string& owne
if (end_time != -1)
request << " and " << Database::Date{} << "<=" << end_time;
}
+ if (reference_record_id != Id::unset_value)
+ {
+ request << " and " << Id{};
+ if (paging == Database::Paging::first)
+ request << ">";
+ else
+ request << "<";
+ request << reference_record_id;
+ }
- request.order_by() << Id{} << " DESC ";
+ if (paging == Database::Paging::first)
+ request.order_by() << Id{} << " ASC ";
+ else
+ request.order_by() << Id{} << " DESC ";
if (limit >= 0)
request.limit() << limit;
auto result = request.execute(*Database::db);
- return {result.crbegin(), result.crend()};
+ if (paging == Database::Paging::first)
+ return result;
+ else
+ return {result.crbegin(), result.crend()};
+}
+
+Database::MucLogLine Database::get_muc_log(const std::string& owner, const std::string& chan_name, const std::string& server,
+ const std::string& uuid, const std::string& start, const std::string& end)
+{
+ auto request = select(Database::muc_log_lines);
+ request.where() << Database::Owner{} << "=" << owner << \
+ " and " << Database::IrcChanName{} << "=" << chan_name << \
+ " and " << Database::IrcServerName{} << "=" << server << \
+ " and " << Database::Uuid{} << "=" << uuid;
+
+ if (!start.empty())
+ {
+ const auto start_time = utils::parse_datetime(start);
+ if (start_time != -1)
+ request << " and " << Database::Date{} << ">=" << start_time;
+ }
+ if (!end.empty())
+ {
+ const auto end_time = utils::parse_datetime(end);
+ if (end_time != -1)
+ request << " and " << Database::Date{} << "<=" << end_time;
+ }
+
+ auto result = request.execute(*Database::db);
+
+ if (result.empty())
+ throw Database::RecordNotFound{};
+ return result.front();
}
void Database::add_roster_item(const std::string& local, const std::string& remote)
@@ -202,7 +275,7 @@ void Database::add_roster_item(const std::string& local, const std::string& remo
roster_item.col<Database::LocalJid>() = local;
roster_item.col<Database::RemoteJid>() = remote;
- roster_item.save(Database::db);
+ save(roster_item, *Database::db);
}
void Database::delete_roster_item(const std::string& local, const std::string& remote)
@@ -216,7 +289,7 @@ void Database::delete_roster_item(const std::string& local, const std::string& r
bool Database::has_roster_item(const std::string& local, const std::string& remote)
{
- auto query = Database::roster.select();
+ auto query = select(Database::roster);
query.where() << Database::LocalJid{} << "=" << local << \
" and " << Database::RemoteJid{} << "=" << remote;
@@ -227,7 +300,7 @@ bool Database::has_roster_item(const std::string& local, const std::string& remo
std::vector<Database::RosterItem> Database::get_contact_list(const std::string& local)
{
- auto query = Database::roster.select();
+ auto query = select(Database::roster);
query.where() << Database::LocalJid{} << "=" << local;
return query.execute(*Database::db);
@@ -235,7 +308,7 @@ std::vector<Database::RosterItem> Database::get_contact_list(const std::string&
std::vector<Database::RosterItem> Database::get_full_roster()
{
- auto query = Database::roster.select();
+ auto query = select(Database::roster);
return query.execute(*Database::db);
}
@@ -247,11 +320,26 @@ void Database::close()
std::string Database::gen_uuid()
{
- char uuid_str[37];
- uuid_t uuid;
- uuid_generate(uuid);
- uuid_unparse(uuid, uuid_str);
- return uuid_str;
+ return utils::gen_uuid();
+}
+
+Transaction::Transaction()
+{
+ const auto result = Database::raw_exec("BEGIN");
+ if (std::get<bool>(result) == false)
+ log_error("Failed to create SQL transaction: ", std::get<std::string>(result));
+ else
+ this->success = true;
+
}
+Transaction::~Transaction()
+{
+ if (this->success)
+ {
+ const auto result = Database::raw_exec("END");
+ if (std::get<bool>(result) == false)
+ log_error("Failed to end SQL transaction: ", std::get<std::string>(result));
+ }
+}
#endif
diff --git a/src/database/database.hpp b/src/database/database.hpp
index ec44543..d986ecc 100644
--- a/src/database/database.hpp
+++ b/src/database/database.hpp
@@ -22,6 +22,8 @@ class Database
{
public:
using time_point = std::chrono::system_clock::time_point;
+ struct RecordNotFound: public std::exception {};
+ enum class Paging { first, last };
struct Uuid: Column<std::string> { static constexpr auto name = "uuid_"; };
@@ -82,6 +84,7 @@ class Database
struct RemoteJid: Column<std::string> { static constexpr auto name = "remote"; };
+ struct Address: Column<std::string> { static constexpr auto name = "address_"; };
using MucLogLineTable = Table<Id, Uuid, Owner, IrcChanName, IrcServerName, Date, Body, Nick>;
using MucLogLine = MucLogLineTable::RowType;
@@ -89,7 +92,7 @@ class Database
using GlobalOptionsTable = Table<Id, Owner, MaxHistoryLength, RecordHistory, GlobalPersistent>;
using GlobalOptions = GlobalOptionsTable::RowType;
- using IrcServerOptionsTable = Table<Id, Owner, Server, Pass, AfterConnectionCommand, TlsPorts, Ports, Username, Realname, VerifyCert, TrustedFingerprint, EncodingOut, EncodingIn, MaxHistoryLength>;
+ using IrcServerOptionsTable = Table<Id, Owner, Server, Pass, TlsPorts, Ports, Username, Realname, VerifyCert, TrustedFingerprint, EncodingOut, EncodingIn, MaxHistoryLength, Address, Nick>;
using IrcServerOptions = IrcServerOptionsTable::RowType;
using IrcChannelOptionsTable = Table<Id, Owner, Server, Channel, EncodingOut, EncodingIn, MaxHistoryLength, Persistent, RecordHistoryOptional>;
@@ -98,6 +101,9 @@ class Database
using RosterTable = Table<LocalJid, RemoteJid>;
using RosterItem = RosterTable::RowType;
+ using AfterConnectionCommandsTable = Table<Id, ForeignKey, AfterConnectionCommand>;
+ using AfterConnectionCommands = std::vector<AfterConnectionCommandsTable::RowType>;
+
Database() = default;
~Database() = default;
@@ -118,8 +124,22 @@ class Database
static IrcChannelOptions get_irc_channel_options_with_server_and_global_default(const std::string& owner,
const std::string& server,
const std::string& channel);
+ static AfterConnectionCommands get_after_connection_commands(const IrcServerOptions& server_options);
+ static void set_after_connection_commands(const IrcServerOptions& server_options, AfterConnectionCommands& commands);
+
+ /**
+ * Get all the lines between (optional) start and end dates, with a (optional) limit.
+ * If after_id is set, only the records after it will be returned.
+ */
static std::vector<MucLogLine> get_muc_logs(const std::string& owner, const std::string& chan_name, const std::string& server,
- int limit=-1, const std::string& start="", const std::string& end="");
+ int limit=-1, const std::string& start="", const std::string& end="",
+ const Id::real_type reference_record_id=Id::unset_value, Paging=Paging::first);
+
+ /**
+ * Get just one single record matching the given uuid, between (optional) end and start.
+ * If it does not exist (or is not between end and start), throw a RecordNotFound exception.
+ */
+ static MucLogLine get_muc_log(const std::string& owner, const std::string& chan_name, const std::string& server, const std::string& uuid, const std::string& start="", const std::string& end="");
static std::string store_muc_message(const std::string& owner, const std::string& chan_name, const std::string& server_name,
time_point date, const std::string& body, const std::string& nick);
@@ -144,6 +164,8 @@ class Database
static IrcServerOptionsTable irc_server_options;
static IrcChannelOptionsTable irc_channel_options;
static RosterTable roster;
+ static AfterConnectionCommandsTable after_connection_commands;
+
static std::unique_ptr<DatabaseEngine> db;
/**
@@ -181,11 +203,20 @@ class Database
static auto raw_exec(const std::string& query)
{
- Database::db->raw_exec(query);
+ return Database::db->raw_exec(query);
}
private:
static std::string gen_uuid();
static std::map<CacheKey, EncodingIn::real_type> encoding_in_cache;
};
+
+class Transaction
+{
+public:
+ Transaction();
+ ~Transaction();
+ bool success{false};
+};
+
#endif /* USE_DATABASE */
diff --git a/src/database/delete_query.hpp b/src/database/delete_query.hpp
new file mode 100644
index 0000000..dce705b
--- /dev/null
+++ b/src/database/delete_query.hpp
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <database/query.hpp>
+#include <database/engine.hpp>
+
+class DeleteQuery: public Query
+{
+public:
+ DeleteQuery(const std::string& name):
+ Query("DELETE")
+ {
+ this->body += " from " + name;
+ }
+
+ DeleteQuery& where()
+ {
+ this->body += " WHERE ";
+ return *this;
+ };
+
+ void execute(DatabaseEngine& db)
+ {
+ auto statement = db.prepare(this->body);
+ if (!statement)
+ return;
+#ifdef DEBUG_SQL_QUERIES
+ const auto timer = this->log_and_time();
+#endif
+ statement->bind(std::move(this->params));
+ if (statement->step() != StepResult::Done)
+ log_error("Failed to execute DELETE command");
+ }
+};
diff --git a/src/database/insert_query.hpp b/src/database/insert_query.hpp
index 9726424..230e873 100644
--- a/src/database/insert_query.hpp
+++ b/src/database/insert_query.hpp
@@ -1,10 +1,15 @@
#pragma once
#include <database/statement.hpp>
+#include <database/database.hpp>
#include <database/column.hpp>
#include <database/query.hpp>
+#include <database/row.hpp>
+
#include <logger/logger.hpp>
+#include <utils/is_one_of.hpp>
+
#include <type_traits>
#include <vector>
#include <string>
@@ -22,7 +27,7 @@ update_autoincrement_id(std::tuple<T...>& columns, Statement& statement)
template <std::size_t N=0, typename... T>
typename std::enable_if<N == sizeof...(T), void>::type
-update_autoincrement_id(std::tuple<T...>&, Statement& statement)
+update_autoincrement_id(std::tuple<T...>&, Statement&)
{}
struct InsertQuery: public Query
@@ -127,3 +132,13 @@ struct InsertQuery: public Query
insert_col_name(const std::tuple<T...>&)
{}
};
+
+template <typename... T>
+void insert(Row<T...>& row, DatabaseEngine& db)
+{
+ InsertQuery query(row.table_name, row.columns);
+ // Ugly workaround for non portable stuff
+ if (is_one_of<Id, T...>)
+ query.body += db.get_returning_id_sql_string(Id::name);
+ query.execute(db, row.columns);
+}
diff --git a/src/database/postgresql_engine.cpp b/src/database/postgresql_engine.cpp
index 984a959..59bc885 100644
--- a/src/database/postgresql_engine.cpp
+++ b/src/database/postgresql_engine.cpp
@@ -11,6 +11,8 @@
#include <logger/logger.hpp>
+#include <cstring>
+
PostgresqlEngine::PostgresqlEngine(PGconn*const conn):
conn(conn)
{}
@@ -20,6 +22,15 @@ PostgresqlEngine::~PostgresqlEngine()
PQfinish(this->conn);
}
+static void logging_notice_processor(void*, const char* original)
+{
+ if (original && std::strlen(original) > 0)
+ {
+ std::string message{original, std::strlen(original) - 1};
+ log_warning("PostgreSQL: ", message);
+ }
+}
+
std::unique_ptr<DatabaseEngine> PostgresqlEngine::open(const std::string& conninfo)
{
PGconn* con = PQconnectdb(conninfo.data());
@@ -34,8 +45,10 @@ std::unique_ptr<DatabaseEngine> PostgresqlEngine::open(const std::string& connin
{
const char* errmsg = PQerrorMessage(con);
log_error("Postgresql connection failed: ", errmsg);
+ PQfinish(con);
throw std::runtime_error("failed to open connection.");
}
+ PQsetNoticeProcessor(con, &logging_notice_processor, nullptr);
return std::make_unique<PostgresqlEngine>(con);
}
diff --git a/src/database/postgresql_engine.hpp b/src/database/postgresql_engine.hpp
index fe4fb53..1a9c249 100644
--- a/src/database/postgresql_engine.hpp
+++ b/src/database/postgresql_engine.hpp
@@ -36,12 +36,15 @@ private:
#else
+using namespace std::string_literals;
+
class PostgresqlEngine
{
public:
static std::unique_ptr<DatabaseEngine> open(const std::string& string)
{
throw std::runtime_error("Cannot open postgresql database "s + string + ": biboumi is not compiled with libpq.");
+ return {};
}
};
diff --git a/src/database/postgresql_statement.hpp b/src/database/postgresql_statement.hpp
index 571c8f1..37e8ea0 100644
--- a/src/database/postgresql_statement.hpp
+++ b/src/database/postgresql_statement.hpp
@@ -6,6 +6,8 @@
#include <libpq-fe.h>
+#include <cstring>
+
class PostgresqlStatement: public Statement
{
public:
@@ -90,7 +92,7 @@ class PostgresqlStatement: public Statement
private:
private:
- bool execute()
+ bool execute(const bool second_attempt=false)
{
std::vector<const char*> params;
params.reserve(this->params.size());
@@ -108,8 +110,20 @@ private:
const auto status = PQresultStatus(this->result);
if (status != PGRES_TUPLES_OK && status != PGRES_COMMAND_OK)
{
- log_error("Failed to execute command: ", PQresultErrorMessage(this->result));
- return false;
+ const char* original = PQerrorMessage(this->conn);
+ if (original && std::strlen(original) > 0)
+ log_error("Failed to execute command: ", std::string{original, std::strlen(original) - 1});
+ if (PQstatus(this->conn) != CONNECTION_OK && !second_attempt)
+ {
+ log_info("Trying to reconnect to PostgreSQL server and execute the query again.");
+ PQreset(this->conn);
+ return this->execute(true);
+ }
+ else
+ {
+ log_error("Givin up.");
+ return false;
+ }
}
return true;
}
diff --git a/src/database/query.cpp b/src/database/query.cpp
index 4054007..d72066e 100644
--- a/src/database/query.cpp
+++ b/src/database/query.cpp
@@ -6,7 +6,7 @@ void actual_bind(Statement& statement, const std::string& value, int index)
statement.bind_text(index, value);
}
-void actual_bind(Statement& statement, const std::size_t value, int index)
+void actual_bind(Statement& statement, const std::int64_t& value, int index)
{
statement.bind_int64(index, value);
}
@@ -21,7 +21,6 @@ void actual_bind(Statement& statement, const OptionalBool& value, int index)
statement.bind_int64(index, -1);
}
-
void actual_add_param(Query& query, const std::string& val)
{
query.params.push_back(val);
diff --git a/src/database/query.hpp b/src/database/query.hpp
index 547138f..ba28b1a 100644
--- a/src/database/query.hpp
+++ b/src/database/query.hpp
@@ -12,7 +12,12 @@
#include <string>
void actual_bind(Statement& statement, const std::string& value, int index);
-void actual_bind(Statement& statement, const std::size_t value, int index);
+void actual_bind(Statement& statement, const std::int64_t& value, int index);
+template <typename T, typename std::enable_if_t<std::is_integral<T>::value>* = 0>
+void actual_bind(Statement& statement, const T& value, int index)
+{
+ actual_bind(statement, static_cast<std::int64_t>(value), index);
+}
void actual_bind(Statement& statement, const OptionalBool& value, int index);
#ifdef DEBUG_SQL_QUERIES
diff --git a/src/database/row.hpp b/src/database/row.hpp
index 2d55897..27caf43 100644
--- a/src/database/row.hpp
+++ b/src/database/row.hpp
@@ -1,9 +1,5 @@
#pragma once
-#include <database/insert_query.hpp>
-#include <database/update_query.hpp>
-#include <logger/logger.hpp>
-
#include <utils/is_one_of.hpp>
#include <type_traits>
@@ -29,43 +25,7 @@ struct Row
return col.value;
}
- template <bool Coucou=true>
- void save(std::unique_ptr<DatabaseEngine>& db, typename std::enable_if<!is_one_of<Id, T...>::value && Coucou>::type* = nullptr)
- {
- this->insert(*db);
- }
-
- template <bool Coucou=true>
- void save(std::unique_ptr<DatabaseEngine>& db, typename std::enable_if<is_one_of<Id, T...>::value && Coucou>::type* = nullptr)
- {
- const Id& id = std::get<Id>(this->columns);
- if (id.value == Id::unset_value)
- {
- this->insert(*db);
- std::get<Id>(this->columns).value = db->last_inserted_rowid;
- }
- else
- this->update(*db);
- }
-
- private:
- void insert(DatabaseEngine& db)
- {
- InsertQuery query(this->table_name, this->columns);
- // Ugly workaround for non portable stuff
- query.body += db.get_returning_id_sql_string(Id::name);
- query.execute(db, this->columns);
- }
-
- void update(DatabaseEngine& db)
- {
- UpdateQuery query(this->table_name, this->columns);
-
- query.execute(db, this->columns);
- }
-
public:
std::tuple<T...> columns;
std::string table_name;
-
};
diff --git a/src/database/save.hpp b/src/database/save.hpp
new file mode 100644
index 0000000..4362110
--- /dev/null
+++ b/src/database/save.hpp
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <database/update_query.hpp>
+#include <database/insert_query.hpp>
+
+#include <database/engine.hpp>
+
+#include <database/row.hpp>
+
+#include <utils/is_one_of.hpp>
+
+template <typename... T, bool Coucou=true>
+void save(Row<T...>& row, DatabaseEngine& db, typename std::enable_if<!is_one_of<Id, T...> && Coucou>::type* = nullptr)
+{
+ insert(row, db);
+}
+
+template <typename... T, bool Coucou=true>
+void save(Row<T...>& row, DatabaseEngine& db, typename std::enable_if<is_one_of<Id, T...> && Coucou>::type* = nullptr)
+{
+ const Id& id = std::get<Id>(row.columns);
+ if (id.value == Id::unset_value)
+ {
+ insert(row, db);
+ if (db.last_inserted_rowid >= 0)
+ std::get<Id>(row.columns).value = static_cast<Id::real_type>(db.last_inserted_rowid);
+ }
+ else
+ update(row, db);
+}
+
diff --git a/src/database/select_query.hpp b/src/database/select_query.hpp
index 5a17f38..b9fdc06 100644
--- a/src/database/select_query.hpp
+++ b/src/database/select_query.hpp
@@ -2,6 +2,8 @@
#include <database/engine.hpp>
+#include <database/table.hpp>
+#include <database/database.hpp>
#include <database/statement.hpp>
#include <database/query.hpp>
#include <logger/logger.hpp>
@@ -115,6 +117,8 @@ struct SelectQuery: public Query
#endif
auto statement = db.prepare(this->body);
+ if (!statement)
+ return rows;
statement->bind(std::move(this->params));
while (statement->step() == StepResult::Row)
@@ -130,3 +134,9 @@ struct SelectQuery: public Query
const std::string table_name;
};
+template <typename... T>
+auto select(const Table<T...> table)
+{
+ SelectQuery<T...> query(table.name);
+ return query;
+}
diff --git a/src/database/sqlite3_engine.cpp b/src/database/sqlite3_engine.cpp
index ae4a146..5e3bba1 100644
--- a/src/database/sqlite3_engine.cpp
+++ b/src/database/sqlite3_engine.cpp
@@ -3,7 +3,6 @@
#ifdef SQLITE3_FOUND
#include <database/sqlite3_engine.hpp>
-
#include <database/sqlite3_statement.hpp>
#include <database/query.hpp>
diff --git a/src/database/sqlite3_engine.hpp b/src/database/sqlite3_engine.hpp
index 5b8176c..a7bfcdb 100644
--- a/src/database/sqlite3_engine.hpp
+++ b/src/database/sqlite3_engine.hpp
@@ -35,12 +35,15 @@ private:
#else
+using namespace std::string_literals;
+
class Sqlite3Engine
{
public:
static std::unique_ptr<DatabaseEngine> open(const std::string& string)
{
throw std::runtime_error("Cannot open sqlite3 database "s + string + ": biboumi is not compiled with sqlite3 lib.");
+ return {};
}
};
diff --git a/src/database/sqlite3_statement.hpp b/src/database/sqlite3_statement.hpp
index 7738fa6..3ed60c0 100644
--- a/src/database/sqlite3_statement.hpp
+++ b/src/database/sqlite3_statement.hpp
@@ -88,5 +88,4 @@ class Sqlite3Statement: public Statement
private:
sqlite3_stmt* stmt;
- int last_step_result{SQLITE_OK};
};
diff --git a/src/database/table.hpp b/src/database/table.hpp
index 680e7cc..0b8bfc0 100644
--- a/src/database/table.hpp
+++ b/src/database/table.hpp
@@ -2,7 +2,7 @@
#include <database/engine.hpp>
-#include <database/select_query.hpp>
+#include <database/delete_query.hpp>
#include <database/row.hpp>
#include <algorithm>
@@ -79,10 +79,10 @@ class Table
return {this->name};
}
- auto select()
+ auto del()
{
- SelectQuery<T...> select(this->name);
- return select;
+ DeleteQuery query(this->name);
+ return query;
}
const std::string& get_name() const
@@ -90,6 +90,8 @@ class Table
return this->name;
}
+ const std::string name;
+
private:
template <std::size_t N=0>
@@ -124,5 +126,4 @@ class Table
add_column_create(DatabaseEngine&, std::string&)
{ }
- const std::string name;
};
diff --git a/src/database/update_query.hpp b/src/database/update_query.hpp
index a29ac3f..c2b819d 100644
--- a/src/database/update_query.hpp
+++ b/src/database/update_query.hpp
@@ -1,7 +1,8 @@
#pragma once
-#include <database/query.hpp>
#include <database/engine.hpp>
+#include <database/query.hpp>
+#include <database/row.hpp>
using namespace std::string_literals;
@@ -102,3 +103,11 @@ struct UpdateQuery: public Query
actual_bind(statement, value.value, sizeof...(T));
}
};
+
+template <typename... T>
+void update(Row<T...>& row, DatabaseEngine& db)
+{
+ UpdateQuery query(row.table_name, row.columns);
+
+ query.execute(db, row.columns);
+}