From 99389eefba1883753c15c6f411fb8543c93f58a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sat, 10 Feb 2018 17:36:33 +0100 Subject: Always return the oldest matching messages from MAM, even if no date is set --- src/database/database.cpp | 25 ++++++++++++++++++++++++- src/database/database.hpp | 9 +++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) (limited to 'src/database') diff --git a/src/database/database.cpp b/src/database/database.cpp index 3622963..c43ace4 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -185,13 +185,36 @@ std::vector Database::get_muc_logs(const std::string& owne request << " and " << Database::Date{} << "<=" << end_time; } + if (limit >= 0) + request.limit() << limit; + + auto result = request.execute(*Database::db); + + return {result.cbegin(), result.cend()}; +} + +std::vector Database::get_muc_most_recent_logs(const std::string& owner, const std::string& chan_name, const std::string& server, + int limit, const std::string& start) +{ + auto request = Database::muc_log_lines.select(); + request.where() << Database::Owner{} << "=" << owner << \ + " and " << Database::IrcChanName{} << "=" << chan_name << \ + " and " << Database::IrcServerName{} << "=" << server; + + if (!start.empty()) + { + const auto start_time = utils::parse_datetime(start); + if (start_time != -1) + request << " and " << Database::Date{} << ">=" << start_time; + } + request.order_by() << Id{} << " DESC "; if (limit >= 0) request.limit() << limit; auto result = request.execute(*Database::db); - + return {result.crbegin(), result.crend()}; } diff --git a/src/database/database.hpp b/src/database/database.hpp index ec44543..f9aed2b 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -118,8 +118,17 @@ 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); + /** + * Get all the lines between (optional) start and end dates, with a (optional) limit. + */ static std::vector 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=""); + + /** + * Get the most recent messages from the archive, with optional limit and start date + */ + static std::vector get_muc_most_recent_logs(const std::string& owner, const std::string& chan_name, const std::string& server, + int limit=-1, const std::string& start=""); 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); -- cgit v1.2.3 From 0280343ced6c520700c3ca508e2d04c6b512d319 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sat, 10 Feb 2018 19:51:59 +0100 Subject: =?UTF-8?q?Handle=20the=20=E2=80=9Cafter=E2=80=9D=20RSM=20value=20?= =?UTF-8?q?to=20page=20through=20results?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/database/database.cpp | 35 ++++++++++++++++++++++++++++++++++- src/database/database.hpp | 9 ++++++++- 2 files changed, 42 insertions(+), 2 deletions(-) (limited to 'src/database') diff --git a/src/database/database.cpp b/src/database/database.cpp index c43ace4..2d6fbbd 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -165,7 +165,7 @@ std::string Database::store_muc_message(const std::string& owner, const std::str } std::vector 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 after_id) { auto request = Database::muc_log_lines.select(); request.where() << Database::Owner{} << "=" << owner << \ @@ -184,6 +184,10 @@ std::vector Database::get_muc_logs(const std::string& owne if (end_time != -1) request << " and " << Database::Date{} << "<=" << end_time; } + if (after_id != Id::unset_value) + { + request << " and " << Id{} << ">" << after_id; + } if (limit >= 0) request.limit() << limit; @@ -218,6 +222,35 @@ std::vector Database::get_muc_most_recent_logs(const std:: 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 = Database::muc_log_lines.select(); + 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) { auto roster_item = Database::roster.row(); diff --git a/src/database/database.hpp b/src/database/database.hpp index f9aed2b..810af16 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -120,15 +120,22 @@ class Database const std::string& channel); /** * 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 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 after_id=Id::unset_value); /** * Get the most recent messages from the archive, with optional limit and start date */ static std::vector get_muc_most_recent_logs(const std::string& owner, const std::string& chan_name, const std::string& server, int limit=-1, const std::string& start=""); + /** + * 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); -- cgit v1.2.3 From d70554143554f1a4ed3d225d30a6e49227f40fc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sun, 11 Feb 2018 23:29:58 +0100 Subject: =?UTF-8?q?Send=20a=20item-not-found=20error=20when=20the=20?= =?UTF-8?q?=E2=80=9Cafter=E2=80=9D=20value=20is=20not=20in=20the=20archive?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/database/database.hpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/database') diff --git a/src/database/database.hpp b/src/database/database.hpp index 810af16..fb62c34 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -22,6 +22,7 @@ class Database { public: using time_point = std::chrono::system_clock::time_point; + struct RecordNotFound: public std::exception {}; struct Uuid: Column { static constexpr auto name = "uuid_"; }; -- cgit v1.2.3 From fd9c7139386e773ee64fe970089d77fede75181f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sun, 11 Feb 2018 23:39:42 +0100 Subject: Fix a few warnings --- src/database/column.hpp | 2 +- src/database/insert_query.hpp | 2 +- src/database/query.cpp | 9 +++++++++ src/database/query.hpp | 2 ++ src/database/sqlite3_statement.hpp | 1 - 5 files changed, 13 insertions(+), 3 deletions(-) (limited to 'src/database') diff --git a/src/database/column.hpp b/src/database/column.hpp index 1f16bcf..9367701 100644 --- a/src/database/column.hpp +++ b/src/database/column.hpp @@ -18,5 +18,5 @@ struct Id: Column { static constexpr auto name = "id_"; static constexpr auto options = "PRIMARY KEY"; - Id(): Column(-1) {} + Id(): Column(unset_value) {} }; diff --git a/src/database/insert_query.hpp b/src/database/insert_query.hpp index 9726424..04c098c 100644 --- a/src/database/insert_query.hpp +++ b/src/database/insert_query.hpp @@ -22,7 +22,7 @@ update_autoincrement_id(std::tuple& columns, Statement& statement) template typename std::enable_if::type -update_autoincrement_id(std::tuple&, Statement& statement) +update_autoincrement_id(std::tuple&, Statement&) {} struct InsertQuery: public Query diff --git a/src/database/query.cpp b/src/database/query.cpp index d27dc59..9611c97 100644 --- a/src/database/query.cpp +++ b/src/database/query.cpp @@ -21,6 +21,15 @@ void actual_bind(Statement& statement, const OptionalBool& value, int index) statement.bind_int64(index, -1); } +void actual_bind(Statement& statement, const std::size_t value, int index) +{ + actual_bind(statement, static_cast(value), index); +} + +void actual_bind(Statement& statement, const int value, int index) +{ + actual_bind(statement, static_cast(value), index); +} void actual_add_param(Query& query, const std::string& val) { diff --git a/src/database/query.hpp b/src/database/query.hpp index 8434944..25c3a62 100644 --- a/src/database/query.hpp +++ b/src/database/query.hpp @@ -13,6 +13,8 @@ void actual_bind(Statement& statement, const std::string& value, int index); void actual_bind(Statement& statement, const std::int64_t value, int index); +void actual_bind(Statement& statement, const std::size_t value, int index); +void actual_bind(Statement& statement, const int value, int index); void actual_bind(Statement& statement, const OptionalBool& value, int index); #ifdef DEBUG_SQL_QUERIES 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}; }; -- cgit v1.2.3 From fd7c365288b9c4db1d441b553d42b26e81715b36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 13 Feb 2018 03:10:00 +0100 Subject: Use the same function for both history orders --- src/database/database.cpp | 40 +++++++++++++--------------------------- src/database/database.hpp | 8 ++------ 2 files changed, 15 insertions(+), 33 deletions(-) (limited to 'src/database') diff --git a/src/database/database.cpp b/src/database/database.cpp index 2d6fbbd..2a63a6b 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -165,8 +165,11 @@ std::string Database::store_muc_message(const std::string& owner, const std::str } std::vector 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, const Id::real_type after_id) + int limit, const std::string& start, const std::string& end, const Id::real_type after_id, Database::Paging paging) { + if (limit == 0) + return {}; + auto request = Database::muc_log_lines.select(); request.where() << Database::Owner{} << "=" << owner << \ " and " << Database::IrcChanName{} << "=" << chan_name << \ @@ -189,37 +192,20 @@ std::vector Database::get_muc_logs(const std::string& owne request << " and " << Id{} << ">" << after_id; } - if (limit >= 0) - request.limit() << limit; - - auto result = request.execute(*Database::db); - - return {result.cbegin(), result.cend()}; -} - -std::vector Database::get_muc_most_recent_logs(const std::string& owner, const std::string& chan_name, const std::string& server, - int limit, const std::string& start) -{ - auto request = Database::muc_log_lines.select(); - request.where() << Database::Owner{} << "=" << owner << \ - " and " << Database::IrcChanName{} << "=" << chan_name << \ - " and " << Database::IrcServerName{} << "=" << server; - - if (!start.empty()) - { - const auto start_time = utils::parse_datetime(start); - if (start_time != -1) - request << " and " << Database::Date{} << ">=" << start_time; - } - - 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, diff --git a/src/database/database.hpp b/src/database/database.hpp index fb62c34..9c483f4 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -23,6 +23,7 @@ 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 { static constexpr auto name = "uuid_"; }; @@ -125,13 +126,8 @@ class Database */ static std::vector 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="", - const Id::real_type after_id=Id::unset_value); + const Id::real_type after_id=Id::unset_value, Paging=Paging::first); - /** - * Get the most recent messages from the archive, with optional limit and start date - */ - static std::vector get_muc_most_recent_logs(const std::string& owner, const std::string& chan_name, const std::string& server, - int limit=-1, const std::string& start=""); /** * 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. -- cgit v1.2.3 From 4a2a280d76e45e165d5c4657f4a46eebf71594bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 13 Feb 2018 03:51:03 +0100 Subject: Support the element in MAM requests --- src/database/database.cpp | 16 +++++++++++----- src/database/database.hpp | 2 +- 2 files changed, 12 insertions(+), 6 deletions(-) (limited to 'src/database') diff --git a/src/database/database.cpp b/src/database/database.cpp index 2a63a6b..b2413d0 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -165,7 +165,7 @@ std::string Database::store_muc_message(const std::string& owner, const std::str } std::vector 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, const Id::real_type after_id, Database::Paging paging) + int limit, const std::string& start, const std::string& end, const Id::real_type reference_record_id, Database::Paging paging) { if (limit == 0) return {}; @@ -187,15 +187,21 @@ std::vector Database::get_muc_logs(const std::string& owne if (end_time != -1) request << " and " << Database::Date{} << "<=" << end_time; } - if (after_id != Id::unset_value) + if (reference_record_id != Id::unset_value) { - request << " and " << Id{} << ">" << after_id; + request << " and " << Id{}; + if (paging == Database::Paging::first) + request << ">"; + else + request << "<"; + request << reference_record_id; } + request.order_by() << Id{}; if (paging == Database::Paging::first) - request.order_by() << Id{} << " ASC "; + request << " ASC "; else - request.order_by() << Id{} << " DESC "; + request << " DESC "; if (limit >= 0) request.limit() << limit; diff --git a/src/database/database.hpp b/src/database/database.hpp index 9c483f4..ce7595b 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -126,7 +126,7 @@ class Database */ static std::vector 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="", - const Id::real_type after_id=Id::unset_value, Paging=Paging::first); + 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. -- cgit v1.2.3 From 505deda1763137fd807e16586c8a2e1aa978c647 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 14 Feb 2018 23:25:12 +0100 Subject: Fix a memory leak when psql connection fails --- src/database/postgresql_engine.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/database') diff --git a/src/database/postgresql_engine.cpp b/src/database/postgresql_engine.cpp index 984a959..518c23a 100644 --- a/src/database/postgresql_engine.cpp +++ b/src/database/postgresql_engine.cpp @@ -34,6 +34,7 @@ std::unique_ptr 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."); } return std::make_unique(con); -- cgit v1.2.3 From dbb86bcc12921576c270009537b81b951d2ed84f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Thu, 15 Feb 2018 16:54:54 +0100 Subject: Fix the actual_bind versions for integrals --- src/database/query.cpp | 12 +----------- src/database/query.hpp | 9 ++++++--- 2 files changed, 7 insertions(+), 14 deletions(-) (limited to 'src/database') diff --git a/src/database/query.cpp b/src/database/query.cpp index 9611c97..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::int64_t value, int index) +void actual_bind(Statement& statement, const std::int64_t& value, int index) { statement.bind_int64(index, value); } @@ -21,16 +21,6 @@ void actual_bind(Statement& statement, const OptionalBool& value, int index) statement.bind_int64(index, -1); } -void actual_bind(Statement& statement, const std::size_t value, int index) -{ - actual_bind(statement, static_cast(value), index); -} - -void actual_bind(Statement& statement, const int value, int index) -{ - actual_bind(statement, static_cast(value), index); -} - 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 25c3a62..ba28b1a 100644 --- a/src/database/query.hpp +++ b/src/database/query.hpp @@ -12,9 +12,12 @@ #include void actual_bind(Statement& statement, const std::string& value, int index); -void actual_bind(Statement& statement, const std::int64_t value, int index); -void actual_bind(Statement& statement, const std::size_t value, int index); -void actual_bind(Statement& statement, const int value, int index); +void actual_bind(Statement& statement, const std::int64_t& value, int index); +template ::value>* = 0> +void actual_bind(Statement& statement, const T& value, int index) +{ + actual_bind(statement, static_cast(value), index); +} void actual_bind(Statement& statement, const OptionalBool& value, int index); #ifdef DEBUG_SQL_QUERIES -- cgit v1.2.3 From d3e07eee1335822643d7086b95590b60a4e002c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 16 Feb 2018 00:56:07 +0100 Subject: Sort archive messages by date (and only then by id) fix #3337 Because apparently the IDs are not always incrementing. We still use them to know the order of two messages that were received at the same second (in this case, the ID will always be incrementing). --- src/database/database.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/database') diff --git a/src/database/database.cpp b/src/database/database.cpp index b2413d0..d19ed7a 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -197,11 +197,10 @@ std::vector Database::get_muc_logs(const std::string& owne request << reference_record_id; } - request.order_by() << Id{}; if (paging == Database::Paging::first) - request << " ASC "; + request.order_by() << Database::Date{} << " ASC, " << Id{} << " ASC "; else - request << " DESC "; + request.order_by() << Database::Date{} << " DESC, " << Id{} << " DESC "; if (limit >= 0) request.limit() << limit; -- cgit v1.2.3 From b1750843008b7928c72699c592776fb56c66ec26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 16 Feb 2018 00:57:49 +0100 Subject: Log the warn/error messages coming from libpq in a correct format --- src/database/postgresql_engine.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src/database') diff --git a/src/database/postgresql_engine.cpp b/src/database/postgresql_engine.cpp index 518c23a..59bc885 100644 --- a/src/database/postgresql_engine.cpp +++ b/src/database/postgresql_engine.cpp @@ -11,6 +11,8 @@ #include +#include + 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 PostgresqlEngine::open(const std::string& conninfo) { PGconn* con = PQconnectdb(conninfo.data()); @@ -37,6 +48,7 @@ std::unique_ptr PostgresqlEngine::open(const std::string& connin PQfinish(con); throw std::runtime_error("failed to open connection."); } + PQsetNoticeProcessor(con, &logging_notice_processor, nullptr); return std::make_unique(con); } -- cgit v1.2.3 From 8503d1f6831fc8522a16e0670578d1157d23e116 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 16 Feb 2018 00:58:49 +0100 Subject: Actually display the error message from postgresql when a query failed --- src/database/postgresql_statement.hpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/database') diff --git a/src/database/postgresql_statement.hpp b/src/database/postgresql_statement.hpp index 571c8f1..5665aed 100644 --- a/src/database/postgresql_statement.hpp +++ b/src/database/postgresql_statement.hpp @@ -6,6 +6,8 @@ #include +#include + class PostgresqlStatement: public Statement { public: @@ -108,7 +110,9 @@ 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)); + 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}); return false; } return true; -- cgit v1.2.3 From 9e4a3e2b054ee4604b6a42c73895216c68fa96e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Mon, 12 Mar 2018 00:17:03 +0100 Subject: =?UTF-8?q?Fix=20the=20INSERT=20query=20for=20types=20that=20don?= =?UTF-8?q?=E2=80=99t=20have=20any=20Id=20column?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/database/row.hpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'src/database') diff --git a/src/database/row.hpp b/src/database/row.hpp index 4dc98be..3703ff7 100644 --- a/src/database/row.hpp +++ b/src/database/row.hpp @@ -50,7 +50,8 @@ struct Row } private: - void insert(DatabaseEngine& db) + template + void insert(DatabaseEngine& db, typename std::enable_if && Coucou>::type* = nullptr) { InsertQuery query(this->table_name, this->columns); // Ugly workaround for non portable stuff @@ -58,6 +59,13 @@ struct Row query.execute(db, this->columns); } + template + void insert(DatabaseEngine& db, typename std::enable_if && Coucou>::type* = nullptr) + { + InsertQuery query(this->table_name, this->columns); + query.execute(db, this->columns); + } + void update(DatabaseEngine& db) { UpdateQuery query(this->table_name, this->columns); -- cgit v1.2.3 From bb476f4a0d60bbc41393a86a370aa94ab405b081 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Mon, 12 Mar 2018 01:00:17 +0100 Subject: Allow to override the addresses used to connect to an IRC network fix #3273 --- src/database/database.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/database') diff --git a/src/database/database.hpp b/src/database/database.hpp index ce7595b..79b16d6 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -84,6 +84,7 @@ class Database struct RemoteJid: Column { static constexpr auto name = "remote"; }; + struct Address: Column { static constexpr auto name = "hostname_"; }; using MucLogLineTable = Table; using MucLogLine = MucLogLineTable::RowType; @@ -91,7 +92,7 @@ class Database using GlobalOptionsTable = Table; using GlobalOptions = GlobalOptionsTable::RowType; - using IrcServerOptionsTable = Table; + using IrcServerOptionsTable = Table; using IrcServerOptions = IrcServerOptionsTable::RowType; using IrcChannelOptionsTable = Table; -- cgit v1.2.3 From b3b90139dbb0a09988477fdab4f19d717ac05cab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Mon, 12 Mar 2018 01:06:15 +0100 Subject: =?UTF-8?q?Rename=20a=20few=20=E2=80=9Chostname=E2=80=9D=20into=20?= =?UTF-8?q?=E2=80=9Caddress=E2=80=9D,=20regarding=20last=20commit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #3273 --- src/database/database.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/database') diff --git a/src/database/database.hpp b/src/database/database.hpp index 79b16d6..8a967d8 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -84,7 +84,7 @@ class Database struct RemoteJid: Column { static constexpr auto name = "remote"; }; - struct Address: Column { static constexpr auto name = "hostname_"; }; + struct Address: Column { static constexpr auto name = "address_"; }; using MucLogLineTable = Table; using MucLogLine = MucLogLineTable::RowType; -- cgit v1.2.3 From ba879a882e031d7b8503f78fe41d1210000c96ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 16 Mar 2018 00:53:47 +0100 Subject: Use std::optional instead of OptionalBool --- src/database/database.hpp | 2 +- src/database/query.cpp | 12 ++++++------ src/database/query.hpp | 4 ++-- src/database/select_query.hpp | 9 ++++----- 4 files changed, 13 insertions(+), 14 deletions(-) (limited to 'src/database') diff --git a/src/database/database.hpp b/src/database/database.hpp index 8a967d8..03d82ae 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -69,7 +69,7 @@ class Database struct RecordHistory: Column { static constexpr auto name = "recordhistory_"; RecordHistory(): Column(true) {}}; - struct RecordHistoryOptional: Column { static constexpr auto name = "recordhistory_"; }; + struct RecordHistoryOptional: Column> { static constexpr auto name = "recordhistory_"; }; struct VerifyCert: Column { static constexpr auto name = "verifycert_"; VerifyCert(): Column(true) {} }; diff --git a/src/database/query.cpp b/src/database/query.cpp index d72066e..13c881b 100644 --- a/src/database/query.cpp +++ b/src/database/query.cpp @@ -11,11 +11,11 @@ void actual_bind(Statement& statement, const std::int64_t& value, int index) statement.bind_int64(index, value); } -void actual_bind(Statement& statement, const OptionalBool& value, int index) +void actual_bind(Statement& statement, const std::optional& value, int index) { - if (!value.is_set) + if (!value) statement.bind_int64(index, 0); - else if (value.value) + else if (*value) statement.bind_int64(index, 1); else statement.bind_int64(index, -1); @@ -26,11 +26,11 @@ void actual_add_param(Query& query, const std::string& val) query.params.push_back(val); } -void actual_add_param(Query& query, const OptionalBool& val) +void actual_add_param(Query& query, const std::optional& val) { - if (!val.is_set) + if (!val) query.params.push_back("0"); - else if (val.value) + else if (*val) query.params.push_back("1"); else query.params.push_back("-1"); diff --git a/src/database/query.hpp b/src/database/query.hpp index ba28b1a..1c4a5ff 100644 --- a/src/database/query.hpp +++ b/src/database/query.hpp @@ -18,7 +18,7 @@ void actual_bind(Statement& statement, const T& value, int index) { actual_bind(statement, static_cast(value), index); } -void actual_bind(Statement& statement, const OptionalBool& value, int index); +void actual_bind(Statement& statement, const std::optional& value, int index); #ifdef DEBUG_SQL_QUERIES #include @@ -71,7 +71,6 @@ void actual_add_param(Query& query, const T& val) } void actual_add_param(Query& query, const std::string& val); -void actual_add_param(Query& query, const OptionalBool& val); template typename std::enable_if::value, Query&>::type @@ -80,6 +79,7 @@ operator<<(Query& query, const T&) query.body += T::name; return query; } +void actual_add_param(Query& query, const std::optional& val); Query& operator<<(Query& query, const char* str); Query& operator<<(Query& query, const std::string& str); diff --git a/src/database/select_query.hpp b/src/database/select_query.hpp index 5a17f38..cd9943c 100644 --- a/src/database/select_query.hpp +++ b/src/database/select_query.hpp @@ -29,16 +29,15 @@ extract_row_value(Statement& statement, const int i) } template -typename std::enable_if::value, T>::type +typename std::enable_if, T>::value, T>::type extract_row_value(Statement& statement, const int i) { const auto integer = statement.get_column_int(i); - OptionalBool result; if (integer > 0) - result.set_value(true); + return true; else if (integer < 0) - result.set_value(false); - return result; + return false; + return std::nullopt; } template -- cgit v1.2.3 From d62ca9f87906be6f046fe9d07afb8bfb69c166e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 16 Mar 2018 01:11:47 +0100 Subject: Use if constexpr to make things a lot more readable --- src/database/index.hpp | 21 +++++----- src/database/insert_query.hpp | 91 +++++++++++++++++++------------------------ src/database/query.hpp | 41 +++++++------------ src/database/row.hpp | 52 ++++++++++++------------- src/database/select_query.hpp | 77 +++++++++++++++--------------------- src/database/table.hpp | 44 ++++++++++----------- src/database/update_query.hpp | 51 +++++++++++------------- 7 files changed, 164 insertions(+), 213 deletions(-) (limited to 'src/database') diff --git a/src/database/index.hpp b/src/database/index.hpp index 30766ab..094a36a 100644 --- a/src/database/index.hpp +++ b/src/database/index.hpp @@ -8,19 +8,16 @@ namespace { template -typename std::enable_if::type -add_column_name(std::string&) -{ } - -template -typename std::enable_if::type -add_column_name(std::string& out) +void add_column_name(std::string& out) { - using ColumnType = typename std::remove_reference(std::declval>()))>::type; - out += ColumnType::name; - if (N != sizeof...(T) - 1) - out += ","; - add_column_name(out); + if constexpr(N < sizeof...(T)) + { + using ColumnType = typename std::remove_reference(std::declval>()))>::type; + out += ColumnType::name; + if (N != sizeof...(T) - 1) + out += ","; + add_column_name(out); + } } } diff --git a/src/database/insert_query.hpp b/src/database/insert_query.hpp index 04c098c..ed1944f 100644 --- a/src/database/insert_query.hpp +++ b/src/database/insert_query.hpp @@ -11,20 +11,17 @@ #include template -typename std::enable_if::type -update_autoincrement_id(std::tuple& columns, Statement& statement) +void update_autoincrement_id(std::tuple& columns, Statement& statement) { - using ColumnType = typename std::decay(columns))>::type; - if (std::is_same::value) - auto&& column = std::get(columns); - update_autoincrement_id(columns, statement); + if constexpr(N < sizeof...(T)) + { + using ColumnType = typename std::decay(columns))>::type; + if (std::is_same::value) + auto&& column = std::get(columns); + update_autoincrement_id(columns, statement); + } } -template -typename std::enable_if::type -update_autoincrement_id(std::tuple&, Statement&) -{} - struct InsertQuery: public Query { template @@ -53,23 +50,20 @@ struct InsertQuery: public Query } template - typename std::enable_if::type - bind_param(const std::tuple& columns, Statement& statement, int index=1) + void bind_param(const std::tuple& columns, Statement& statement, int index=1) { - auto&& column = std::get(columns); - using ColumnType = std::decay_t; + if constexpr(N < sizeof...(T)) + { + auto&& column = std::get(columns); + using ColumnType = std::decay_t; - if (!std::is_same::value) - actual_bind(statement, column.value, index++); + if constexpr(!std::is_same::value) + actual_bind(statement, column.value, index++); - this->bind_param(columns, statement, index); + this->bind_param(columns, statement, index); + } } - template - typename std::enable_if::type - bind_param(const std::tuple&, Statement&, int) - {} - template void insert_values(const std::tuple& columns) { @@ -79,23 +73,21 @@ struct InsertQuery: public Query } template - typename std::enable_if::type - insert_value(const std::tuple& columns, int index=1) + void insert_value(const std::tuple& columns, int index=1) { - using ColumnType = std::decay_t(columns))>; - - if (!std::is_same::value) + if constexpr(N < sizeof...(T)) { - this->body += "$" + std::to_string(index++); - if (N != sizeof...(T) - 1) - this->body += ", "; + using ColumnType = std::decay_t(columns))>; + + if (!std::is_same::value) + { + this->body += "$" + std::to_string(index++); + if (N != sizeof...(T) - 1) + this->body += ", "; + } + this->insert_value(columns, index); } - this->insert_value(columns, index); } - template - typename std::enable_if::type - insert_value(const std::tuple&, const int) - { } template void insert_col_names(const std::tuple& columns) @@ -106,24 +98,21 @@ struct InsertQuery: public Query } template - typename std::enable_if::type - insert_col_name(const std::tuple& columns) + void insert_col_name(const std::tuple& columns) { - using ColumnType = std::decay_t(columns))>; - - if (!std::is_same::value) + if constexpr(N < sizeof...(T)) { - this->body += ColumnType::name; + using ColumnType = std::decay_t(columns))>; - if (N < (sizeof...(T) - 1)) - this->body += ", "; - } + if (!std::is_same::value) + { + this->body += ColumnType::name; - this->insert_col_name(columns); - } + if (N < (sizeof...(T) - 1)) + this->body += ", "; + } - template - typename std::enable_if::type - insert_col_name(const std::tuple&) - {} + this->insert_col_name(columns); + } + } }; diff --git a/src/database/query.hpp b/src/database/query.hpp index 1c4a5ff..2a2d2d4 100644 --- a/src/database/query.hpp +++ b/src/database/query.hpp @@ -13,12 +13,12 @@ void actual_bind(Statement& statement, const std::string& value, int index); void actual_bind(Statement& statement, const std::int64_t& value, int index); -template ::value>* = 0> +void actual_bind(Statement& statement, const std::optional& value, int index); +template void actual_bind(Statement& statement, const T& value, int index) { actual_bind(statement, static_cast(value), index); } -void actual_bind(Statement& statement, const std::optional& value, int index); #ifdef DEBUG_SQL_QUERIES #include @@ -57,38 +57,27 @@ struct Query #endif }; -template -void add_param(Query& query, const ColumnType& column) -{ - std::cout << "add_param" << std::endl; - actual_add_param(query, column.value); -} - +void actual_add_param(Query& query, const std::string& val); +void actual_add_param(Query& query, const std::optional& val); template void actual_add_param(Query& query, const T& val) { query.params.push_back(std::to_string(val)); } -void actual_add_param(Query& query, const std::string& val); - -template -typename std::enable_if::value, Query&>::type -operator<<(Query& query, const T&) -{ - query.body += T::name; - return query; -} -void actual_add_param(Query& query, const std::optional& val); - Query& operator<<(Query& query, const char* str); Query& operator<<(Query& query, const std::string& str); -template -typename std::enable_if::value, Query&>::type -operator<<(Query& query, const Integer& i) +template +Query& operator<<(Query& query, const T& i) { - query.body += "$" + std::to_string(query.current_param++); - actual_add_param(query, i); + if constexpr(std::is_integral::value) + { + query.body += "$" + std::to_string(query.current_param++); + actual_add_param(query, i); + } + else + { + query.body += T::name; + } return query; } - diff --git a/src/database/row.hpp b/src/database/row.hpp index 3703ff7..194a9c5 100644 --- a/src/database/row.hpp +++ b/src/database/row.hpp @@ -29,41 +29,39 @@ struct Row return col.value; } - template - void save(std::unique_ptr& db, typename std::enable_if && Coucou>::type* = nullptr) + void save(std::unique_ptr& db) { - this->insert(*db); - } - - template - void save(std::unique_ptr& db, typename std::enable_if && Coucou>::type* = nullptr) - { - const Id& id = std::get(this->columns); - if (id.value == Id::unset_value) + if constexpr(is_one_of) { - this->insert(*db); - if (db->last_inserted_rowid >= 0) - std::get(this->columns).value = static_cast(db->last_inserted_rowid); + const Id& id = std::get(this->columns); + if (id.value == Id::unset_value) + { + this->insert(*db); + if (db->last_inserted_rowid >= 0) + std::get(this->columns).value = static_cast(db->last_inserted_rowid); + } + else + this->update(*db); } else - this->update(*db); + this->insert(*db); } private: - template - void insert(DatabaseEngine& db, typename std::enable_if && Coucou>::type* = nullptr) - { - 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); - } - - template - void insert(DatabaseEngine& db, typename std::enable_if && Coucou>::type* = nullptr) + void insert(DatabaseEngine& db) { - InsertQuery query(this->table_name, this->columns); - query.execute(db, this->columns); + if constexpr(is_one_of) + { + 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); + } + else + { + InsertQuery query(this->table_name, this->columns); + query.execute(db, this->columns); + } } void update(DatabaseEngine& db) diff --git a/src/database/select_query.hpp b/src/database/select_query.hpp index cd9943c..f7496a4 100644 --- a/src/database/select_query.hpp +++ b/src/database/select_query.hpp @@ -15,48 +15,37 @@ using namespace std::string_literals; template -typename std::enable_if::value, std::int64_t>::type -extract_row_value(Statement& statement, const int i) +auto extract_row_value(Statement& statement, const int i) { - return statement.get_column_int64(i); -} - -template -typename std::enable_if::value, T>::type -extract_row_value(Statement& statement, const int i) -{ - return statement.get_column_text(i); -} - -template -typename std::enable_if, T>::value, T>::type -extract_row_value(Statement& statement, const int i) -{ - const auto integer = statement.get_column_int(i); - if (integer > 0) - return true; - else if (integer < 0) - return false; - return std::nullopt; + if constexpr(std::is_integral::value) + return statement.get_column_int64(i); + else if constexpr (std::is_same::value) + return statement.get_column_text(i); + else if (std::is_same, T>::value) + { + const auto integer = statement.get_column_int(i); + if (integer > 0) + return std::optional{true}; + else if (integer < 0) + return std::optional{false}; + return std::optional{}; + } } template -typename std::enable_if::type -extract_row_values(Row& row, Statement& statement) +void extract_row_values(Row& row, Statement& statement) { - using ColumnType = typename std::remove_reference(row.columns))>::type; + if constexpr(N < sizeof...(T)) + { + using ColumnType = typename std::remove_reference(row.columns))>::type; - auto&& column = std::get(row.columns); - column.value = static_cast(extract_row_value(statement, N)); + auto&& column = std::get(row.columns); + column.value = static_cast(extract_row_value(statement, N)); - extract_row_values(row, statement); + extract_row_values(row, statement); + } } -template -typename std::enable_if::type -extract_row_values(Row&, Statement&) -{} - template struct SelectQuery: public Query { @@ -69,23 +58,21 @@ struct SelectQuery: public Query } template - typename std::enable_if::type - insert_col_name() + void insert_col_name() { - using ColumnsType = std::tuple; - using ColumnType = typename std::remove_reference(std::declval()))>::type; + if constexpr(N < sizeof...(T)) + { + using ColumnsType = std::tuple; + using ColumnType = typename std::remove_reference(std::declval()))>::type; - this->body += " " + std::string{ColumnType::name}; + this->body += " " + std::string{ColumnType::name}; - if (N < (sizeof...(T) - 1)) - this->body += ", "; + if (N < (sizeof...(T) - 1)) + this->body += ", "; - this->insert_col_name(); + this->insert_col_name(); + } } - template - typename std::enable_if::type - insert_col_name() - {} SelectQuery& where() { diff --git a/src/database/table.hpp b/src/database/table.hpp index 680e7cc..eb68418 100644 --- a/src/database/table.hpp +++ b/src/database/table.hpp @@ -93,36 +93,32 @@ class Table private: template - typename std::enable_if::type - add_column_if_not_exists(DatabaseEngine& db, const std::set& existing_columns) + void add_column_if_not_exists(DatabaseEngine& db, const std::set& existing_columns) { - using ColumnType = typename std::remove_reference(std::declval()))>::type; - if (existing_columns.count(ColumnType::name) == 0) - add_column_to_table(db, this->name); - add_column_if_not_exists(db, existing_columns); + if constexpr(N < sizeof...(T)) + { + using ColumnType = typename std::remove_reference(std::declval()))>::type; + if (existing_columns.count(ColumnType::name) == 0) + add_column_to_table(db, this->name); + add_column_if_not_exists(db, existing_columns); + } } - template - typename std::enable_if::type - add_column_if_not_exists(DatabaseEngine&, const std::set&) - {} template - typename std::enable_if::type - add_column_create(DatabaseEngine& db, std::string& str) + void add_column_create(DatabaseEngine& db, std::string& str) { - using ColumnType = typename std::remove_reference(std::declval()))>::type; - str += ColumnType::name; - str += " "; - str += ToSQLType(db); - if (N != sizeof...(T) - 1) - str += ","; - - add_column_create(db, str); + if constexpr(N < sizeof...(T)) + { + using ColumnType = typename std::remove_reference(std::declval()))>::type; + str += ColumnType::name; + str += " "; + str += ToSQLType(db); + if (N != sizeof...(T) - 1) + str += ","; + + add_column_create(db, str); + } } - template - typename std::enable_if::type - 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..0ee2bd9 100644 --- a/src/database/update_query.hpp +++ b/src/database/update_query.hpp @@ -39,27 +39,25 @@ struct UpdateQuery: public Query } template - typename std::enable_if::type - insert_col_name_and_value(const std::tuple& columns) + void insert_col_name_and_value(const std::tuple& columns) { - using ColumnType = std::decay_t(columns))>; - - if (!std::is_same::value) + if constexpr(N < sizeof...(T)) { - this->body += ColumnType::name + "=$"s + std::to_string(this->current_param); - this->current_param++; + using ColumnType = std::decay_t(columns))>; - if (N < (sizeof...(T) - 1)) - this->body += ", "; - } + if (!std::is_same::value) + { + this->body += ColumnType::name + "=$"s + + std::to_string(this->current_param); + this->current_param++; - this->insert_col_name_and_value(columns); - } - template - typename std::enable_if::type - insert_col_name_and_value(const std::tuple&) - {} + if (N < (sizeof...(T) - 1)) + this->body += ", "; + } + this->insert_col_name_and_value(columns); + } + } template void execute(DatabaseEngine& db, const std::tuple& columns) @@ -76,23 +74,20 @@ struct UpdateQuery: public Query } template - typename std::enable_if::type - bind_param(const std::tuple& columns, Statement& statement, int index=1) + void bind_param(const std::tuple& columns, Statement& statement, int index=1) { - auto&& column = std::get(columns); - using ColumnType = std::decay_t; + if constexpr(N < sizeof...(T)) + { + auto&& column = std::get(columns); + using ColumnType = std::decay_t; - if (!std::is_same::value) - actual_bind(statement, column.value, index++); + if (!std::is_same::value) + actual_bind(statement, column.value, index++); - this->bind_param(columns, statement, index); + this->bind_param(columns, statement, index); + } } - template - typename std::enable_if::type - bind_param(const std::tuple&, Statement&, int) - {} - template void bind_id(const std::tuple& columns, Statement& statement) { -- cgit v1.2.3 From d0e3c71b91f1a1c1780158789fd42b8ac7209495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sat, 17 Mar 2018 17:28:41 +0100 Subject: Revert "Use if constexpr to make things a lot more readable" This reverts commit d62ca9f87906be6f046fe9d07afb8bfb69c166e3. --- src/database/index.hpp | 21 +++++----- src/database/insert_query.hpp | 91 ++++++++++++++++++++++++------------------- src/database/query.hpp | 41 ++++++++++++------- src/database/row.hpp | 52 +++++++++++++------------ src/database/select_query.hpp | 77 +++++++++++++++++++++--------------- src/database/table.hpp | 44 +++++++++++---------- src/database/update_query.hpp | 51 +++++++++++++----------- 7 files changed, 213 insertions(+), 164 deletions(-) (limited to 'src/database') diff --git a/src/database/index.hpp b/src/database/index.hpp index 094a36a..30766ab 100644 --- a/src/database/index.hpp +++ b/src/database/index.hpp @@ -8,16 +8,19 @@ namespace { template -void add_column_name(std::string& out) +typename std::enable_if::type +add_column_name(std::string&) +{ } + +template +typename std::enable_if::type +add_column_name(std::string& out) { - if constexpr(N < sizeof...(T)) - { - using ColumnType = typename std::remove_reference(std::declval>()))>::type; - out += ColumnType::name; - if (N != sizeof...(T) - 1) - out += ","; - add_column_name(out); - } + using ColumnType = typename std::remove_reference(std::declval>()))>::type; + out += ColumnType::name; + if (N != sizeof...(T) - 1) + out += ","; + add_column_name(out); } } diff --git a/src/database/insert_query.hpp b/src/database/insert_query.hpp index ed1944f..04c098c 100644 --- a/src/database/insert_query.hpp +++ b/src/database/insert_query.hpp @@ -11,17 +11,20 @@ #include template -void update_autoincrement_id(std::tuple& columns, Statement& statement) +typename std::enable_if::type +update_autoincrement_id(std::tuple& columns, Statement& statement) { - if constexpr(N < sizeof...(T)) - { - using ColumnType = typename std::decay(columns))>::type; - if (std::is_same::value) - auto&& column = std::get(columns); - update_autoincrement_id(columns, statement); - } + using ColumnType = typename std::decay(columns))>::type; + if (std::is_same::value) + auto&& column = std::get(columns); + update_autoincrement_id(columns, statement); } +template +typename std::enable_if::type +update_autoincrement_id(std::tuple&, Statement&) +{} + struct InsertQuery: public Query { template @@ -50,20 +53,23 @@ struct InsertQuery: public Query } template - void bind_param(const std::tuple& columns, Statement& statement, int index=1) + typename std::enable_if::type + bind_param(const std::tuple& columns, Statement& statement, int index=1) { - if constexpr(N < sizeof...(T)) - { - auto&& column = std::get(columns); - using ColumnType = std::decay_t; + auto&& column = std::get(columns); + using ColumnType = std::decay_t; - if constexpr(!std::is_same::value) - actual_bind(statement, column.value, index++); + if (!std::is_same::value) + actual_bind(statement, column.value, index++); - this->bind_param(columns, statement, index); - } + this->bind_param(columns, statement, index); } + template + typename std::enable_if::type + bind_param(const std::tuple&, Statement&, int) + {} + template void insert_values(const std::tuple& columns) { @@ -73,21 +79,23 @@ struct InsertQuery: public Query } template - void insert_value(const std::tuple& columns, int index=1) + typename std::enable_if::type + insert_value(const std::tuple& columns, int index=1) { - if constexpr(N < sizeof...(T)) + using ColumnType = std::decay_t(columns))>; + + if (!std::is_same::value) { - using ColumnType = std::decay_t(columns))>; - - if (!std::is_same::value) - { - this->body += "$" + std::to_string(index++); - if (N != sizeof...(T) - 1) - this->body += ", "; - } - this->insert_value(columns, index); + this->body += "$" + std::to_string(index++); + if (N != sizeof...(T) - 1) + this->body += ", "; } + this->insert_value(columns, index); } + template + typename std::enable_if::type + insert_value(const std::tuple&, const int) + { } template void insert_col_names(const std::tuple& columns) @@ -98,21 +106,24 @@ struct InsertQuery: public Query } template - void insert_col_name(const std::tuple& columns) + typename std::enable_if::type + insert_col_name(const std::tuple& columns) { - if constexpr(N < sizeof...(T)) - { - using ColumnType = std::decay_t(columns))>; + using ColumnType = std::decay_t(columns))>; - if (!std::is_same::value) - { - this->body += ColumnType::name; - - if (N < (sizeof...(T) - 1)) - this->body += ", "; - } + if (!std::is_same::value) + { + this->body += ColumnType::name; - this->insert_col_name(columns); + if (N < (sizeof...(T) - 1)) + this->body += ", "; } + + this->insert_col_name(columns); } + + template + typename std::enable_if::type + insert_col_name(const std::tuple&) + {} }; diff --git a/src/database/query.hpp b/src/database/query.hpp index 2a2d2d4..1c4a5ff 100644 --- a/src/database/query.hpp +++ b/src/database/query.hpp @@ -13,12 +13,12 @@ void actual_bind(Statement& statement, const std::string& value, int index); void actual_bind(Statement& statement, const std::int64_t& value, int index); -void actual_bind(Statement& statement, const std::optional& value, int index); -template +template ::value>* = 0> void actual_bind(Statement& statement, const T& value, int index) { actual_bind(statement, static_cast(value), index); } +void actual_bind(Statement& statement, const std::optional& value, int index); #ifdef DEBUG_SQL_QUERIES #include @@ -57,27 +57,38 @@ struct Query #endif }; -void actual_add_param(Query& query, const std::string& val); -void actual_add_param(Query& query, const std::optional& val); +template +void add_param(Query& query, const ColumnType& column) +{ + std::cout << "add_param" << std::endl; + actual_add_param(query, column.value); +} + template void actual_add_param(Query& query, const T& val) { query.params.push_back(std::to_string(val)); } +void actual_add_param(Query& query, const std::string& val); + +template +typename std::enable_if::value, Query&>::type +operator<<(Query& query, const T&) +{ + query.body += T::name; + return query; +} +void actual_add_param(Query& query, const std::optional& val); + Query& operator<<(Query& query, const char* str); Query& operator<<(Query& query, const std::string& str); -template -Query& operator<<(Query& query, const T& i) +template +typename std::enable_if::value, Query&>::type +operator<<(Query& query, const Integer& i) { - if constexpr(std::is_integral::value) - { - query.body += "$" + std::to_string(query.current_param++); - actual_add_param(query, i); - } - else - { - query.body += T::name; - } + query.body += "$" + std::to_string(query.current_param++); + actual_add_param(query, i); return query; } + diff --git a/src/database/row.hpp b/src/database/row.hpp index 194a9c5..3703ff7 100644 --- a/src/database/row.hpp +++ b/src/database/row.hpp @@ -29,39 +29,41 @@ struct Row return col.value; } - void save(std::unique_ptr& db) + template + void save(std::unique_ptr& db, typename std::enable_if && Coucou>::type* = nullptr) { - if constexpr(is_one_of) + this->insert(*db); + } + + template + void save(std::unique_ptr& db, typename std::enable_if && Coucou>::type* = nullptr) + { + const Id& id = std::get(this->columns); + if (id.value == Id::unset_value) { - const Id& id = std::get(this->columns); - if (id.value == Id::unset_value) - { - this->insert(*db); - if (db->last_inserted_rowid >= 0) - std::get(this->columns).value = static_cast(db->last_inserted_rowid); - } - else - this->update(*db); + this->insert(*db); + if (db->last_inserted_rowid >= 0) + std::get(this->columns).value = static_cast(db->last_inserted_rowid); } else - this->insert(*db); + this->update(*db); } private: - void insert(DatabaseEngine& db) + template + void insert(DatabaseEngine& db, typename std::enable_if && Coucou>::type* = nullptr) { - if constexpr(is_one_of) - { - 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); - } - else - { - InsertQuery query(this->table_name, this->columns); - query.execute(db, this->columns); - } + 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); + } + + template + void insert(DatabaseEngine& db, typename std::enable_if && Coucou>::type* = nullptr) + { + InsertQuery query(this->table_name, this->columns); + query.execute(db, this->columns); } void update(DatabaseEngine& db) diff --git a/src/database/select_query.hpp b/src/database/select_query.hpp index f7496a4..cd9943c 100644 --- a/src/database/select_query.hpp +++ b/src/database/select_query.hpp @@ -15,37 +15,48 @@ using namespace std::string_literals; template -auto extract_row_value(Statement& statement, const int i) +typename std::enable_if::value, std::int64_t>::type +extract_row_value(Statement& statement, const int i) { - if constexpr(std::is_integral::value) - return statement.get_column_int64(i); - else if constexpr (std::is_same::value) - return statement.get_column_text(i); - else if (std::is_same, T>::value) - { - const auto integer = statement.get_column_int(i); - if (integer > 0) - return std::optional{true}; - else if (integer < 0) - return std::optional{false}; - return std::optional{}; - } + return statement.get_column_int64(i); +} + +template +typename std::enable_if::value, T>::type +extract_row_value(Statement& statement, const int i) +{ + return statement.get_column_text(i); +} + +template +typename std::enable_if, T>::value, T>::type +extract_row_value(Statement& statement, const int i) +{ + const auto integer = statement.get_column_int(i); + if (integer > 0) + return true; + else if (integer < 0) + return false; + return std::nullopt; } template -void extract_row_values(Row& row, Statement& statement) +typename std::enable_if::type +extract_row_values(Row& row, Statement& statement) { - if constexpr(N < sizeof...(T)) - { - using ColumnType = typename std::remove_reference(row.columns))>::type; + using ColumnType = typename std::remove_reference(row.columns))>::type; - auto&& column = std::get(row.columns); - column.value = static_cast(extract_row_value(statement, N)); + auto&& column = std::get(row.columns); + column.value = static_cast(extract_row_value(statement, N)); - extract_row_values(row, statement); - } + extract_row_values(row, statement); } +template +typename std::enable_if::type +extract_row_values(Row&, Statement&) +{} + template struct SelectQuery: public Query { @@ -58,21 +69,23 @@ struct SelectQuery: public Query } template - void insert_col_name() + typename std::enable_if::type + insert_col_name() { - if constexpr(N < sizeof...(T)) - { - using ColumnsType = std::tuple; - using ColumnType = typename std::remove_reference(std::declval()))>::type; + using ColumnsType = std::tuple; + using ColumnType = typename std::remove_reference(std::declval()))>::type; - this->body += " " + std::string{ColumnType::name}; + this->body += " " + std::string{ColumnType::name}; - if (N < (sizeof...(T) - 1)) - this->body += ", "; + if (N < (sizeof...(T) - 1)) + this->body += ", "; - this->insert_col_name(); - } + this->insert_col_name(); } + template + typename std::enable_if::type + insert_col_name() + {} SelectQuery& where() { diff --git a/src/database/table.hpp b/src/database/table.hpp index eb68418..680e7cc 100644 --- a/src/database/table.hpp +++ b/src/database/table.hpp @@ -93,32 +93,36 @@ class Table private: template - void add_column_if_not_exists(DatabaseEngine& db, const std::set& existing_columns) + typename std::enable_if::type + add_column_if_not_exists(DatabaseEngine& db, const std::set& existing_columns) { - if constexpr(N < sizeof...(T)) - { - using ColumnType = typename std::remove_reference(std::declval()))>::type; - if (existing_columns.count(ColumnType::name) == 0) - add_column_to_table(db, this->name); - add_column_if_not_exists(db, existing_columns); - } + using ColumnType = typename std::remove_reference(std::declval()))>::type; + if (existing_columns.count(ColumnType::name) == 0) + add_column_to_table(db, this->name); + add_column_if_not_exists(db, existing_columns); } + template + typename std::enable_if::type + add_column_if_not_exists(DatabaseEngine&, const std::set&) + {} template - void add_column_create(DatabaseEngine& db, std::string& str) + typename std::enable_if::type + add_column_create(DatabaseEngine& db, std::string& str) { - if constexpr(N < sizeof...(T)) - { - using ColumnType = typename std::remove_reference(std::declval()))>::type; - str += ColumnType::name; - str += " "; - str += ToSQLType(db); - if (N != sizeof...(T) - 1) - str += ","; - - add_column_create(db, str); - } + using ColumnType = typename std::remove_reference(std::declval()))>::type; + str += ColumnType::name; + str += " "; + str += ToSQLType(db); + if (N != sizeof...(T) - 1) + str += ","; + + add_column_create(db, str); } + template + typename std::enable_if::type + 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 0ee2bd9..a29ac3f 100644 --- a/src/database/update_query.hpp +++ b/src/database/update_query.hpp @@ -39,25 +39,27 @@ struct UpdateQuery: public Query } template - void insert_col_name_and_value(const std::tuple& columns) + typename std::enable_if::type + insert_col_name_and_value(const std::tuple& columns) { - if constexpr(N < sizeof...(T)) - { - using ColumnType = std::decay_t(columns))>; - - if (!std::is_same::value) - { - this->body += ColumnType::name + "=$"s - + std::to_string(this->current_param); - this->current_param++; + using ColumnType = std::decay_t(columns))>; - if (N < (sizeof...(T) - 1)) - this->body += ", "; - } + if (!std::is_same::value) + { + this->body += ColumnType::name + "=$"s + std::to_string(this->current_param); + this->current_param++; - this->insert_col_name_and_value(columns); + if (N < (sizeof...(T) - 1)) + this->body += ", "; } + + this->insert_col_name_and_value(columns); } + template + typename std::enable_if::type + insert_col_name_and_value(const std::tuple&) + {} + template void execute(DatabaseEngine& db, const std::tuple& columns) @@ -74,20 +76,23 @@ struct UpdateQuery: public Query } template - void bind_param(const std::tuple& columns, Statement& statement, int index=1) + typename std::enable_if::type + bind_param(const std::tuple& columns, Statement& statement, int index=1) { - if constexpr(N < sizeof...(T)) - { - auto&& column = std::get(columns); - using ColumnType = std::decay_t; + auto&& column = std::get(columns); + using ColumnType = std::decay_t; - if (!std::is_same::value) - actual_bind(statement, column.value, index++); + if (!std::is_same::value) + actual_bind(statement, column.value, index++); - this->bind_param(columns, statement, index); - } + this->bind_param(columns, statement, index); } + template + typename std::enable_if::type + bind_param(const std::tuple&, Statement&, int) + {} + template void bind_id(const std::tuple& columns, Statement& statement) { -- cgit v1.2.3 From 03714c6cebf90dc7db8e3997a18cdd19e039c667 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sat, 17 Mar 2018 17:28:47 +0100 Subject: Revert "Use std::optional instead of OptionalBool" This reverts commit ba879a882e031d7b8503f78fe41d1210000c96ca. --- src/database/database.hpp | 2 +- src/database/query.cpp | 12 ++++++------ src/database/query.hpp | 4 ++-- src/database/select_query.hpp | 9 +++++---- 4 files changed, 14 insertions(+), 13 deletions(-) (limited to 'src/database') diff --git a/src/database/database.hpp b/src/database/database.hpp index 03d82ae..8a967d8 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -69,7 +69,7 @@ class Database struct RecordHistory: Column { static constexpr auto name = "recordhistory_"; RecordHistory(): Column(true) {}}; - struct RecordHistoryOptional: Column> { static constexpr auto name = "recordhistory_"; }; + struct RecordHistoryOptional: Column { static constexpr auto name = "recordhistory_"; }; struct VerifyCert: Column { static constexpr auto name = "verifycert_"; VerifyCert(): Column(true) {} }; diff --git a/src/database/query.cpp b/src/database/query.cpp index 13c881b..d72066e 100644 --- a/src/database/query.cpp +++ b/src/database/query.cpp @@ -11,11 +11,11 @@ void actual_bind(Statement& statement, const std::int64_t& value, int index) statement.bind_int64(index, value); } -void actual_bind(Statement& statement, const std::optional& value, int index) +void actual_bind(Statement& statement, const OptionalBool& value, int index) { - if (!value) + if (!value.is_set) statement.bind_int64(index, 0); - else if (*value) + else if (value.value) statement.bind_int64(index, 1); else statement.bind_int64(index, -1); @@ -26,11 +26,11 @@ void actual_add_param(Query& query, const std::string& val) query.params.push_back(val); } -void actual_add_param(Query& query, const std::optional& val) +void actual_add_param(Query& query, const OptionalBool& val) { - if (!val) + if (!val.is_set) query.params.push_back("0"); - else if (*val) + else if (val.value) query.params.push_back("1"); else query.params.push_back("-1"); diff --git a/src/database/query.hpp b/src/database/query.hpp index 1c4a5ff..ba28b1a 100644 --- a/src/database/query.hpp +++ b/src/database/query.hpp @@ -18,7 +18,7 @@ void actual_bind(Statement& statement, const T& value, int index) { actual_bind(statement, static_cast(value), index); } -void actual_bind(Statement& statement, const std::optional& value, int index); +void actual_bind(Statement& statement, const OptionalBool& value, int index); #ifdef DEBUG_SQL_QUERIES #include @@ -71,6 +71,7 @@ void actual_add_param(Query& query, const T& val) } void actual_add_param(Query& query, const std::string& val); +void actual_add_param(Query& query, const OptionalBool& val); template typename std::enable_if::value, Query&>::type @@ -79,7 +80,6 @@ operator<<(Query& query, const T&) query.body += T::name; return query; } -void actual_add_param(Query& query, const std::optional& val); Query& operator<<(Query& query, const char* str); Query& operator<<(Query& query, const std::string& str); diff --git a/src/database/select_query.hpp b/src/database/select_query.hpp index cd9943c..5a17f38 100644 --- a/src/database/select_query.hpp +++ b/src/database/select_query.hpp @@ -29,15 +29,16 @@ extract_row_value(Statement& statement, const int i) } template -typename std::enable_if, T>::value, T>::type +typename std::enable_if::value, T>::type extract_row_value(Statement& statement, const int i) { const auto integer = statement.get_column_int(i); + OptionalBool result; if (integer > 0) - return true; + result.set_value(true); else if (integer < 0) - return false; - return std::nullopt; + result.set_value(false); + return result; } template -- cgit v1.2.3 From 577984faf2befaa7f11a1e4a115dc8d80805fec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sun, 18 Mar 2018 02:31:18 +0100 Subject: Allow the execution of multiple commands after the IRC connection fix #3275 --- src/database/column.hpp | 4 ++++ src/database/database.cpp | 29 +++++++++++++++++++++++++++++ src/database/database.hpp | 10 +++++++++- src/database/delete_query.hpp | 33 +++++++++++++++++++++++++++++++++ src/database/table.hpp | 7 +++++++ 5 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 src/database/delete_query.hpp (limited to 'src/database') diff --git a/src/database/column.hpp b/src/database/column.hpp index 9367701..50c9c14 100644 --- a/src/database/column.hpp +++ b/src/database/column.hpp @@ -13,6 +13,10 @@ struct Column T value{}; }; +struct ForeignKey: Column { + static constexpr auto name = "fk_"; +}; + struct Id: Column { static constexpr std::size_t unset_value = static_cast(-1); static constexpr auto name = "id_"; diff --git a/src/database/database.cpp b/src/database/database.cpp index d19ed7a..812d27c 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -21,6 +21,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::encoding_in_cache{}; Database::GlobalPersistent::GlobalPersistent(): @@ -53,6 +54,8 @@ 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::db, "archive_index", Database::muc_log_lines.get_name()); } @@ -88,6 +91,32 @@ Database::IrcServerOptions Database::get_irc_server_options(const std::string& o return options; } +Database::AfterConnectionCommands Database::get_after_connection_commands(const IrcServerOptions& server_options) +{ + const auto id = server_options.col(); + if (id == Id::unset_value) + return {}; + auto request = Database::after_connection_commands.select(); + 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(); + if (id == Id::unset_value) + return ; + auto query = Database::after_connection_commands.del(); + query.where() << ForeignKey{} << "=" << id; + query.execute(*Database::db); + + for (auto& command: commands) + { + command.col() = server_options.col(); + command.save(Database::db); + } +} + 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(); diff --git a/src/database/database.hpp b/src/database/database.hpp index 8a967d8..0e88be8 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -92,7 +92,7 @@ class Database using GlobalOptionsTable = Table; using GlobalOptions = GlobalOptionsTable::RowType; - using IrcServerOptionsTable = Table; + using IrcServerOptionsTable = Table; using IrcServerOptions = IrcServerOptionsTable::RowType; using IrcChannelOptionsTable = Table; @@ -101,6 +101,9 @@ class Database using RosterTable = Table; using RosterItem = RosterTable::RowType; + using AfterConnectionCommandsTable = Table; + using AfterConnectionCommands = std::vector; + Database() = default; ~Database() = default; @@ -121,6 +124,9 @@ 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. @@ -158,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 db; /** 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 +#include + +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/table.hpp b/src/database/table.hpp index 680e7cc..c8c1bdd 100644 --- a/src/database/table.hpp +++ b/src/database/table.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -85,6 +86,12 @@ class Table return select; } + auto del() + { + DeleteQuery query(this->name); + return query; + } + const std::string& get_name() const { return this->name; -- cgit v1.2.3 From 5af0a8040c33d07dacf78343eb9ed0a03437a65a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sun, 18 Mar 2018 02:50:33 +0100 Subject: Use a transaction around the DELETE + INSERT of the after_connection_commands Otherwise we can imagine that two clients changing this value at the same time would mix things up. ref #3275 --- src/database/database.cpp | 21 +++++++++++++++++++++ src/database/database.hpp | 11 ++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) (limited to 'src/database') diff --git a/src/database/database.cpp b/src/database/database.cpp index 812d27c..b1525d5 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -106,6 +106,8 @@ void Database::set_after_connection_commands(const Database::IrcServerOptions& s const auto id = server_options.col(); if (id == Id::unset_value) return ; + + Transaction transaction; auto query = Database::after_connection_commands.del(); query.where() << ForeignKey{} << "=" << id; query.execute(*Database::db); @@ -330,4 +332,23 @@ std::string Database::gen_uuid() return uuid_str; } +Transaction::Transaction() +{ + const auto result = Database::raw_exec("BEGIN"); + if (std::get(result) == false) + log_error("Failed to create SQL transaction: ", std::get(result)); + else + this->success = true; + +} + +Transaction::~Transaction() +{ + if (this->success) + { + const auto result = Database::raw_exec("END"); + if (std::get(result) == false) + log_error("Failed to end SQL transaction: ", std::get(result)); + } +} #endif diff --git a/src/database/database.hpp b/src/database/database.hpp index 0e88be8..5dde447 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -203,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 encoding_in_cache; }; + +class Transaction +{ +public: + Transaction(); + ~Transaction(); + bool success{false}; +}; + #endif /* USE_DATABASE */ -- cgit v1.2.3 From 9500bfd4ccb21b261fd8204180d78553704f7acc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sun, 18 Mar 2018 19:33:07 +0100 Subject: Reflect message IDs in channel MUCs fix #3283 --- src/database/database.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'src/database') diff --git a/src/database/database.cpp b/src/database/database.cpp index b1525d5..02c5b4f 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -2,9 +2,9 @@ #ifdef USE_DATABASE #include -#include #include #include +#include #include #include @@ -325,11 +325,7 @@ 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() -- cgit v1.2.3 From d7427fc9ca4c06fda458e4951559f57163d90b94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Mon, 19 Mar 2018 00:16:16 +0100 Subject: Re-connect to postgresql when the connection is lost fix #3336 --- src/database/postgresql_statement.hpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'src/database') diff --git a/src/database/postgresql_statement.hpp b/src/database/postgresql_statement.hpp index 5665aed..37e8ea0 100644 --- a/src/database/postgresql_statement.hpp +++ b/src/database/postgresql_statement.hpp @@ -92,7 +92,7 @@ class PostgresqlStatement: public Statement private: private: - bool execute() + bool execute(const bool second_attempt=false) { std::vector params; params.reserve(this->params.size()); @@ -113,7 +113,17 @@ private: 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}); - return false; + 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; } -- cgit v1.2.3 From 619c991a691a455efee2baa4e1fe96e64d27ff64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Mon, 19 Mar 2018 20:08:35 +0100 Subject: Add a nick field in the IRC server configuration form fix #3317 --- src/database/database.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/database') diff --git a/src/database/database.hpp b/src/database/database.hpp index 5dde447..d986ecc 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -92,7 +92,7 @@ class Database using GlobalOptionsTable = Table; using GlobalOptions = GlobalOptionsTable::RowType; - using IrcServerOptionsTable = Table; + using IrcServerOptionsTable = Table; using IrcServerOptions = IrcServerOptionsTable::RowType; using IrcChannelOptionsTable = Table; -- cgit v1.2.3 From cdace80758e2ee41f33824ad7f52a369c42a4a89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 23 Mar 2018 15:54:36 +0100 Subject: Check that db.prepare worked before using the returned statement --- src/database/select_query.hpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/database') diff --git a/src/database/select_query.hpp b/src/database/select_query.hpp index 5a17f38..743a011 100644 --- a/src/database/select_query.hpp +++ b/src/database/select_query.hpp @@ -115,6 +115,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) -- cgit v1.2.3 From 857c7d3972a03cbeebf730d99b924d3710dee6a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 10 Apr 2018 23:33:59 +0200 Subject: Use a different Date data type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PLEASE backup your database before testing this commit, and report any migration issue. In postgresql, we use timestamp with timezone. In sqlite3 we use REAL (the date is expressed as julianday) This requires a migration of the muclogline_ table: In postgresql it’s pretty simple, we convert all the integer into timestamps With sqlite3, we actually rename the table, create the new one with the correct type, then copy everything to the new table, with a conversion function for the Date_ column, and then we delete the old table. fix #3343 --- src/database/column_escape.cpp | 46 ++++++++++++++++++++++++++ src/database/database.cpp | 43 +++++++++++++++++++------ src/database/database.hpp | 30 ++++++++++++++--- src/database/datetime_writer.hpp | 32 ++++++++++++++++++ src/database/engine.hpp | 18 +++++++++-- src/database/insert_query.hpp | 16 ++++++++- src/database/postgresql_engine.cpp | 44 ++++++++++++++++++++++--- src/database/postgresql_engine.hpp | 13 ++++++-- src/database/query.cpp | 16 +++++++++ src/database/query.hpp | 10 ++++-- src/database/select_query.hpp | 25 +++++++++++++-- src/database/sqlite3_engine.cpp | 66 ++++++++++++++++++++++++++++++++++---- src/database/sqlite3_engine.hpp | 11 +++++-- src/database/table.hpp | 8 +++-- 14 files changed, 339 insertions(+), 39 deletions(-) create mode 100644 src/database/column_escape.cpp create mode 100644 src/database/datetime_writer.hpp (limited to 'src/database') diff --git a/src/database/column_escape.cpp b/src/database/column_escape.cpp new file mode 100644 index 0000000..0f1f611 --- /dev/null +++ b/src/database/column_escape.cpp @@ -0,0 +1,46 @@ +#include + +#include +#include + +template <> +std::string before_column() +{ + if (Database::engine_type() == DatabaseEngine::EngineType::Sqlite3) + return "strftime(\"%Y-%m-%dT%H:%M:%SZ\", "; + else if (Database::engine_type() == DatabaseEngine::EngineType::Postgresql) + return "to_char("; + return {}; +} + +template <> +std::string after_column() +{ + if (Database::engine_type() == DatabaseEngine::EngineType::Sqlite3) + return ")"; + else if (Database::engine_type() == DatabaseEngine::EngineType::Postgresql) + return R"(, 'YYYY-MM-DD"T"HH24:MM:SS"Z"'))"; + return {}; +} + +#include + +template <> +std::string before_value() +{ + if (Database::engine_type() == DatabaseEngine::EngineType::Sqlite3) + return "julianday("; + if (Database::engine_type() == DatabaseEngine::EngineType::Postgresql) + return "to_timestamp("; + return {}; +} + +template <> +std::string after_value() +{ + if (Database::engine_type() == DatabaseEngine::EngineType::Sqlite3) + return ", \"unixepoch\")"; + if (Database::engine_type() == DatabaseEngine::EngineType::Postgresql) + return ")"; + return {}; +} diff --git a/src/database/database.cpp b/src/database/database.cpp index 02c5b4f..7cb0a45 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -46,6 +46,7 @@ void Database::open(const std::string& filename) Database::db = std::move(new_db); Database::muc_log_lines.create(*Database::db); Database::muc_log_lines.upgrade(*Database::db); + convert_date_format(*Database::db, Database::muc_log_lines); Database::global_options.create(*Database::db); Database::global_options.upgrade(*Database::db); Database::irc_server_options.create(*Database::db); @@ -57,9 +58,9 @@ void Database::open(const std::string& filename) Database::after_connection_commands.create(*Database::db); Database::after_connection_commands.upgrade(*Database::db); create_index(*Database::db, "archive_index", Database::muc_log_lines.get_name()); + Database::db->init_session(); } - Database::GlobalOptions Database::get_global_options(const std::string& owner) { auto request = Database::global_options.select(); @@ -175,7 +176,7 @@ Database::IrcChannelOptions Database::get_irc_channel_options_with_server_and_gl } std::string Database::store_muc_message(const std::string& owner, const std::string& chan_name, - const std::string& server_name, Database::time_point date, + const std::string& server_name, DateTime::time_point date, const std::string& body, const std::string& nick) { auto line = Database::muc_log_lines.row(); @@ -186,7 +187,7 @@ std::string Database::store_muc_message(const std::string& owner, const std::str line.col() = owner; line.col() = chan_name; line.col() = server_name; - line.col() = std::chrono::duration_cast(date.time_since_epoch()).count(); + line.col() = date; line.col() = body; line.col() = nick; @@ -210,13 +211,21 @@ std::vector Database::get_muc_logs(const std::string& owne { const auto start_time = utils::parse_datetime(start); if (start_time != -1) - request << " and " << Database::Date{} << ">=" << start_time; + { + DateTime datetime(start_time); + DatetimeWriter writer(datetime, *Database::db); + request << " and " << Database::Date{} << ">=" << writer; + } } if (!end.empty()) { const auto end_time = utils::parse_datetime(end); if (end_time != -1) - request << " and " << Database::Date{} << "<=" << end_time; + { + DateTime datetime(end_time); + DatetimeWriter writer(datetime, *Database::db); + request << " and " << Database::Date{} << "<=" << writer; + } } if (reference_record_id != Id::unset_value) { @@ -229,9 +238,9 @@ std::vector Database::get_muc_logs(const std::string& owne } if (paging == Database::Paging::first) - request.order_by() << Database::Date{} << " ASC, " << Id{} << " ASC "; + request.order_by() << Database::Date{} << " ASC"; else - request.order_by() << Database::Date{} << " DESC, " << Id{} << " DESC "; + request.order_by() << Database::Date{} << " DESC"; if (limit >= 0) request.limit() << limit; @@ -257,13 +266,21 @@ Database::MucLogLine Database::get_muc_log(const std::string& owner, const std:: { const auto start_time = utils::parse_datetime(start); if (start_time != -1) - request << " and " << Database::Date{} << ">=" << start_time; + { + DateTime datetime(start_time); + DatetimeWriter writer(datetime, *Database::db); + request << " and " << Database::Date{} << ">=" << writer; + } } if (!end.empty()) { const auto end_time = utils::parse_datetime(end); if (end_time != -1) - request << " and " << Database::Date{} << "<=" << end_time; + { + DateTime datetime(end_time); + DatetimeWriter writer(datetime, *Database::db); + request << " and " << Database::Date{} << "<=" << writer; + } } auto result = request.execute(*Database::db); @@ -347,4 +364,12 @@ Transaction::~Transaction() log_error("Failed to end SQL transaction: ", std::get(result)); } } + +void Transaction::rollback() +{ + this->success = false; + const auto result = Database::raw_exec("ROLLBACK"); + if (std::get(result) == false) + log_error("Failed to rollback SQL transaction: ", std::get(result)); +} #endif diff --git a/src/database/database.hpp b/src/database/database.hpp index d986ecc..75ff8f3 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -17,11 +18,9 @@ #include #include - class Database { public: - using time_point = std::chrono::system_clock::time_point; struct RecordNotFound: public std::exception {}; enum class Paging { first, last }; @@ -37,7 +36,8 @@ class Database struct Server: Column { static constexpr auto name = "server_"; }; - struct Date: Column { static constexpr auto name = "date_"; }; + struct OldDate: Column { static constexpr auto name = "date_"; }; + struct Date: Column { static constexpr auto name = "date_"; }; struct Body: Column { static constexpr auto name = "body_"; }; @@ -88,6 +88,8 @@ class Database using MucLogLineTable = Table; using MucLogLine = MucLogLineTable::RowType; + using OldMucLogLineTable = Table; + using OldMucLogLine = OldMucLogLineTable::RowType; using GlobalOptionsTable = Table; using GlobalOptions = GlobalOptionsTable::RowType; @@ -141,7 +143,7 @@ class Database */ 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); + DateTime::time_point date, const std::string& body, const std::string& nick); static void add_roster_item(const std::string& local, const std::string& remote); static bool has_roster_item(const std::string& local, const std::string& remote); @@ -168,6 +170,13 @@ class Database static std::unique_ptr db; + static DatabaseEngine::EngineType engine_type() + { + if (Database::db) + return Database::db->engine_type(); + return DatabaseEngine::EngineType::None; + } + /** * Some caches, to avoid doing very frequent query requests for a few options. */ @@ -216,7 +225,20 @@ class Transaction public: Transaction(); ~Transaction(); + void rollback(); bool success{false}; }; +template +void convert_date_format(DatabaseEngine& db, Table table) +{ + const auto existing_columns = db.get_all_columns_from_table(table.get_name()); + const auto date_pair = existing_columns.find(Database::Date::name); + if (date_pair != existing_columns.end() && date_pair->second == "integer") + { + log_info("Converting Date_ format to the new one."); + db.convert_date_format(db); + } +} + #endif /* USE_DATABASE */ diff --git a/src/database/datetime_writer.hpp b/src/database/datetime_writer.hpp new file mode 100644 index 0000000..b104911 --- /dev/null +++ b/src/database/datetime_writer.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include +#include + +#include +#include +#include + +class DatetimeWriter +{ +public: + DatetimeWriter(DateTime datetime, const DatabaseEngine& engine): + datetime(datetime), + engine(engine) + {} + + long double get_value() const + { + const long double epoch_duration = this->datetime.epoch().count(); + const long double epoch_seconds = epoch_duration / std::chrono::system_clock::period::den; + return this->engine.epoch_to_floating_value(epoch_seconds); + } + std::string escape_param_number(int value) const + { + return this->engine.escape_param_number(value); + } + +private: + const DateTime datetime; + const DatabaseEngine& engine; +}; diff --git a/src/database/engine.hpp b/src/database/engine.hpp index 41dccf5..ecf047f 100644 --- a/src/database/engine.hpp +++ b/src/database/engine.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include class DatabaseEngine @@ -27,7 +28,10 @@ class DatabaseEngine DatabaseEngine(DatabaseEngine&&) = delete; DatabaseEngine& operator=(DatabaseEngine&&) = delete; - virtual std::set get_all_columns_from_table(const std::string& table_name) = 0; + enum class EngineType { None, Postgresql, Sqlite3, }; + virtual EngineType engine_type() const = 0; + + virtual std::map get_all_columns_from_table(const std::string& table_name) = 0; virtual std::tuple raw_exec(const std::string& query) = 0; virtual std::unique_ptr prepare(const std::string& query) = 0; virtual void extract_last_insert_rowid(Statement& statement) = 0; @@ -35,7 +39,17 @@ class DatabaseEngine { return {}; } - virtual std::string id_column_type() = 0; + virtual void convert_date_format(DatabaseEngine&) = 0; + virtual std::string id_column_type() const = 0; + virtual std::string datetime_column_type() const = 0; + virtual long double epoch_to_floating_value(long double seconds) const = 0; + virtual std::string escape_param_number(int nb) const + { + return "$" + std::to_string(nb); + } + virtual void init_session() + { + } int64_t last_inserted_rowid{-1}; }; diff --git a/src/database/insert_query.hpp b/src/database/insert_query.hpp index 04c098c..e3a7e83 100644 --- a/src/database/insert_query.hpp +++ b/src/database/insert_query.hpp @@ -25,6 +25,18 @@ typename std::enable_if::type update_autoincrement_id(std::tuple&, Statement&) {} +template +std::string before_value() +{ + return {}; +} + +template +std::string after_value() +{ + return {}; +} + struct InsertQuery: public Query { template @@ -73,7 +85,7 @@ struct InsertQuery: public Query template void insert_values(const std::tuple& columns) { - this->body += "VALUES ("; + this->body += " VALUES ("; this->insert_value(columns); this->body += ")"; } @@ -86,7 +98,9 @@ struct InsertQuery: public Query if (!std::is_same::value) { + this->body += before_value(); this->body += "$" + std::to_string(index++); + this->body += after_value(); if (N != sizeof...(T) - 1) this->body += ", "; } diff --git a/src/database/postgresql_engine.cpp b/src/database/postgresql_engine.cpp index 59bc885..abeb779 100644 --- a/src/database/postgresql_engine.cpp +++ b/src/database/postgresql_engine.cpp @@ -2,6 +2,7 @@ #ifdef PQ_FOUND #include +#include #include @@ -12,6 +13,7 @@ #include #include +#include PostgresqlEngine::PostgresqlEngine(PGconn*const conn): conn(conn) @@ -52,14 +54,14 @@ std::unique_ptr PostgresqlEngine::open(const std::string& connin return std::make_unique(con); } -std::set PostgresqlEngine::get_all_columns_from_table(const std::string& table_name) +std::map PostgresqlEngine::get_all_columns_from_table(const std::string& table_name) { - const auto query = "SELECT column_name from information_schema.columns where table_name='" + table_name + "'"; + const auto query = "SELECT column_name, data_type from information_schema.columns where table_name='" + table_name + "'"; auto statement = this->prepare(query); - std::set columns; + std::map columns; while (statement->step() == StepResult::Row) - columns.insert(statement->get_column_text(0)); + columns[utils::tolower(statement->get_column_text(0))] = utils::tolower(statement->get_column_text(1)); return columns; } @@ -96,9 +98,41 @@ std::string PostgresqlEngine::get_returning_id_sql_string(const std::string& col return " RETURNING " + col_name; } -std::string PostgresqlEngine::id_column_type() +std::string PostgresqlEngine::id_column_type() const { return "SERIAL"; } +std::string PostgresqlEngine::datetime_column_type() const +{ + return "TIMESTAMP"; +} + +void PostgresqlEngine::convert_date_format(DatabaseEngine& db) +{ + const auto table_name = Database::muc_log_lines.get_name(); + const std::string column_name = Database::Date::name; + const std::string query = "ALTER TABLE " + table_name + " ALTER COLMUN " + column_name + " SET DATA TYPE timestamp USING to_timestamp(" + column_name + ")"; + + auto result = db.raw_exec(query); + if (!std::get(result)) + log_error("Failed to execute query: ", std::get(result)); +} + +std::string PostgresqlEngine::escape_param_number(int nb) const +{ + return "to_timestamp(" + DatabaseEngine::escape_param_number(nb) + ")"; +} + +void PostgresqlEngine::init_session() +{ + const auto res = this->raw_exec("SET SESSION TIME ZONE 'UTC'"); + if (!std::get(res)) + log_debug("Failed to set UTC timezone: ", std::get(res)); +} +long double PostgresqlEngine::epoch_to_floating_value(long double seconds) const +{ + return seconds; +} + #endif diff --git a/src/database/postgresql_engine.hpp b/src/database/postgresql_engine.hpp index fe4fb53..f2dcec3 100644 --- a/src/database/postgresql_engine.hpp +++ b/src/database/postgresql_engine.hpp @@ -23,13 +23,22 @@ class PostgresqlEngine: public DatabaseEngine ~PostgresqlEngine(); static std::unique_ptr open(const std::string& string); + EngineType engine_type() const override + { + return EngineType::Postgresql; + } - std::set get_all_columns_from_table(const std::string& table_name) override final; + std::map get_all_columns_from_table(const std::string& table_name) override final; std::tuple raw_exec(const std::string& query) override final; std::unique_ptr prepare(const std::string& query) override; void extract_last_insert_rowid(Statement& statement) override; std::string get_returning_id_sql_string(const std::string& col_name) override; - std::string id_column_type() override; + std::string id_column_type() const override; + std::string datetime_column_type() const override; + void convert_date_format(DatabaseEngine& engine) override; + long double epoch_to_floating_value(long double seconds) const override; + void init_session() override; + std::string escape_param_number(int nb) const override; private: PGconn* const conn; }; diff --git a/src/database/query.cpp b/src/database/query.cpp index d72066e..6d20302 100644 --- a/src/database/query.cpp +++ b/src/database/query.cpp @@ -21,6 +21,13 @@ void actual_bind(Statement& statement, const OptionalBool& value, int index) statement.bind_int64(index, -1); } +void actual_bind(Statement& statement, const DateTime& value, int index) +{ + const auto epoch = value.epoch().count(); + const auto result = std::to_string(static_cast(epoch) / std::chrono::system_clock::period::den); + statement.bind_text(index, result); +} + void actual_add_param(Query& query, const std::string& val) { query.params.push_back(val); @@ -49,3 +56,12 @@ Query& operator<<(Query& query, const std::string& str) actual_add_param(query, str); return query; } + +Query& operator<<(Query& query, const DatetimeWriter& datetime_writer) +{ + query.body += datetime_writer.escape_param_number(query.current_param++); + actual_add_param(query, datetime_writer.get_value()); + return query; +} + + diff --git a/src/database/query.hpp b/src/database/query.hpp index ba28b1a..910271a 100644 --- a/src/database/query.hpp +++ b/src/database/query.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -10,6 +11,7 @@ #include #include +#include void actual_bind(Statement& statement, const std::string& value, int index); void actual_bind(Statement& statement, const std::int64_t& value, int index); @@ -19,6 +21,7 @@ void actual_bind(Statement& statement, const T& value, int index) actual_bind(statement, static_cast(value), index); } void actual_bind(Statement& statement, const OptionalBool& value, int index); +void actual_bind(Statement& statement, const DateTime& value, int index); #ifdef DEBUG_SQL_QUERIES #include @@ -74,15 +77,13 @@ void actual_add_param(Query& query, const std::string& val); void actual_add_param(Query& query, const OptionalBool& val); template -typename std::enable_if::value, Query&>::type +typename std::enable_if::value, Query&>::type operator<<(Query& query, const T&) { query.body += T::name; return query; } -Query& operator<<(Query& query, const char* str); -Query& operator<<(Query& query, const std::string& str); template typename std::enable_if::value, Query&>::type operator<<(Query& query, const Integer& i) @@ -92,3 +93,6 @@ operator<<(Query& query, const Integer& i) return query; } +Query& operator<<(Query& query, const char* str); +Query& operator<<(Query& query, const std::string& str); +Query& operator<<(Query& query, const DatetimeWriter& datetime); diff --git a/src/database/select_query.hpp b/src/database/select_query.hpp index 743a011..3013dd8 100644 --- a/src/database/select_query.hpp +++ b/src/database/select_query.hpp @@ -3,8 +3,8 @@ #include #include +#include #include -#include #include #include @@ -41,6 +41,14 @@ extract_row_value(Statement& statement, const int i) return result; } +template +typename std::enable_if::value, T>::type +extract_row_value(Statement& statement, const int i) +{ + const std::string timestamp = statement.get_column_text(i); + return {timestamp}; +} + template typename std::enable_if::type extract_row_values(Row& row, Statement& statement) @@ -58,6 +66,18 @@ typename std::enable_if::type extract_row_values(Row&, Statement&) {} +template +std::string before_column() +{ + return {}; +} + +template +std::string after_column() +{ + return {}; +} + template struct SelectQuery: public Query { @@ -76,7 +96,8 @@ struct SelectQuery: public Query using ColumnsType = std::tuple; using ColumnType = typename std::remove_reference(std::declval()))>::type; - this->body += " " + std::string{ColumnType::name}; + this->body += " "; + this->body += before_column() + ColumnType::name + after_column(); if (N < (sizeof...(T) - 1)) this->body += ", "; diff --git a/src/database/sqlite3_engine.cpp b/src/database/sqlite3_engine.cpp index ae4a146..1fa6316 100644 --- a/src/database/sqlite3_engine.cpp +++ b/src/database/sqlite3_engine.cpp @@ -2,6 +2,7 @@ #ifdef SQLITE3_FOUND +#include #include #include @@ -22,16 +23,17 @@ Sqlite3Engine::~Sqlite3Engine() sqlite3_close(this->db); } -std::set Sqlite3Engine::get_all_columns_from_table(const std::string& table_name) +std::map Sqlite3Engine::get_all_columns_from_table(const std::string& table_name) { - std::set result; + std::map result; char* errmsg; std::string query{"PRAGMA table_info(" + table_name + ")"}; int res = sqlite3_exec(this->db, query.data(), [](void* param, int columns_nb, char** columns, char**) -> int { constexpr int name_column = 1; - std::set* result = static_cast*>(param); - if (name_column < columns_nb) - result->insert(utils::tolower(columns[name_column])); + constexpr int data_type_column = 2; + auto* result = static_cast*>(param); + if (name_column < columns_nb && data_type_column < columns_nb) + (*result)[utils::tolower(columns[name_column])] = utils::tolower(columns[data_type_column]); return 0; }, &result, &errmsg); @@ -44,6 +46,48 @@ std::set Sqlite3Engine::get_all_columns_from_table(const std::strin return result; } +template +static auto make_select_query(const Row&, const std::string& name) +{ + return SelectQuery{name}; +} + +void Sqlite3Engine::convert_date_format(DatabaseEngine& db) +{ + Transaction transaction{}; + auto rollback = [&transaction] (const std::string& error_msg) + { + log_error("Failed to execute query: ", error_msg); + transaction.rollback(); + }; + + const auto real_name = Database::muc_log_lines.get_name(); + const auto tmp_name = real_name + "tmp_"; + const std::string date_name = Database::Date::name; + + auto result = db.raw_exec("ALTER TABLE " + real_name + " RENAME TO " + tmp_name); + if (!std::get(result)) + return rollback(std::get(result)); + + Database::muc_log_lines.create(db); + + Database::OldMucLogLineTable old_muc_log_line(tmp_name); + auto select_query = make_select_query(old_muc_log_line.row(), old_muc_log_line.get_name()); + + auto& select_body = select_query.body; + auto begin = select_body.find(date_name); + select_body.replace(begin, date_name.size(), "julianday("+date_name+", 'unixepoch')"); + select_body = "INSERT INTO " + real_name + " " + select_body; + + result = db.raw_exec(select_body); + if (!std::get(result)) + return rollback(std::get(result)); + + result = db.raw_exec("DROP TABLE " + tmp_name); + if (!std::get(result)) + return rollback(std::get(result)); +} + std::unique_ptr Sqlite3Engine::open(const std::string& filename) { sqlite3* new_db; @@ -93,9 +137,19 @@ void Sqlite3Engine::extract_last_insert_rowid(Statement&) this->last_inserted_rowid = sqlite3_last_insert_rowid(this->db); } -std::string Sqlite3Engine::id_column_type() +std::string Sqlite3Engine::id_column_type() const { return "INTEGER PRIMARY KEY AUTOINCREMENT"; } +std::string Sqlite3Engine::datetime_column_type() const +{ + return "REAL"; +} + +long double Sqlite3Engine::epoch_to_floating_value(long double d) const +{ + return (d / 86400.0) + 2440587.5; +} + #endif diff --git a/src/database/sqlite3_engine.hpp b/src/database/sqlite3_engine.hpp index 5b8176c..82d01c9 100644 --- a/src/database/sqlite3_engine.hpp +++ b/src/database/sqlite3_engine.hpp @@ -23,12 +23,19 @@ class Sqlite3Engine: public DatabaseEngine ~Sqlite3Engine(); static std::unique_ptr open(const std::string& string); + EngineType engine_type() const override + { + return EngineType::Sqlite3; + } - std::set get_all_columns_from_table(const std::string& table_name) override final; + std::map get_all_columns_from_table(const std::string& table_name) override final; std::tuple raw_exec(const std::string& query) override final; std::unique_ptr prepare(const std::string& query) override; void extract_last_insert_rowid(Statement& statement) override; - std::string id_column_type() override; + std::string id_column_type() const override; + std::string datetime_column_type() const override; + void convert_date_format(DatabaseEngine&) override; + long double epoch_to_floating_value(long double d) const override; private: sqlite3* const db; }; diff --git a/src/database/table.hpp b/src/database/table.hpp index c8c1bdd..31b92a7 100644 --- a/src/database/table.hpp +++ b/src/database/table.hpp @@ -19,6 +19,8 @@ std::string ToSQLType(DatabaseEngine& db) return db.id_column_type(); else if (std::is_same::value) return "TEXT"; + else if (std::is_same::value) + return db.datetime_column_type(); else return "INTEGER"; } @@ -101,16 +103,16 @@ class Table template typename std::enable_if::type - add_column_if_not_exists(DatabaseEngine& db, const std::set& existing_columns) + add_column_if_not_exists(DatabaseEngine& db, const std::map& existing_columns) { using ColumnType = typename std::remove_reference(std::declval()))>::type; - if (existing_columns.count(ColumnType::name) == 0) + if (existing_columns.find(ColumnType::name) == existing_columns.end()) add_column_to_table(db, this->name); add_column_if_not_exists(db, existing_columns); } template typename std::enable_if::type - add_column_if_not_exists(DatabaseEngine&, const std::set&) + add_column_if_not_exists(DatabaseEngine&, const std::map&) {} template -- cgit v1.2.3 From de8267fa3f4f4e1d61bcf35fb36c6664f520a385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 11 Apr 2018 00:45:29 +0200 Subject: Fix a build warning when compiling without postgres or sqlite --- src/database/postgresql_engine.hpp | 3 +++ src/database/sqlite3_engine.hpp | 3 +++ 2 files changed, 6 insertions(+) (limited to 'src/database') diff --git a/src/database/postgresql_engine.hpp b/src/database/postgresql_engine.hpp index f2dcec3..70215d4 100644 --- a/src/database/postgresql_engine.hpp +++ b/src/database/postgresql_engine.hpp @@ -45,12 +45,15 @@ private: #else +using namespace std::string_literals; + class PostgresqlEngine { public: static std::unique_ptr 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/sqlite3_engine.hpp b/src/database/sqlite3_engine.hpp index 82d01c9..1b37e8c 100644 --- a/src/database/sqlite3_engine.hpp +++ b/src/database/sqlite3_engine.hpp @@ -42,12 +42,15 @@ private: #else +using namespace std::string_literals; + class Sqlite3Engine { public: static std::unique_ptr open(const std::string& string) { throw std::runtime_error("Cannot open sqlite3 database "s + string + ": biboumi is not compiled with sqlite3 lib."); + return {}; } }; -- cgit v1.2.3 From 4bd7b6981bb49dd4111c908aaa34c34f677171f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 13 Apr 2018 23:35:06 +0200 Subject: Refactor that fixes a compilation issue in Release mode Some template specialization were not found, because they were not declared at the point they were used. We moved things around, things are less inter-dependant, and also now it works. --- src/database/column_escape.cpp | 46 --------------------------------- src/database/database.cpp | 57 +++++++++++++++++++---------------------- src/database/insert_query.cpp | 21 +++++++++++++++ src/database/insert_query.hpp | 21 +++++++++++++++ src/database/row.hpp | 49 ----------------------------------- src/database/save.hpp | 31 ++++++++++++++++++++++ src/database/select_query.cpp | 21 +++++++++++++++ src/database/select_query.hpp | 14 ++++++++++ src/database/sqlite3_engine.cpp | 1 + src/database/table.hpp | 10 ++------ src/database/update_query.hpp | 11 +++++++- 11 files changed, 147 insertions(+), 135 deletions(-) delete mode 100644 src/database/column_escape.cpp create mode 100644 src/database/insert_query.cpp create mode 100644 src/database/save.hpp create mode 100644 src/database/select_query.cpp (limited to 'src/database') diff --git a/src/database/column_escape.cpp b/src/database/column_escape.cpp deleted file mode 100644 index 0f1f611..0000000 --- a/src/database/column_escape.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include - -#include -#include - -template <> -std::string before_column() -{ - if (Database::engine_type() == DatabaseEngine::EngineType::Sqlite3) - return "strftime(\"%Y-%m-%dT%H:%M:%SZ\", "; - else if (Database::engine_type() == DatabaseEngine::EngineType::Postgresql) - return "to_char("; - return {}; -} - -template <> -std::string after_column() -{ - if (Database::engine_type() == DatabaseEngine::EngineType::Sqlite3) - return ")"; - else if (Database::engine_type() == DatabaseEngine::EngineType::Postgresql) - return R"(, 'YYYY-MM-DD"T"HH24:MM:SS"Z"'))"; - return {}; -} - -#include - -template <> -std::string before_value() -{ - if (Database::engine_type() == DatabaseEngine::EngineType::Sqlite3) - return "julianday("; - if (Database::engine_type() == DatabaseEngine::EngineType::Postgresql) - return "to_timestamp("; - return {}; -} - -template <> -std::string after_value() -{ - if (Database::engine_type() == DatabaseEngine::EngineType::Sqlite3) - return ", \"unixepoch\")"; - if (Database::engine_type() == DatabaseEngine::EngineType::Postgresql) - return ")"; - return {}; -} diff --git a/src/database/database.cpp b/src/database/database.cpp index 7cb0a45..c935139 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -1,6 +1,8 @@ #include "biboumi.h" #ifdef USE_DATABASE +#include +#include #include #include #include @@ -63,32 +65,28 @@ void Database::open(const std::string& filename) 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; + return result.front(); + Database::GlobalOptions options{Database::global_options.get_name()}; + options.col() = 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 - { - options.col() = owner; - options.col() = server; - } + return result.front(); + Database::IrcServerOptions options{Database::irc_server_options.get_name()}; + options.col() = owner; + options.col() = server; return options; } @@ -97,7 +95,7 @@ Database::AfterConnectionCommands Database::get_after_connection_commands(const const auto id = server_options.col(); if (id == Id::unset_value) return {}; - auto request = Database::after_connection_commands.select(); + auto request = select(Database::after_connection_commands); request.where() << ForeignKey{} << "=" << id; return request.execute(*Database::db); } @@ -116,26 +114,23 @@ void Database::set_after_connection_commands(const Database::IrcServerOptions& s for (auto& command: commands) { command.col() = server_options.col(); - command.save(Database::db); + save(command, *Database::db); } } 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; - options.col() = server; - options.col() = channel; - } + return result.front(); + Database::IrcChannelOptions options{Database::irc_channel_options.get_name()}; + options.col() = owner; + options.col() = server; + options.col() = channel; return options; } @@ -191,7 +186,7 @@ std::string Database::store_muc_message(const std::string& owner, const std::str line.col() = body; line.col() = nick; - line.save(Database::db); + save(line, *Database::db); return uuid; } @@ -202,7 +197,7 @@ std::vector Database::get_muc_logs(const std::string& owne if (limit == 0) return {}; - auto request = Database::muc_log_lines.select(); + auto request = select(Database::muc_log_lines); request.where() << Database::Owner{} << "=" << owner << \ " and " << Database::IrcChanName{} << "=" << chan_name << \ " and " << Database::IrcServerName{} << "=" << server; @@ -256,7 +251,7 @@ std::vector Database::get_muc_logs(const std::string& owne 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 = Database::muc_log_lines.select(); + auto request = select(Database::muc_log_lines); request.where() << Database::Owner{} << "=" << owner << \ " and " << Database::IrcChanName{} << "=" << chan_name << \ " and " << Database::IrcServerName{} << "=" << server << \ @@ -297,7 +292,7 @@ void Database::add_roster_item(const std::string& local, const std::string& remo roster_item.col() = local; roster_item.col() = remote; - roster_item.save(Database::db); + save(roster_item, *Database::db); } void Database::delete_roster_item(const std::string& local, const std::string& remote) @@ -311,7 +306,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; @@ -322,7 +317,7 @@ bool Database::has_roster_item(const std::string& local, const std::string& remo std::vector 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); @@ -330,7 +325,7 @@ std::vector Database::get_contact_list(const std::string& std::vector Database::get_full_roster() { - auto query = Database::roster.select(); + auto query = select(Database::roster); return query.execute(*Database::db); } diff --git a/src/database/insert_query.cpp b/src/database/insert_query.cpp new file mode 100644 index 0000000..f72d67f --- /dev/null +++ b/src/database/insert_query.cpp @@ -0,0 +1,21 @@ +#include + +template <> +std::string before_value() +{ + if (Database::engine_type() == DatabaseEngine::EngineType::Sqlite3) + return "julianday("; + if (Database::engine_type() == DatabaseEngine::EngineType::Postgresql) + return "to_timestamp("; + return {}; +} + +template <> +std::string after_value() +{ + if (Database::engine_type() == DatabaseEngine::EngineType::Sqlite3) + return ", \"unixepoch\")"; + if (Database::engine_type() == DatabaseEngine::EngineType::Postgresql) + return ")"; + return {}; +} diff --git a/src/database/insert_query.hpp b/src/database/insert_query.hpp index e3a7e83..cd1942f 100644 --- a/src/database/insert_query.hpp +++ b/src/database/insert_query.hpp @@ -1,10 +1,15 @@ #pragma once #include +#include #include #include +#include + #include +#include + #include #include #include @@ -37,6 +42,12 @@ std::string after_value() return {}; } +template <> +std::string before_value(); + +template <> +std::string after_value(); + struct InsertQuery: public Query { template @@ -141,3 +152,13 @@ struct InsertQuery: public Query insert_col_name(const std::tuple&) {} }; + +template +void insert(Row& row, DatabaseEngine& db) +{ + InsertQuery query(row.table_name, row.columns); + // Ugly workaround for non portable stuff + if (is_one_of) + query.body += db.get_returning_id_sql_string(Id::name); + query.execute(db, row.columns); +} diff --git a/src/database/row.hpp b/src/database/row.hpp index 3703ff7..27caf43 100644 --- a/src/database/row.hpp +++ b/src/database/row.hpp @@ -1,9 +1,5 @@ #pragma once -#include -#include -#include - #include #include @@ -29,52 +25,7 @@ struct Row return col.value; } - template - void save(std::unique_ptr& db, typename std::enable_if && Coucou>::type* = nullptr) - { - this->insert(*db); - } - - template - void save(std::unique_ptr& db, typename std::enable_if && Coucou>::type* = nullptr) - { - const Id& id = std::get(this->columns); - if (id.value == Id::unset_value) - { - this->insert(*db); - if (db->last_inserted_rowid >= 0) - std::get(this->columns).value = static_cast(db->last_inserted_rowid); - } - else - this->update(*db); - } - - private: - template - void insert(DatabaseEngine& db, typename std::enable_if && Coucou>::type* = nullptr) - { - 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); - } - - template - void insert(DatabaseEngine& db, typename std::enable_if && Coucou>::type* = nullptr) - { - InsertQuery query(this->table_name, this->columns); - query.execute(db, this->columns); - } - - void update(DatabaseEngine& db) - { - UpdateQuery query(this->table_name, this->columns); - - query.execute(db, this->columns); - } - public: std::tuple 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 +#include + +#include + +#include + +#include + +template +void save(Row& row, DatabaseEngine& db, typename std::enable_if && Coucou>::type* = nullptr) +{ + insert(row, db); +} + +template +void save(Row& row, DatabaseEngine& db, typename std::enable_if && Coucou>::type* = nullptr) +{ + const Id& id = std::get(row.columns); + if (id.value == Id::unset_value) + { + insert(row, db); + if (db.last_inserted_rowid >= 0) + std::get(row.columns).value = static_cast(db.last_inserted_rowid); + } + else + update(row, db); +} + diff --git a/src/database/select_query.cpp b/src/database/select_query.cpp new file mode 100644 index 0000000..970b06c --- /dev/null +++ b/src/database/select_query.cpp @@ -0,0 +1,21 @@ +#include + +template <> +std::string before_column() +{ + if (Database::engine_type() == DatabaseEngine::EngineType::Sqlite3) + return "strftime(\"%Y-%m-%dT%H:%M:%SZ\", "; + else if (Database::engine_type() == DatabaseEngine::EngineType::Postgresql) + return "to_char("; + return {}; +} + +template <> +std::string after_column() +{ + if (Database::engine_type() == DatabaseEngine::EngineType::Sqlite3) + return ")"; + else if (Database::engine_type() == DatabaseEngine::EngineType::Postgresql) + return R"(, 'YYYY-MM-DD"T"HH24:MM:SS"Z"'))"; + return {}; +} diff --git a/src/database/select_query.hpp b/src/database/select_query.hpp index 3013dd8..0de4fe5 100644 --- a/src/database/select_query.hpp +++ b/src/database/select_query.hpp @@ -2,6 +2,8 @@ #include +#include +#include #include #include #include @@ -78,6 +80,12 @@ std::string after_column() return {}; } +template <> +std::string before_column(); + +template <> +std::string after_column(); + template struct SelectQuery: public Query { @@ -153,3 +161,9 @@ struct SelectQuery: public Query const std::string table_name; }; +template +auto select(const Table table) +{ + SelectQuery query(table.name); + return query; +} diff --git a/src/database/sqlite3_engine.cpp b/src/database/sqlite3_engine.cpp index 1fa6316..b6ac1a1 100644 --- a/src/database/sqlite3_engine.cpp +++ b/src/database/sqlite3_engine.cpp @@ -3,6 +3,7 @@ #ifdef SQLITE3_FOUND #include +#include #include #include diff --git a/src/database/table.hpp b/src/database/table.hpp index 31b92a7..4c96151 100644 --- a/src/database/table.hpp +++ b/src/database/table.hpp @@ -2,7 +2,6 @@ #include -#include #include #include @@ -82,12 +81,6 @@ class Table return {this->name}; } - auto select() - { - SelectQuery select(this->name); - return select; - } - auto del() { DeleteQuery query(this->name); @@ -99,6 +92,8 @@ class Table return this->name; } + const std::string name; + private: template @@ -133,5 +128,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 #include +#include +#include using namespace std::string_literals; @@ -102,3 +103,11 @@ struct UpdateQuery: public Query actual_bind(statement, value.value, sizeof...(T)); } }; + +template +void update(Row& row, DatabaseEngine& db) +{ + UpdateQuery query(row.table_name, row.columns); + + query.execute(db, row.columns); +} -- cgit v1.2.3 From d375a9edeb7a2aa21497a2cbc1db0d3f703283a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 13 Apr 2018 23:37:46 +0200 Subject: one log_debug -> log_error, also some trivial indent --- src/database/postgresql_engine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/database') diff --git a/src/database/postgresql_engine.cpp b/src/database/postgresql_engine.cpp index abeb779..d97f2ff 100644 --- a/src/database/postgresql_engine.cpp +++ b/src/database/postgresql_engine.cpp @@ -128,7 +128,7 @@ void PostgresqlEngine::init_session() { const auto res = this->raw_exec("SET SESSION TIME ZONE 'UTC'"); if (!std::get(res)) - log_debug("Failed to set UTC timezone: ", std::get(res)); + log_error("Failed to set UTC timezone: ", std::get(res)); } long double PostgresqlEngine::epoch_to_floating_value(long double seconds) const { -- cgit v1.2.3 From 5ef7ba08028065b03d51d1dc70bb35aeb41ae19d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sat, 14 Apr 2018 19:48:45 +0200 Subject: Use the Date to find a next page in RSM, not the id This way, it works, whatever the order of insertion in the database was. fix #3343 --- src/database/database.cpp | 9 +++++---- src/database/database.hpp | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'src/database') diff --git a/src/database/database.cpp b/src/database/database.cpp index c935139..fb24ad3 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -192,7 +192,7 @@ std::string Database::store_muc_message(const std::string& owner, const std::str } std::vector 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, const Id::real_type reference_record_id, Database::Paging paging) + int limit, const std::string& start, const std::string& end, const std::string& reference_uuid, Database::Paging paging) { if (limit == 0) return {}; @@ -222,14 +222,15 @@ std::vector Database::get_muc_logs(const std::string& owne request << " and " << Database::Date{} << "<=" << writer; } } - if (reference_record_id != Id::unset_value) + if (!reference_uuid.empty()) { - request << " and " << Id{}; + request << " and " << Database::Date{}; if (paging == Database::Paging::first) request << ">"; else request << "<"; - request << reference_record_id; + request << "(SELECT " << Database::Date{} << " FROM " << Database::muc_log_lines.get_name().data() + << " WHERE " << Database::Uuid{} << " = " << reference_uuid << ")"; } if (paging == Database::Paging::first) diff --git a/src/database/database.hpp b/src/database/database.hpp index 75ff8f3..b4c4fa0 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -135,7 +135,7 @@ class Database */ static std::vector 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="", - const Id::real_type reference_record_id=Id::unset_value, Paging=Paging::first); + const std::string& reference_record_id={}, Paging=Paging::first); /** * Get just one single record matching the given uuid, between (optional) end and start. -- cgit v1.2.3 From a90f196a1ce779d502baf0aadff6e6917fec8a02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 24 Apr 2018 19:13:10 +0200 Subject: Revert "Use the Date to find a next page in RSM, not the id" This reverts commit 5ef7ba08028065b03d51d1dc70bb35aeb41ae19d. --- src/database/database.cpp | 9 ++++----- src/database/database.hpp | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) (limited to 'src/database') diff --git a/src/database/database.cpp b/src/database/database.cpp index fb24ad3..c935139 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -192,7 +192,7 @@ std::string Database::store_muc_message(const std::string& owner, const std::str } std::vector 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, const std::string& reference_uuid, Database::Paging paging) + int limit, const std::string& start, const std::string& end, const Id::real_type reference_record_id, Database::Paging paging) { if (limit == 0) return {}; @@ -222,15 +222,14 @@ std::vector Database::get_muc_logs(const std::string& owne request << " and " << Database::Date{} << "<=" << writer; } } - if (!reference_uuid.empty()) + if (reference_record_id != Id::unset_value) { - request << " and " << Database::Date{}; + request << " and " << Id{}; if (paging == Database::Paging::first) request << ">"; else request << "<"; - request << "(SELECT " << Database::Date{} << " FROM " << Database::muc_log_lines.get_name().data() - << " WHERE " << Database::Uuid{} << " = " << reference_uuid << ")"; + request << reference_record_id; } if (paging == Database::Paging::first) diff --git a/src/database/database.hpp b/src/database/database.hpp index b4c4fa0..75ff8f3 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -135,7 +135,7 @@ class Database */ static std::vector 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="", - const std::string& reference_record_id={}, Paging=Paging::first); + 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. -- cgit v1.2.3 From 61de6b1dac4ef29627f3bdb9ce11b6c0d06f4a24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 24 Apr 2018 19:19:06 +0200 Subject: Revert "Use a different Date data type" This reverts commit 857c7d3972a03cbeebf730d99b924d3710dee6a0. --- src/database/database.cpp | 43 +++++------------------- src/database/database.hpp | 30 +++-------------- src/database/datetime_writer.hpp | 32 ------------------ src/database/engine.hpp | 18 ++-------- src/database/insert_query.cpp | 21 ------------ src/database/insert_query.hpp | 22 +----------- src/database/postgresql_engine.cpp | 44 +++--------------------- src/database/postgresql_engine.hpp | 13 ++------ src/database/query.cpp | 16 --------- src/database/query.hpp | 10 ++---- src/database/select_query.cpp | 21 ------------ src/database/select_query.hpp | 31 ++--------------- src/database/sqlite3_engine.cpp | 68 ++++---------------------------------- src/database/sqlite3_engine.hpp | 11 ++---- src/database/table.hpp | 8 ++--- 15 files changed, 39 insertions(+), 349 deletions(-) delete mode 100644 src/database/datetime_writer.hpp delete mode 100644 src/database/insert_query.cpp delete mode 100644 src/database/select_query.cpp (limited to 'src/database') diff --git a/src/database/database.cpp b/src/database/database.cpp index c935139..a09dfe3 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -48,7 +48,6 @@ void Database::open(const std::string& filename) Database::db = std::move(new_db); Database::muc_log_lines.create(*Database::db); Database::muc_log_lines.upgrade(*Database::db); - convert_date_format(*Database::db, Database::muc_log_lines); Database::global_options.create(*Database::db); Database::global_options.upgrade(*Database::db); Database::irc_server_options.create(*Database::db); @@ -60,9 +59,9 @@ void Database::open(const std::string& filename) Database::after_connection_commands.create(*Database::db); Database::after_connection_commands.upgrade(*Database::db); create_index(*Database::db, "archive_index", Database::muc_log_lines.get_name()); - Database::db->init_session(); } + Database::GlobalOptions Database::get_global_options(const std::string& owner) { auto request = select(Database::global_options); @@ -171,7 +170,7 @@ Database::IrcChannelOptions Database::get_irc_channel_options_with_server_and_gl } std::string Database::store_muc_message(const std::string& owner, const std::string& chan_name, - const std::string& server_name, DateTime::time_point date, + const std::string& server_name, Database::time_point date, const std::string& body, const std::string& nick) { auto line = Database::muc_log_lines.row(); @@ -182,7 +181,7 @@ std::string Database::store_muc_message(const std::string& owner, const std::str line.col() = owner; line.col() = chan_name; line.col() = server_name; - line.col() = date; + line.col() = std::chrono::duration_cast(date.time_since_epoch()).count(); line.col() = body; line.col() = nick; @@ -206,21 +205,13 @@ std::vector Database::get_muc_logs(const std::string& owne { const auto start_time = utils::parse_datetime(start); if (start_time != -1) - { - DateTime datetime(start_time); - DatetimeWriter writer(datetime, *Database::db); - request << " and " << Database::Date{} << ">=" << writer; - } + request << " and " << Database::Date{} << ">=" << start_time; } if (!end.empty()) { const auto end_time = utils::parse_datetime(end); if (end_time != -1) - { - DateTime datetime(end_time); - DatetimeWriter writer(datetime, *Database::db); - request << " and " << Database::Date{} << "<=" << writer; - } + request << " and " << Database::Date{} << "<=" << end_time; } if (reference_record_id != Id::unset_value) { @@ -233,9 +224,9 @@ std::vector Database::get_muc_logs(const std::string& owne } if (paging == Database::Paging::first) - request.order_by() << Database::Date{} << " ASC"; + request.order_by() << Database::Date{} << " ASC, " << Id{} << " ASC "; else - request.order_by() << Database::Date{} << " DESC"; + request.order_by() << Database::Date{} << " DESC, " << Id{} << " DESC "; if (limit >= 0) request.limit() << limit; @@ -261,21 +252,13 @@ Database::MucLogLine Database::get_muc_log(const std::string& owner, const std:: { const auto start_time = utils::parse_datetime(start); if (start_time != -1) - { - DateTime datetime(start_time); - DatetimeWriter writer(datetime, *Database::db); - request << " and " << Database::Date{} << ">=" << writer; - } + request << " and " << Database::Date{} << ">=" << start_time; } if (!end.empty()) { const auto end_time = utils::parse_datetime(end); if (end_time != -1) - { - DateTime datetime(end_time); - DatetimeWriter writer(datetime, *Database::db); - request << " and " << Database::Date{} << "<=" << writer; - } + request << " and " << Database::Date{} << "<=" << end_time; } auto result = request.execute(*Database::db); @@ -359,12 +342,4 @@ Transaction::~Transaction() log_error("Failed to end SQL transaction: ", std::get(result)); } } - -void Transaction::rollback() -{ - this->success = false; - const auto result = Database::raw_exec("ROLLBACK"); - if (std::get(result) == false) - log_error("Failed to rollback SQL transaction: ", std::get(result)); -} #endif diff --git a/src/database/database.hpp b/src/database/database.hpp index 75ff8f3..d986ecc 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -10,7 +10,6 @@ #include #include -#include #include #include @@ -18,9 +17,11 @@ #include #include + class Database { public: + using time_point = std::chrono::system_clock::time_point; struct RecordNotFound: public std::exception {}; enum class Paging { first, last }; @@ -36,8 +37,7 @@ class Database struct Server: Column { static constexpr auto name = "server_"; }; - struct OldDate: Column { static constexpr auto name = "date_"; }; - struct Date: Column { static constexpr auto name = "date_"; }; + struct Date: Column { static constexpr auto name = "date_"; }; struct Body: Column { static constexpr auto name = "body_"; }; @@ -88,8 +88,6 @@ class Database using MucLogLineTable = Table; using MucLogLine = MucLogLineTable::RowType; - using OldMucLogLineTable = Table; - using OldMucLogLine = OldMucLogLineTable::RowType; using GlobalOptionsTable = Table; using GlobalOptions = GlobalOptionsTable::RowType; @@ -143,7 +141,7 @@ class Database */ 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, - DateTime::time_point date, const std::string& body, const std::string& nick); + time_point date, const std::string& body, const std::string& nick); static void add_roster_item(const std::string& local, const std::string& remote); static bool has_roster_item(const std::string& local, const std::string& remote); @@ -170,13 +168,6 @@ class Database static std::unique_ptr db; - static DatabaseEngine::EngineType engine_type() - { - if (Database::db) - return Database::db->engine_type(); - return DatabaseEngine::EngineType::None; - } - /** * Some caches, to avoid doing very frequent query requests for a few options. */ @@ -225,20 +216,7 @@ class Transaction public: Transaction(); ~Transaction(); - void rollback(); bool success{false}; }; -template -void convert_date_format(DatabaseEngine& db, Table table) -{ - const auto existing_columns = db.get_all_columns_from_table(table.get_name()); - const auto date_pair = existing_columns.find(Database::Date::name); - if (date_pair != existing_columns.end() && date_pair->second == "integer") - { - log_info("Converting Date_ format to the new one."); - db.convert_date_format(db); - } -} - #endif /* USE_DATABASE */ diff --git a/src/database/datetime_writer.hpp b/src/database/datetime_writer.hpp deleted file mode 100644 index b104911..0000000 --- a/src/database/datetime_writer.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include -#include - -#include -#include -#include - -class DatetimeWriter -{ -public: - DatetimeWriter(DateTime datetime, const DatabaseEngine& engine): - datetime(datetime), - engine(engine) - {} - - long double get_value() const - { - const long double epoch_duration = this->datetime.epoch().count(); - const long double epoch_seconds = epoch_duration / std::chrono::system_clock::period::den; - return this->engine.epoch_to_floating_value(epoch_seconds); - } - std::string escape_param_number(int value) const - { - return this->engine.escape_param_number(value); - } - -private: - const DateTime datetime; - const DatabaseEngine& engine; -}; diff --git a/src/database/engine.hpp b/src/database/engine.hpp index ecf047f..41dccf5 100644 --- a/src/database/engine.hpp +++ b/src/database/engine.hpp @@ -13,7 +13,6 @@ #include #include #include -#include #include class DatabaseEngine @@ -28,10 +27,7 @@ class DatabaseEngine DatabaseEngine(DatabaseEngine&&) = delete; DatabaseEngine& operator=(DatabaseEngine&&) = delete; - enum class EngineType { None, Postgresql, Sqlite3, }; - virtual EngineType engine_type() const = 0; - - virtual std::map get_all_columns_from_table(const std::string& table_name) = 0; + virtual std::set get_all_columns_from_table(const std::string& table_name) = 0; virtual std::tuple raw_exec(const std::string& query) = 0; virtual std::unique_ptr prepare(const std::string& query) = 0; virtual void extract_last_insert_rowid(Statement& statement) = 0; @@ -39,17 +35,7 @@ class DatabaseEngine { return {}; } - virtual void convert_date_format(DatabaseEngine&) = 0; - virtual std::string id_column_type() const = 0; - virtual std::string datetime_column_type() const = 0; - virtual long double epoch_to_floating_value(long double seconds) const = 0; - virtual std::string escape_param_number(int nb) const - { - return "$" + std::to_string(nb); - } - virtual void init_session() - { - } + virtual std::string id_column_type() = 0; int64_t last_inserted_rowid{-1}; }; diff --git a/src/database/insert_query.cpp b/src/database/insert_query.cpp deleted file mode 100644 index f72d67f..0000000 --- a/src/database/insert_query.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include - -template <> -std::string before_value() -{ - if (Database::engine_type() == DatabaseEngine::EngineType::Sqlite3) - return "julianday("; - if (Database::engine_type() == DatabaseEngine::EngineType::Postgresql) - return "to_timestamp("; - return {}; -} - -template <> -std::string after_value() -{ - if (Database::engine_type() == DatabaseEngine::EngineType::Sqlite3) - return ", \"unixepoch\")"; - if (Database::engine_type() == DatabaseEngine::EngineType::Postgresql) - return ")"; - return {}; -} diff --git a/src/database/insert_query.hpp b/src/database/insert_query.hpp index cd1942f..230e873 100644 --- a/src/database/insert_query.hpp +++ b/src/database/insert_query.hpp @@ -30,24 +30,6 @@ typename std::enable_if::type update_autoincrement_id(std::tuple&, Statement&) {} -template -std::string before_value() -{ - return {}; -} - -template -std::string after_value() -{ - return {}; -} - -template <> -std::string before_value(); - -template <> -std::string after_value(); - struct InsertQuery: public Query { template @@ -96,7 +78,7 @@ struct InsertQuery: public Query template void insert_values(const std::tuple& columns) { - this->body += " VALUES ("; + this->body += "VALUES ("; this->insert_value(columns); this->body += ")"; } @@ -109,9 +91,7 @@ struct InsertQuery: public Query if (!std::is_same::value) { - this->body += before_value(); this->body += "$" + std::to_string(index++); - this->body += after_value(); if (N != sizeof...(T) - 1) this->body += ", "; } diff --git a/src/database/postgresql_engine.cpp b/src/database/postgresql_engine.cpp index d97f2ff..59bc885 100644 --- a/src/database/postgresql_engine.cpp +++ b/src/database/postgresql_engine.cpp @@ -2,7 +2,6 @@ #ifdef PQ_FOUND #include -#include #include @@ -13,7 +12,6 @@ #include #include -#include PostgresqlEngine::PostgresqlEngine(PGconn*const conn): conn(conn) @@ -54,14 +52,14 @@ std::unique_ptr PostgresqlEngine::open(const std::string& connin return std::make_unique(con); } -std::map PostgresqlEngine::get_all_columns_from_table(const std::string& table_name) +std::set PostgresqlEngine::get_all_columns_from_table(const std::string& table_name) { - const auto query = "SELECT column_name, data_type from information_schema.columns where table_name='" + table_name + "'"; + const auto query = "SELECT column_name from information_schema.columns where table_name='" + table_name + "'"; auto statement = this->prepare(query); - std::map columns; + std::set columns; while (statement->step() == StepResult::Row) - columns[utils::tolower(statement->get_column_text(0))] = utils::tolower(statement->get_column_text(1)); + columns.insert(statement->get_column_text(0)); return columns; } @@ -98,41 +96,9 @@ std::string PostgresqlEngine::get_returning_id_sql_string(const std::string& col return " RETURNING " + col_name; } -std::string PostgresqlEngine::id_column_type() const +std::string PostgresqlEngine::id_column_type() { return "SERIAL"; } -std::string PostgresqlEngine::datetime_column_type() const -{ - return "TIMESTAMP"; -} - -void PostgresqlEngine::convert_date_format(DatabaseEngine& db) -{ - const auto table_name = Database::muc_log_lines.get_name(); - const std::string column_name = Database::Date::name; - const std::string query = "ALTER TABLE " + table_name + " ALTER COLMUN " + column_name + " SET DATA TYPE timestamp USING to_timestamp(" + column_name + ")"; - - auto result = db.raw_exec(query); - if (!std::get(result)) - log_error("Failed to execute query: ", std::get(result)); -} - -std::string PostgresqlEngine::escape_param_number(int nb) const -{ - return "to_timestamp(" + DatabaseEngine::escape_param_number(nb) + ")"; -} - -void PostgresqlEngine::init_session() -{ - const auto res = this->raw_exec("SET SESSION TIME ZONE 'UTC'"); - if (!std::get(res)) - log_error("Failed to set UTC timezone: ", std::get(res)); -} -long double PostgresqlEngine::epoch_to_floating_value(long double seconds) const -{ - return seconds; -} - #endif diff --git a/src/database/postgresql_engine.hpp b/src/database/postgresql_engine.hpp index 70215d4..1a9c249 100644 --- a/src/database/postgresql_engine.hpp +++ b/src/database/postgresql_engine.hpp @@ -23,22 +23,13 @@ class PostgresqlEngine: public DatabaseEngine ~PostgresqlEngine(); static std::unique_ptr open(const std::string& string); - EngineType engine_type() const override - { - return EngineType::Postgresql; - } - std::map get_all_columns_from_table(const std::string& table_name) override final; + std::set get_all_columns_from_table(const std::string& table_name) override final; std::tuple raw_exec(const std::string& query) override final; std::unique_ptr prepare(const std::string& query) override; void extract_last_insert_rowid(Statement& statement) override; std::string get_returning_id_sql_string(const std::string& col_name) override; - std::string id_column_type() const override; - std::string datetime_column_type() const override; - void convert_date_format(DatabaseEngine& engine) override; - long double epoch_to_floating_value(long double seconds) const override; - void init_session() override; - std::string escape_param_number(int nb) const override; + std::string id_column_type() override; private: PGconn* const conn; }; diff --git a/src/database/query.cpp b/src/database/query.cpp index 6d20302..d72066e 100644 --- a/src/database/query.cpp +++ b/src/database/query.cpp @@ -21,13 +21,6 @@ void actual_bind(Statement& statement, const OptionalBool& value, int index) statement.bind_int64(index, -1); } -void actual_bind(Statement& statement, const DateTime& value, int index) -{ - const auto epoch = value.epoch().count(); - const auto result = std::to_string(static_cast(epoch) / std::chrono::system_clock::period::den); - statement.bind_text(index, result); -} - void actual_add_param(Query& query, const std::string& val) { query.params.push_back(val); @@ -56,12 +49,3 @@ Query& operator<<(Query& query, const std::string& str) actual_add_param(query, str); return query; } - -Query& operator<<(Query& query, const DatetimeWriter& datetime_writer) -{ - query.body += datetime_writer.escape_param_number(query.current_param++); - actual_add_param(query, datetime_writer.get_value()); - return query; -} - - diff --git a/src/database/query.hpp b/src/database/query.hpp index 910271a..ba28b1a 100644 --- a/src/database/query.hpp +++ b/src/database/query.hpp @@ -3,7 +3,6 @@ #include #include -#include #include #include @@ -11,7 +10,6 @@ #include #include -#include void actual_bind(Statement& statement, const std::string& value, int index); void actual_bind(Statement& statement, const std::int64_t& value, int index); @@ -21,7 +19,6 @@ void actual_bind(Statement& statement, const T& value, int index) actual_bind(statement, static_cast(value), index); } void actual_bind(Statement& statement, const OptionalBool& value, int index); -void actual_bind(Statement& statement, const DateTime& value, int index); #ifdef DEBUG_SQL_QUERIES #include @@ -77,13 +74,15 @@ void actual_add_param(Query& query, const std::string& val); void actual_add_param(Query& query, const OptionalBool& val); template -typename std::enable_if::value, Query&>::type +typename std::enable_if::value, Query&>::type operator<<(Query& query, const T&) { query.body += T::name; return query; } +Query& operator<<(Query& query, const char* str); +Query& operator<<(Query& query, const std::string& str); template typename std::enable_if::value, Query&>::type operator<<(Query& query, const Integer& i) @@ -93,6 +92,3 @@ operator<<(Query& query, const Integer& i) return query; } -Query& operator<<(Query& query, const char* str); -Query& operator<<(Query& query, const std::string& str); -Query& operator<<(Query& query, const DatetimeWriter& datetime); diff --git a/src/database/select_query.cpp b/src/database/select_query.cpp deleted file mode 100644 index 970b06c..0000000 --- a/src/database/select_query.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include - -template <> -std::string before_column() -{ - if (Database::engine_type() == DatabaseEngine::EngineType::Sqlite3) - return "strftime(\"%Y-%m-%dT%H:%M:%SZ\", "; - else if (Database::engine_type() == DatabaseEngine::EngineType::Postgresql) - return "to_char("; - return {}; -} - -template <> -std::string after_column() -{ - if (Database::engine_type() == DatabaseEngine::EngineType::Sqlite3) - return ")"; - else if (Database::engine_type() == DatabaseEngine::EngineType::Postgresql) - return R"(, 'YYYY-MM-DD"T"HH24:MM:SS"Z"'))"; - return {}; -} diff --git a/src/database/select_query.hpp b/src/database/select_query.hpp index 0de4fe5..b9fdc06 100644 --- a/src/database/select_query.hpp +++ b/src/database/select_query.hpp @@ -5,8 +5,8 @@ #include #include #include -#include #include +#include #include #include @@ -43,14 +43,6 @@ extract_row_value(Statement& statement, const int i) return result; } -template -typename std::enable_if::value, T>::type -extract_row_value(Statement& statement, const int i) -{ - const std::string timestamp = statement.get_column_text(i); - return {timestamp}; -} - template typename std::enable_if::type extract_row_values(Row& row, Statement& statement) @@ -68,24 +60,6 @@ typename std::enable_if::type extract_row_values(Row&, Statement&) {} -template -std::string before_column() -{ - return {}; -} - -template -std::string after_column() -{ - return {}; -} - -template <> -std::string before_column(); - -template <> -std::string after_column(); - template struct SelectQuery: public Query { @@ -104,8 +78,7 @@ struct SelectQuery: public Query using ColumnsType = std::tuple; using ColumnType = typename std::remove_reference(std::declval()))>::type; - this->body += " "; - this->body += before_column() + ColumnType::name + after_column(); + this->body += " " + std::string{ColumnType::name}; if (N < (sizeof...(T) - 1)) this->body += ", "; diff --git a/src/database/sqlite3_engine.cpp b/src/database/sqlite3_engine.cpp index b6ac1a1..5e3bba1 100644 --- a/src/database/sqlite3_engine.cpp +++ b/src/database/sqlite3_engine.cpp @@ -2,10 +2,7 @@ #ifdef SQLITE3_FOUND -#include -#include #include - #include #include @@ -24,17 +21,16 @@ Sqlite3Engine::~Sqlite3Engine() sqlite3_close(this->db); } -std::map Sqlite3Engine::get_all_columns_from_table(const std::string& table_name) +std::set Sqlite3Engine::get_all_columns_from_table(const std::string& table_name) { - std::map result; + std::set result; char* errmsg; std::string query{"PRAGMA table_info(" + table_name + ")"}; int res = sqlite3_exec(this->db, query.data(), [](void* param, int columns_nb, char** columns, char**) -> int { constexpr int name_column = 1; - constexpr int data_type_column = 2; - auto* result = static_cast*>(param); - if (name_column < columns_nb && data_type_column < columns_nb) - (*result)[utils::tolower(columns[name_column])] = utils::tolower(columns[data_type_column]); + std::set* result = static_cast*>(param); + if (name_column < columns_nb) + result->insert(utils::tolower(columns[name_column])); return 0; }, &result, &errmsg); @@ -47,48 +43,6 @@ std::map Sqlite3Engine::get_all_columns_from_table(con return result; } -template -static auto make_select_query(const Row&, const std::string& name) -{ - return SelectQuery{name}; -} - -void Sqlite3Engine::convert_date_format(DatabaseEngine& db) -{ - Transaction transaction{}; - auto rollback = [&transaction] (const std::string& error_msg) - { - log_error("Failed to execute query: ", error_msg); - transaction.rollback(); - }; - - const auto real_name = Database::muc_log_lines.get_name(); - const auto tmp_name = real_name + "tmp_"; - const std::string date_name = Database::Date::name; - - auto result = db.raw_exec("ALTER TABLE " + real_name + " RENAME TO " + tmp_name); - if (!std::get(result)) - return rollback(std::get(result)); - - Database::muc_log_lines.create(db); - - Database::OldMucLogLineTable old_muc_log_line(tmp_name); - auto select_query = make_select_query(old_muc_log_line.row(), old_muc_log_line.get_name()); - - auto& select_body = select_query.body; - auto begin = select_body.find(date_name); - select_body.replace(begin, date_name.size(), "julianday("+date_name+", 'unixepoch')"); - select_body = "INSERT INTO " + real_name + " " + select_body; - - result = db.raw_exec(select_body); - if (!std::get(result)) - return rollback(std::get(result)); - - result = db.raw_exec("DROP TABLE " + tmp_name); - if (!std::get(result)) - return rollback(std::get(result)); -} - std::unique_ptr Sqlite3Engine::open(const std::string& filename) { sqlite3* new_db; @@ -138,19 +92,9 @@ void Sqlite3Engine::extract_last_insert_rowid(Statement&) this->last_inserted_rowid = sqlite3_last_insert_rowid(this->db); } -std::string Sqlite3Engine::id_column_type() const +std::string Sqlite3Engine::id_column_type() { return "INTEGER PRIMARY KEY AUTOINCREMENT"; } -std::string Sqlite3Engine::datetime_column_type() const -{ - return "REAL"; -} - -long double Sqlite3Engine::epoch_to_floating_value(long double d) const -{ - return (d / 86400.0) + 2440587.5; -} - #endif diff --git a/src/database/sqlite3_engine.hpp b/src/database/sqlite3_engine.hpp index 1b37e8c..a7bfcdb 100644 --- a/src/database/sqlite3_engine.hpp +++ b/src/database/sqlite3_engine.hpp @@ -23,19 +23,12 @@ class Sqlite3Engine: public DatabaseEngine ~Sqlite3Engine(); static std::unique_ptr open(const std::string& string); - EngineType engine_type() const override - { - return EngineType::Sqlite3; - } - std::map get_all_columns_from_table(const std::string& table_name) override final; + std::set get_all_columns_from_table(const std::string& table_name) override final; std::tuple raw_exec(const std::string& query) override final; std::unique_ptr prepare(const std::string& query) override; void extract_last_insert_rowid(Statement& statement) override; - std::string id_column_type() const override; - std::string datetime_column_type() const override; - void convert_date_format(DatabaseEngine&) override; - long double epoch_to_floating_value(long double d) const override; + std::string id_column_type() override; private: sqlite3* const db; }; diff --git a/src/database/table.hpp b/src/database/table.hpp index 4c96151..0b8bfc0 100644 --- a/src/database/table.hpp +++ b/src/database/table.hpp @@ -18,8 +18,6 @@ std::string ToSQLType(DatabaseEngine& db) return db.id_column_type(); else if (std::is_same::value) return "TEXT"; - else if (std::is_same::value) - return db.datetime_column_type(); else return "INTEGER"; } @@ -98,16 +96,16 @@ class Table template typename std::enable_if::type - add_column_if_not_exists(DatabaseEngine& db, const std::map& existing_columns) + add_column_if_not_exists(DatabaseEngine& db, const std::set& existing_columns) { using ColumnType = typename std::remove_reference(std::declval()))>::type; - if (existing_columns.find(ColumnType::name) == existing_columns.end()) + if (existing_columns.count(ColumnType::name) == 0) add_column_to_table(db, this->name); add_column_if_not_exists(db, existing_columns); } template typename std::enable_if::type - add_column_if_not_exists(DatabaseEngine&, const std::map&) + add_column_if_not_exists(DatabaseEngine&, const std::set&) {} template -- cgit v1.2.3 From 1236b8a03bee403fcaa59d882d8e7fb9058b2280 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 24 Apr 2018 20:24:58 +0200 Subject: Only use the ID to order archives fix #3343 --- src/database/database.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/database') diff --git a/src/database/database.cpp b/src/database/database.cpp index a09dfe3..3b3e890 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -224,9 +224,9 @@ std::vector Database::get_muc_logs(const std::string& owne } if (paging == Database::Paging::first) - request.order_by() << Database::Date{} << " ASC, " << Id{} << " ASC "; + request.order_by() << Id{} << " ASC "; else - request.order_by() << Database::Date{} << " DESC, " << Id{} << " DESC "; + request.order_by() << Id{} << " DESC "; if (limit >= 0) request.limit() << limit; -- cgit v1.2.3 From 7592d966e684410f603942e34413375c8d98ac9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sun, 29 Apr 2018 01:40:46 +0200 Subject: Missing fields in a data-form response are now interpreted as an empty value --- src/database/column.hpp | 16 ++++++++++++++-- src/database/database.hpp | 10 +++++----- src/database/row.hpp | 21 ++++++++++++++++++--- 3 files changed, 37 insertions(+), 10 deletions(-) (limited to 'src/database') diff --git a/src/database/column.hpp b/src/database/column.hpp index 50c9c14..837aa3f 100644 --- a/src/database/column.hpp +++ b/src/database/column.hpp @@ -9,18 +9,30 @@ struct Column value{default_value} {} Column(): value{} {} + void clear() + { + this->value = {}; + } using real_type = T; T value{}; }; +template +struct UnclearableColumn: public Column +{ + using Column::Column; + void clear() + { } +}; + struct ForeignKey: Column { static constexpr auto name = "fk_"; }; -struct Id: Column { +struct Id: UnclearableColumn { static constexpr std::size_t unset_value = static_cast(-1); static constexpr auto name = "id_"; static constexpr auto options = "PRIMARY KEY"; - Id(): Column(unset_value) {} + Id(): UnclearableColumn(unset_value) {} }; diff --git a/src/database/database.hpp b/src/database/database.hpp index d986ecc..30ffcb4 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -27,15 +27,15 @@ class Database struct Uuid: Column { static constexpr auto name = "uuid_"; }; - struct Owner: Column { static constexpr auto name = "owner_"; }; + struct Owner: UnclearableColumn { static constexpr auto name = "owner_"; }; - struct IrcChanName: Column { static constexpr auto name = "ircchanname_"; }; + struct IrcChanName: UnclearableColumn { static constexpr auto name = "ircchanname_"; }; - struct Channel: Column { static constexpr auto name = "channel_"; }; + struct Channel: UnclearableColumn { static constexpr auto name = "channel_"; }; - struct IrcServerName: Column { static constexpr auto name = "ircservername_"; }; + struct IrcServerName: UnclearableColumn { static constexpr auto name = "ircservername_"; }; - struct Server: Column { static constexpr auto name = "server_"; }; + struct Server: UnclearableColumn { static constexpr auto name = "server_"; }; struct Date: Column { static constexpr auto name = "date_"; }; diff --git a/src/database/row.hpp b/src/database/row.hpp index 27caf43..1253f93 100644 --- a/src/database/row.hpp +++ b/src/database/row.hpp @@ -1,7 +1,5 @@ #pragma once -#include - #include template @@ -25,7 +23,24 @@ struct Row return col.value; } -public: + void clear() + { + this->clear_col<0>(); + } + std::tuple columns; std::string table_name; + +private: + template + typename std::enable_if::type + clear_col() + { + std::get(this->columns).clear(); + this->clear_col(); + } + template + typename std::enable_if::type + clear_col() + { } }; -- cgit v1.2.3 From b0168fd45b3683c2d6f61ccae67dcd5b918a363d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sun, 29 Apr 2018 22:18:26 +0200 Subject: =?UTF-8?q?mam:=20Send=20=E2=80=9Cfin=20complete=E2=80=9D=20only?= =?UTF-8?q?=20when=20appropriate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also simplify how we did the whole “limit + 1” And fix one bad interpretation of the XEP for the case where the query has no after or before restriction. fix #3349 --- src/database/database.cpp | 26 +++++++++++++++++--------- src/database/database.hpp | 4 ++-- 2 files changed, 19 insertions(+), 11 deletions(-) (limited to 'src/database') diff --git a/src/database/database.cpp b/src/database/database.cpp index 3b3e890..6e08ee1 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -190,12 +190,9 @@ std::string Database::store_muc_message(const std::string& owner, const std::str return uuid; } -std::vector 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, const Id::real_type reference_record_id, Database::Paging paging) +std::tuple> Database::get_muc_logs(const std::string& owner, const std::string& chan_name, const std::string& server, + std::size_t limit, const std::string& start, const std::string& end, const Id::real_type reference_record_id, Database::Paging paging) { - if (limit == 0) - return {}; - auto request = select(Database::muc_log_lines); request.where() << Database::Owner{} << "=" << owner << \ " and " << Database::IrcChanName{} << "=" << chan_name << \ @@ -228,15 +225,26 @@ std::vector Database::get_muc_logs(const std::string& owne else request.order_by() << Id{} << " DESC "; - if (limit >= 0) - request.limit() << limit; + // Just a simple trick: to know whether we got the totality of the + // possible results matching this query (except for the limit), we just + // ask one more element. If we get that additional element, this means + // we don’t have everything. And then we just discard it. If we don’t + // have more, this means we have everything. + request.limit() << limit + 1; auto result = request.execute(*Database::db); + bool complete = true; + + if (result.size() == limit + 1) + { + complete = false; + result.erase(std::prev(result.end())); + } if (paging == Database::Paging::first) - return result; + return {complete, result}; else - return {result.crbegin(), result.crend()}; + return {complete, {result.crbegin(), result.crend()}}; } Database::MucLogLine Database::get_muc_log(const std::string& owner, const std::string& chan_name, const std::string& server, diff --git a/src/database/database.hpp b/src/database/database.hpp index 30ffcb4..3e25b30 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -131,8 +131,8 @@ class Database * 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 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="", + static std::tuple> get_muc_logs(const std::string& owner, const std::string& chan_name, const std::string& server, + std::size_t limit, const std::string& start="", const std::string& end="", const Id::real_type reference_record_id=Id::unset_value, Paging=Paging::first); /** -- cgit v1.2.3 From 09b10cc80146c1ac2a0d5c53c6c8469b934189f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Mon, 25 Jun 2018 22:54:32 +0200 Subject: Throttle all commands sent to IRC servers fix #3354 --- src/database/database.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/database') diff --git a/src/database/database.hpp b/src/database/database.hpp index 3e25b30..5f637bd 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -86,13 +86,16 @@ class Database struct Address: Column { static constexpr auto name = "address_"; }; + struct ThrottleLimit: Column { static constexpr auto name = "throttlelimit_"; + ThrottleLimit(): Column(10) {} }; + using MucLogLineTable = Table; using MucLogLine = MucLogLineTable::RowType; using GlobalOptionsTable = Table; using GlobalOptions = GlobalOptionsTable::RowType; - using IrcServerOptionsTable = Table; + using IrcServerOptionsTable = Table; using IrcServerOptions = IrcServerOptionsTable::RowType; using IrcChannelOptionsTable = Table; -- cgit v1.2.3 From 52166e07acd785b39c0da98be4c35e886b75c3c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Mon, 25 Jun 2018 22:57:13 +0200 Subject: Trivial syntax improvements --- src/database/row.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/database') diff --git a/src/database/row.hpp b/src/database/row.hpp index 1253f93..4004b5d 100644 --- a/src/database/row.hpp +++ b/src/database/row.hpp @@ -28,7 +28,7 @@ struct Row this->clear_col<0>(); } - std::tuple columns; + std::tuple columns{}; std::string table_name; private: -- cgit v1.2.3 From 458ea53db3edc7318e88a2612baa793a1232cc75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 3 Jul 2018 20:15:36 +0200 Subject: Fix a compile error with GCC 5.x fix #3366 --- src/database/database.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/database') diff --git a/src/database/database.cpp b/src/database/database.cpp index 6e08ee1..4867f52 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -242,9 +242,9 @@ std::tuple> Database::get_muc_logs(const } if (paging == Database::Paging::first) - return {complete, result}; + return std::make_tuple(complete, result); else - return {complete, {result.crbegin(), result.crend()}}; + return std::make_tuple(complete, std::vector(result.crbegin(), result.crend())); } Database::MucLogLine Database::get_muc_log(const std::string& owner, const std::string& chan_name, const std::string& server, -- cgit v1.2.3 From 782732ba167470fa99ab3d0900c02295b4684b6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sat, 4 Aug 2018 14:50:33 +0200 Subject: Remove a bunch of useless empty lines --- src/database/database.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'src/database') diff --git a/src/database/database.cpp b/src/database/database.cpp index 4867f52..3578c04 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -338,7 +338,6 @@ Transaction::Transaction() log_error("Failed to create SQL transaction: ", std::get(result)); else this->success = true; - } Transaction::~Transaction() -- cgit v1.2.3 From 89e038bc3a6d1845f2ed450c71cdb3adb29e974a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sat, 4 Aug 2018 15:11:59 +0200 Subject: Fix the real_type of ThrottleLimit --- src/database/database.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/database') diff --git a/src/database/database.hpp b/src/database/database.hpp index 5f637bd..f6b9d52 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -86,7 +86,7 @@ class Database struct Address: Column { static constexpr auto name = "address_"; }; - struct ThrottleLimit: Column { static constexpr auto name = "throttlelimit_"; + struct ThrottleLimit: Column { static constexpr auto name = "throttlelimit_"; ThrottleLimit(): Column(10) {} }; using MucLogLineTable = Table; -- cgit v1.2.3 From d7bc737751aae7bdc77c68e4d2953aa0d6670724 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 21 Aug 2018 19:54:47 +0200 Subject: Add two missing ref --- src/database/select_query.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/database') diff --git a/src/database/select_query.hpp b/src/database/select_query.hpp index b9fdc06..e372f2e 100644 --- a/src/database/select_query.hpp +++ b/src/database/select_query.hpp @@ -135,7 +135,7 @@ struct SelectQuery: public Query }; template -auto select(const Table table) +auto select(const Table& table) { SelectQuery query(table.name); return query; -- cgit v1.2.3 From 0b51e3828116f6847865fae93893eb97a10d1cc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Thu, 23 Aug 2018 20:30:11 +0200 Subject: MaxHistoryLength now has some sensible default value if the user set a negative one --- src/database/database.cpp | 4 ---- src/database/query.cpp | 5 ----- src/database/query.hpp | 9 +++++---- 3 files changed, 5 insertions(+), 13 deletions(-) (limited to 'src/database') diff --git a/src/database/database.cpp b/src/database/database.cpp index 3578c04..9037ce1 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -162,10 +162,6 @@ Database::IrcChannelOptions Database::get_irc_channel_options_with_server_and_gl coptions.col() = get_first_non_empty(coptions.col(), soptions.col()); - coptions.col() = get_first_non_empty(coptions.col(), - soptions.col(), - goptions.col()); - return coptions; } diff --git a/src/database/query.cpp b/src/database/query.cpp index d72066e..5ec8599 100644 --- a/src/database/query.cpp +++ b/src/database/query.cpp @@ -6,11 +6,6 @@ void actual_bind(Statement& statement, const std::string& value, int index) statement.bind_text(index, value); } -void actual_bind(Statement& statement, const std::int64_t& value, int index) -{ - statement.bind_int64(index, value); -} - void actual_bind(Statement& statement, const OptionalBool& value, int index) { if (!value.is_set) diff --git a/src/database/query.hpp b/src/database/query.hpp index ba28b1a..ae6e946 100644 --- a/src/database/query.hpp +++ b/src/database/query.hpp @@ -12,13 +12,14 @@ #include void actual_bind(Statement& statement, const std::string& value, int index); -void actual_bind(Statement& statement, const std::int64_t& value, int index); -template ::value>* = 0> +void actual_bind(Statement& statement, const OptionalBool& value, int index); +template void actual_bind(Statement& statement, const T& value, int index) { - actual_bind(statement, static_cast(value), index); + static_assert(std::is_integral::value, + "Only a string, an optional-bool or an integer can be used."); + statement.bind_int64(index, static_cast(value)); } -void actual_bind(Statement& statement, const OptionalBool& value, int index); #ifdef DEBUG_SQL_QUERIES #include -- cgit v1.2.3 From 6c431b64d050a13853ebf3715fb6bf7986c0cff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Thu, 23 Aug 2018 22:16:42 +0200 Subject: Fix one more warning --- src/database/database.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/database') diff --git a/src/database/database.hpp b/src/database/database.hpp index f6b9d52..5fabadd 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -87,7 +87,7 @@ class Database struct Address: Column { static constexpr auto name = "address_"; }; struct ThrottleLimit: Column { static constexpr auto name = "throttlelimit_"; - ThrottleLimit(): Column(10) {} }; + ThrottleLimit(): Column(10) {} }; using MucLogLineTable = Table; using MucLogLine = MucLogLineTable::RowType; -- cgit v1.2.3 From 7d0df9b6ddee8db69ea0a511f031f32a4537a749 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Thu, 23 Aug 2018 15:21:12 +0200 Subject: Disable the throttle limit if negative Also, invalid values result in -1 being set --- src/database/database.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/database') diff --git a/src/database/database.hpp b/src/database/database.hpp index 5fabadd..4a413be 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -86,8 +86,8 @@ class Database struct Address: Column { static constexpr auto name = "address_"; }; - struct ThrottleLimit: Column { static constexpr auto name = "throttlelimit_"; - ThrottleLimit(): Column(10) {} }; + struct ThrottleLimit: Column { static constexpr auto name = "throttlelimit_"; + ThrottleLimit(): Column(10) {} }; using MucLogLineTable = Table; using MucLogLine = MucLogLineTable::RowType; -- cgit v1.2.3