Commit 61de6b1d authored by louiz’'s avatar louiz’

Revert "Use a different Date data type"

This reverts commit 857c7d39.
parent a90f196a
Version 8.0 Version 8.0
=========== ===========
- Changed the data type used to store the dates in the database. This
requires an automatic migration on the first start.
- Add a complete='true' in MAM’s iq result when appropriate - Add a complete='true' in MAM’s iq result when appropriate
- The “virtual” channel with an empty name (for example - The “virtual” channel with an empty name (for example
%irc.freenode.net@biboumi) has been entirely removed. %irc.freenode.net@biboumi) has been entirely removed.
......
...@@ -1009,9 +1009,9 @@ void Bridge::send_room_history(const std::string& hostname, std::string chan_nam ...@@ -1009,9 +1009,9 @@ void Bridge::send_room_history(const std::string& hostname, std::string chan_nam
chan_name.append(utils::empty_if_fixed_server("%" + hostname)); chan_name.append(utils::empty_if_fixed_server("%" + hostname));
for (const auto& line: lines) for (const auto& line: lines)
{ {
const DateTime& datetime = line.col<Database::Date>(); const auto seconds = line.col<Database::Date>();
this->xmpp.send_history_message(chan_name, line.col<Database::Nick>(), line.col<Database::Body>(), this->xmpp.send_history_message(chan_name, line.col<Database::Nick>(), line.col<Database::Body>(),
this->user_jid + "/" + resource, datetime); this->user_jid + "/" + resource, seconds);
} }
#else #else
(void)hostname; (void)hostname;
......
...@@ -48,7 +48,6 @@ void Database::open(const std::string& filename) ...@@ -48,7 +48,6 @@ void Database::open(const std::string& filename)
Database::db = std::move(new_db); Database::db = std::move(new_db);
Database::muc_log_lines.create(*Database::db); Database::muc_log_lines.create(*Database::db);
Database::muc_log_lines.upgrade(*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.create(*Database::db);
Database::global_options.upgrade(*Database::db); Database::global_options.upgrade(*Database::db);
Database::irc_server_options.create(*Database::db); Database::irc_server_options.create(*Database::db);
...@@ -60,9 +59,9 @@ void Database::open(const std::string& filename) ...@@ -60,9 +59,9 @@ void Database::open(const std::string& filename)
Database::after_connection_commands.create(*Database::db); Database::after_connection_commands.create(*Database::db);
Database::after_connection_commands.upgrade(*Database::db); Database::after_connection_commands.upgrade(*Database::db);
create_index<Database::Owner, Database::IrcChanName, Database::IrcServerName>(*Database::db, "archive_index", Database::muc_log_lines.get_name()); create_index<Database::Owner, Database::IrcChanName, Database::IrcServerName>(*Database::db, "archive_index", Database::muc_log_lines.get_name());
Database::db->init_session();
} }
Database::GlobalOptions Database::get_global_options(const std::string& owner) Database::GlobalOptions Database::get_global_options(const std::string& owner)
{ {
auto request = select(Database::global_options); auto request = select(Database::global_options);
...@@ -171,7 +170,7 @@ Database::IrcChannelOptions Database::get_irc_channel_options_with_server_and_gl ...@@ -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, 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) const std::string& body, const std::string& nick)
{ {
auto line = Database::muc_log_lines.row(); auto line = Database::muc_log_lines.row();
...@@ -182,7 +181,7 @@ std::string Database::store_muc_message(const std::string& owner, const std::str ...@@ -182,7 +181,7 @@ std::string Database::store_muc_message(const std::string& owner, const std::str
line.col<Owner>() = owner; line.col<Owner>() = owner;
line.col<IrcChanName>() = chan_name; line.col<IrcChanName>() = chan_name;
line.col<IrcServerName>() = server_name; line.col<IrcServerName>() = server_name;
line.col<Date>() = date; line.col<Date>() = std::chrono::duration_cast<std::chrono::seconds>(date.time_since_epoch()).count();
line.col<Body>() = body; line.col<Body>() = body;
line.col<Nick>() = nick; line.col<Nick>() = nick;
...@@ -206,21 +205,13 @@ std::vector<Database::MucLogLine> Database::get_muc_logs(const std::string& owne ...@@ -206,21 +205,13 @@ std::vector<Database::MucLogLine> Database::get_muc_logs(const std::string& owne
{ {
const auto start_time = utils::parse_datetime(start); const auto start_time = utils::parse_datetime(start);
if (start_time != -1) 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()) if (!end.empty())
{ {
const auto end_time = utils::parse_datetime(end); const auto end_time = utils::parse_datetime(end);
if (end_time != -1) 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) if (reference_record_id != Id::unset_value)
{ {
...@@ -233,9 +224,9 @@ std::vector<Database::MucLogLine> Database::get_muc_logs(const std::string& owne ...@@ -233,9 +224,9 @@ std::vector<Database::MucLogLine> Database::get_muc_logs(const std::string& owne
} }
if (paging == Database::Paging::first) if (paging == Database::Paging::first)
request.order_by() << Database::Date{} << " ASC"; request.order_by() << Database::Date{} << " ASC, " << Id{} << " ASC ";
else else
request.order_by() << Database::Date{} << " DESC"; request.order_by() << Database::Date{} << " DESC, " << Id{} << " DESC ";
if (limit >= 0) if (limit >= 0)
request.limit() << limit; request.limit() << limit;
...@@ -261,21 +252,13 @@ Database::MucLogLine Database::get_muc_log(const std::string& owner, const std:: ...@@ -261,21 +252,13 @@ Database::MucLogLine Database::get_muc_log(const std::string& owner, const std::
{ {
const auto start_time = utils::parse_datetime(start); const auto start_time = utils::parse_datetime(start);
if (start_time != -1) 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()) if (!end.empty())
{ {
const auto end_time = utils::parse_datetime(end); const auto end_time = utils::parse_datetime(end);
if (end_time != -1) 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); auto result = request.execute(*Database::db);
...@@ -359,12 +342,4 @@ Transaction::~Transaction() ...@@ -359,12 +342,4 @@ Transaction::~Transaction()
log_error("Failed to end SQL transaction: ", std::get<std::string>(result)); log_error("Failed to end SQL transaction: ", std::get<std::string>(result));
} }
} }
void Transaction::rollback()
{
this->success = false;
const auto result = Database::raw_exec("ROLLBACK");
if (std::get<bool>(result) == false)
log_error("Failed to rollback SQL transaction: ", std::get<std::string>(result));
}
#endif #endif
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
#include <database/engine.hpp> #include <database/engine.hpp>
#include <utils/optional_bool.hpp> #include <utils/optional_bool.hpp>
#include <utils/datetime.hpp>
#include <chrono> #include <chrono>
#include <string> #include <string>
...@@ -18,9 +17,11 @@ ...@@ -18,9 +17,11 @@
#include <memory> #include <memory>
#include <map> #include <map>
class Database class Database
{ {
public: public:
using time_point = std::chrono::system_clock::time_point;
struct RecordNotFound: public std::exception {}; struct RecordNotFound: public std::exception {};
enum class Paging { first, last }; enum class Paging { first, last };
...@@ -36,8 +37,7 @@ class Database ...@@ -36,8 +37,7 @@ class Database
struct Server: Column<std::string> { static constexpr auto name = "server_"; }; struct Server: Column<std::string> { static constexpr auto name = "server_"; };
struct OldDate: Column<std::chrono::system_clock::time_point::rep> { static constexpr auto name = "date_"; }; struct Date: Column<time_point::rep> { static constexpr auto name = "date_"; };
struct Date: Column<DateTime> { static constexpr auto name = "date_"; };
struct Body: Column<std::string> { static constexpr auto name = "body_"; }; struct Body: Column<std::string> { static constexpr auto name = "body_"; };
...@@ -88,8 +88,6 @@ class Database ...@@ -88,8 +88,6 @@ class Database
using MucLogLineTable = Table<Id, Uuid, Owner, IrcChanName, IrcServerName, Date, Body, Nick>; using MucLogLineTable = Table<Id, Uuid, Owner, IrcChanName, IrcServerName, Date, Body, Nick>;
using MucLogLine = MucLogLineTable::RowType; using MucLogLine = MucLogLineTable::RowType;
using OldMucLogLineTable = Table<Id, Uuid, Owner, IrcChanName, IrcServerName, OldDate, Body, Nick>;
using OldMucLogLine = OldMucLogLineTable::RowType;
using GlobalOptionsTable = Table<Id, Owner, MaxHistoryLength, RecordHistory, GlobalPersistent>; using GlobalOptionsTable = Table<Id, Owner, MaxHistoryLength, RecordHistory, GlobalPersistent>;
using GlobalOptions = GlobalOptionsTable::RowType; using GlobalOptions = GlobalOptionsTable::RowType;
...@@ -143,7 +141,7 @@ class Database ...@@ -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 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, 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 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); static bool has_roster_item(const std::string& local, const std::string& remote);
...@@ -170,13 +168,6 @@ class Database ...@@ -170,13 +168,6 @@ class Database
static std::unique_ptr<DatabaseEngine> db; static std::unique_ptr<DatabaseEngine> 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. * Some caches, to avoid doing very frequent query requests for a few options.
*/ */
...@@ -225,20 +216,7 @@ class Transaction ...@@ -225,20 +216,7 @@ class Transaction
public: public:
Transaction(); Transaction();
~Transaction(); ~Transaction();
void rollback();
bool success{false}; bool success{false};
}; };
template <typename... T>
void convert_date_format(DatabaseEngine& db, Table<T...> 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 */ #endif /* USE_DATABASE */
#pragma once
#include <utils/datetime.hpp>
#include <database/engine.hpp>
#include <logger/logger.hpp>
#include <database/postgresql_engine.hpp>
#include <database/sqlite3_engine.hpp>
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;
};
...@@ -13,7 +13,6 @@ ...@@ -13,7 +13,6 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <tuple> #include <tuple>
#include <map>
#include <set> #include <set>
class DatabaseEngine class DatabaseEngine
...@@ -28,10 +27,7 @@ class DatabaseEngine ...@@ -28,10 +27,7 @@ class DatabaseEngine
DatabaseEngine(DatabaseEngine&&) = delete; DatabaseEngine(DatabaseEngine&&) = delete;
DatabaseEngine& operator=(DatabaseEngine&&) = delete; DatabaseEngine& operator=(DatabaseEngine&&) = delete;
enum class EngineType { None, Postgresql, Sqlite3, }; virtual std::set<std::string> get_all_columns_from_table(const std::string& table_name) = 0;
virtual EngineType engine_type() const = 0;
virtual std::map<std::string, std::string> get_all_columns_from_table(const std::string& table_name) = 0;
virtual std::tuple<bool, std::string> raw_exec(const std::string& query) = 0; virtual std::tuple<bool, std::string> raw_exec(const std::string& query) = 0;
virtual std::unique_ptr<Statement> prepare(const std::string& query) = 0; virtual std::unique_ptr<Statement> prepare(const std::string& query) = 0;
virtual void extract_last_insert_rowid(Statement& statement) = 0; virtual void extract_last_insert_rowid(Statement& statement) = 0;
...@@ -39,17 +35,7 @@ class DatabaseEngine ...@@ -39,17 +35,7 @@ class DatabaseEngine
{ {
return {}; return {};
} }
virtual void convert_date_format(DatabaseEngine&) = 0; virtual std::string id_column_type() = 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}; int64_t last_inserted_rowid{-1};
}; };
#include <database/insert_query.hpp>
template <>
std::string before_value<Database::Date>()
{
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<Database::Date>()
{
if (Database::engine_type() == DatabaseEngine::EngineType::Sqlite3)
return ", \"unixepoch\")";
if (Database::engine_type() == DatabaseEngine::EngineType::Postgresql)
return ")";
return {};
}
...@@ -30,24 +30,6 @@ typename std::enable_if<N == sizeof...(T), void>::type ...@@ -30,24 +30,6 @@ typename std::enable_if<N == sizeof...(T), void>::type
update_autoincrement_id(std::tuple<T...>&, Statement&) update_autoincrement_id(std::tuple<T...>&, Statement&)
{} {}
template <typename T>
std::string before_value()
{
return {};
}
template <typename T>
std::string after_value()
{
return {};
}
template <>
std::string before_value<Database::Date>();
template <>
std::string after_value<Database::Date>();
struct InsertQuery: public Query struct InsertQuery: public Query
{ {
template <typename... T> template <typename... T>
...@@ -96,7 +78,7 @@ struct InsertQuery: public Query ...@@ -96,7 +78,7 @@ struct InsertQuery: public Query
template <typename... T> template <typename... T>
void insert_values(const std::tuple<T...>& columns) void insert_values(const std::tuple<T...>& columns)
{ {
this->body += " VALUES ("; this->body += "VALUES (";
this->insert_value(columns); this->insert_value(columns);
this->body += ")"; this->body += ")";
} }
...@@ -109,9 +91,7 @@ struct InsertQuery: public Query ...@@ -109,9 +91,7 @@ struct InsertQuery: public Query
if (!std::is_same<ColumnType, Id>::value) if (!std::is_same<ColumnType, Id>::value)
{ {
this->body += before_value<ColumnType>();
this->body += "$" + std::to_string(index++); this->body += "$" + std::to_string(index++);
this->body += after_value<ColumnType>();
if (N != sizeof...(T) - 1) if (N != sizeof...(T) - 1)
this->body += ", "; this->body += ", ";
} }
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
#ifdef PQ_FOUND #ifdef PQ_FOUND
#include <utils/scopeguard.hpp> #include <utils/scopeguard.hpp>
#include <utils/tolower.hpp>
#include <database/query.hpp> #include <database/query.hpp>
...@@ -13,7 +12,6 @@ ...@@ -13,7 +12,6 @@
#include <logger/logger.hpp> #include <logger/logger.hpp>
#include <cstring> #include <cstring>
#include <database/database.hpp>
PostgresqlEngine::PostgresqlEngine(PGconn*const conn): PostgresqlEngine::PostgresqlEngine(PGconn*const conn):
conn(conn) conn(conn)
...@@ -54,14 +52,14 @@ std::unique_ptr<DatabaseEngine> PostgresqlEngine::open(const std::string& connin ...@@ -54,14 +52,14 @@ std::unique_ptr<DatabaseEngine> PostgresqlEngine::open(const std::string& connin
return std::make_unique<PostgresqlEngine>(con); return std::make_unique<PostgresqlEngine>(con);
} }
std::map<std::string, std::string> PostgresqlEngine::get_all_columns_from_table(const std::string& table_name) std::set<std::string> 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); auto statement = this->prepare(query);
std::map<std::string, std::string> columns; std::set<std::string> columns;
while (statement->step() == StepResult::Row) 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; return columns;
} }
...@@ -98,41 +96,9 @@ std::string PostgresqlEngine::get_returning_id_sql_string(const std::string& col ...@@ -98,41 +96,9 @@ std::string PostgresqlEngine::get_returning_id_sql_string(const std::string& col
return " RETURNING " + col_name; return " RETURNING " + col_name;
} }
std::string PostgresqlEngine::id_column_type() const std::string PostgresqlEngine::id_column_type()
{ {
return "SERIAL"; 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<bool>(result))
log_error("Failed to execute query: ", std::get<std::string>(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<bool>(res))
log_error("Failed to set UTC timezone: ", std::get<std::string>(res));
}
long double PostgresqlEngine::epoch_to_floating_value(long double seconds) const
{
return seconds;
}
#endif #endif
...@@ -23,22 +23,13 @@ class PostgresqlEngine: public DatabaseEngine ...@@ -23,22 +23,13 @@ class PostgresqlEngine: public DatabaseEngine
~PostgresqlEngine(); ~PostgresqlEngine();
static std::unique_ptr<DatabaseEngine> open(const std::string& string); static std::unique_ptr<DatabaseEngine> open(const std::string& string);
EngineType engine_type() const override
{
return EngineType::Postgresql;
}
std::map<std::string, std::string> get_all_columns_from_table(const std::string& table_name) override final; std::set<std::string> get_all_columns_from_table(const std::string& table_name) override final;
std::tuple<bool, std::string> raw_exec(const std::string& query) override final; std::tuple<bool, std::string> raw_exec(const std::string& query) override final;
std::unique_ptr<Statement> prepare(const std::string& query) override; std::unique_ptr<Statement> prepare(const std::string& query) override;
void extract_last_insert_rowid(Statement& statement) override; void extract_last_insert_rowid(Statement& statement) override;
std::string get_returning_id_sql_string(const std::string& col_name) override; std::string get_returning_id_sql_string(const std::string& col_name) override;
std::string id_column_type() const override; std::string id_column_type() 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: private:
PGconn* const conn; PGconn* const conn;
}; };
......
...@@ -21,13 +21,6 @@ void actual_bind(Statement& statement, const OptionalBool& value, int index) ...@@ -21,13 +21,6 @@ void actual_bind(Statement& statement, const OptionalBool& value, int index)
statement.bind_int64(index, -1); 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<long double>(epoch) / std::chrono::system_clock::period::den);
statement.bind_text(index, result);
}
void actual_add_param(Query& query, const std::string& val) void actual_add_param(Query& query, const std::string& val)
{ {
query.params.push_back(val); query.params.push_back(val);
...@@ -56,12 +49,3 @@ Query& operator<<(Query& query, const std::string& str) ...@@ -56,12 +49,3 @@ Query& operator<<(Query& query, const std::string& str)
actual_add_param(query, str); actual_add_param(query, str);
return query; 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;
}
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
#include <biboumi.h> #include <biboumi.h>
#include <utils/optional_bool.hpp> #include <utils/optional_bool.hpp>
#include <utils/datetime.hpp>
#include <database/statement.hpp> #include <database/statement.hpp>
#include <database/column.hpp> #include <database/column.hpp>
...@@ -11,7 +10,6 @@ ...@@ -11,7 +10,6 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include <database/datetime_writer.hpp>
void actual_bind(Statement& statement, const std::string& value, int index); 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::int64_t& value, int index);
...@@ -21,7 +19,6 @@ void actual_bind(Statement& statement, const T& value, int index) ...@@ -21,7 +19,6 @@ void actual_bind(Statement& statement, const T& value, int index)
actual_bind(statement, static_cast<std::int64_t>(value), index); actual_bind(statement, static_cast<std::int64_t>(value), index);
} }
void actual_bind(Statement& statement, const OptionalBool& value, int 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 #ifdef DEBUG_SQL_QUERIES
#include <utils/scopetimer.hpp> #include <utils/scopetimer.hpp>
...@@ -77,13 +74,15 @@ void actual_add_param(Query& query, const std::string& val); ...@@ -77,13 +74,15 @@ void actual_add_param(Query& query, const std::string& val);
void actual_add_param(Query& query, const OptionalBool& val); void actual_add_param(Query& query, const OptionalBool& val);
template <typename T> template <typename T>
typename std::enable_if<!std::is_arithmetic<T>::value, Query&>::type typename std::enable_if<!std::is_integral<T>::value, Query&>::type
operator<<(Query& query, const T&) operator<<(Query& query, const T&)
{ {
query.body += T::name; query.body += T::name;
return query; return query;
} }
Query& operator<<(Query& query, const char* str);
Query& operator<<(Query& query, const std::string& str);
template <typename Integer> template <typename Integer>
typename std::enable_if<std::is_integral<Integer>::