database.cpp 8.82 KB
Newer Older
1 2 3
#include "biboumi.h"
#ifdef USE_DATABASE

4
#include <database/database.hpp>
5
#include <uuid/uuid.h>
6
#include <utils/get_first_non_empty.hpp>
7
#include <utils/time.hpp>
8

9
#include <config/config.hpp>
louiz’'s avatar
louiz’ committed
10 11
#include <database/sqlite3_engine.hpp>
#include <database/postgresql_engine.hpp>
12

louiz’'s avatar
louiz’ committed
13
#include <database/engine.hpp>
14 15
#include <database/index.hpp>

louiz’'s avatar
louiz’ committed
16 17
#include <memory>

18
#include <sqlite3.h>
19

louiz’'s avatar
louiz’ committed
20 21 22 23 24
std::unique_ptr<DatabaseEngine> Database::db;
Database::MucLogLineTable Database::muc_log_lines("muclogline_");
Database::GlobalOptionsTable Database::global_options("globaloptions_");
Database::IrcServerOptionsTable Database::irc_server_options("ircserveroptions_");
Database::IrcChannelOptionsTable Database::irc_channel_options("ircchanneloptions_");
25
Database::RosterTable Database::roster("roster");
26
std::map<Database::CacheKey, Database::EncodingIn::real_type> Database::encoding_in_cache{};
27

28 29 30
Database::GlobalPersistent::GlobalPersistent():
    Column<bool>{Config::get_bool("persistent_by_default", false)}
{}
31

32
void Database::open(const std::string& filename)
33
{
34 35 36
  // Try to open the specified database.
  // Close and replace the previous database pointer if it succeeded. If it did
  // not, just leave things untouched
louiz’'s avatar
louiz’ committed
37 38 39
  std::unique_ptr<DatabaseEngine> new_db;
  static const auto psql_prefix = "postgresql://"s;
  if (filename.substr(0, psql_prefix.size()) == psql_prefix)
40
    new_db = PostgresqlEngine::open(filename);
louiz’'s avatar
louiz’ committed
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
  else
    new_db = Sqlite3Engine::open(filename);
  if (!new_db)
    return;
  Database::db = std::move(new_db);
  Database::muc_log_lines.create(*Database::db);
  Database::muc_log_lines.upgrade(*Database::db);
  Database::global_options.create(*Database::db);
  Database::global_options.upgrade(*Database::db);
  Database::irc_server_options.create(*Database::db);
  Database::irc_server_options.upgrade(*Database::db);
  Database::irc_channel_options.create(*Database::db);
  Database::irc_channel_options.upgrade(*Database::db);
  Database::roster.create(*Database::db);
  Database::roster.upgrade(*Database::db);
  create_index<Database::Owner, Database::IrcChanName, Database::IrcServerName>(*Database::db, "archive_index", Database::muc_log_lines.get_name());
57 58 59
}


60
Database::GlobalOptions Database::get_global_options(const std::string& owner)
61
{
62 63
  auto request = Database::global_options.select();
  request.where() << Owner{} << "=" << owner;
64 65

  Database::GlobalOptions options{Database::global_options.get_name()};
louiz’'s avatar
louiz’ committed
66
  auto result = request.execute(*Database::db);
67 68 69 70 71
  if (result.size() == 1)
    options = result.front();
  else
    options.col<Owner>() = owner;
  return options;
72 73
}

74
Database::IrcServerOptions Database::get_irc_server_options(const std::string& owner, const std::string& server)
75
{
76 77
  auto request = Database::irc_server_options.select();
  request.where() << Owner{} << "=" << owner << " and " << Server{} << "=" << server;
78 79

  Database::IrcServerOptions options{Database::irc_server_options.get_name()};
louiz’'s avatar
louiz’ committed
80
  auto result = request.execute(*Database::db);
81 82 83 84 85 86 87 88
  if (result.size() == 1)
    options = result.front();
  else
    {
      options.col<Owner>() = owner;
      options.col<Server>() = server;
    }
  return options;
89 90
}

91
Database::IrcChannelOptions Database::get_irc_channel_options(const std::string& owner, const std::string& server, const std::string& channel)
92
{
93 94 95 96
  auto request = Database::irc_channel_options.select();
  request.where() << Owner{} << "=" << owner <<\
          " and " << Server{} << "=" << server <<\
          " and " << Channel{} << "=" << channel;
97
  Database::IrcChannelOptions options{Database::irc_channel_options.get_name()};
louiz’'s avatar
louiz’ committed
98
  auto result = request.execute(*Database::db);
99 100 101 102 103 104 105 106 107
  if (result.size() == 1)
    options = result.front();
  else
    {
      options.col<Owner>() = owner;
      options.col<Server>() = server;
      options.col<Channel>() = channel;
    }
  return options;
108 109
}

110 111
Database::IrcChannelOptions Database::get_irc_channel_options_with_server_default(const std::string& owner, const std::string& server,
                                                                                  const std::string& channel)
112 113 114
{
  auto coptions = Database::get_irc_channel_options(owner, server, channel);
  auto soptions = Database::get_irc_server_options(owner, server);
115

116 117 118 119
  coptions.col<EncodingIn>() = get_first_non_empty(coptions.col<EncodingIn>(),
                                                   soptions.col<EncodingIn>());
  coptions.col<EncodingOut>() = get_first_non_empty(coptions.col<EncodingOut>(),
                                                    soptions.col<EncodingOut>());
120

121 122
  coptions.col<MaxHistoryLength>() = get_first_non_empty(coptions.col<MaxHistoryLength>(),
                                                         soptions.col<MaxHistoryLength>());
123 124 125 126

  return coptions;
}

127
Database::IrcChannelOptions Database::get_irc_channel_options_with_server_and_global_default(const std::string& owner, const std::string& server, const std::string& channel)
128 129 130 131 132
{
  auto coptions = Database::get_irc_channel_options(owner, server, channel);
  auto soptions = Database::get_irc_server_options(owner, server);
  auto goptions = Database::get_global_options(owner);

133 134
  coptions.col<EncodingIn>() = get_first_non_empty(coptions.col<EncodingIn>(),
                                                   soptions.col<EncodingIn>());
135

136 137 138 139 140 141
  coptions.col<EncodingOut>() = get_first_non_empty(coptions.col<EncodingOut>(),
                                                    soptions.col<EncodingOut>());

  coptions.col<MaxHistoryLength>() = get_first_non_empty(coptions.col<MaxHistoryLength>(),
                                                         soptions.col<MaxHistoryLength>(),
                                                         goptions.col<MaxHistoryLength>());
142

143 144 145
  return coptions;
}

146 147 148
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& body, const std::string& nick)
149
{
150
  auto line = Database::muc_log_lines.row();
151

152 153
  auto uuid = Database::gen_uuid();

154 155 156 157 158 159 160
  line.col<Uuid>() = uuid;
  line.col<Owner>() = owner;
  line.col<IrcChanName>() = chan_name;
  line.col<IrcServerName>() = server_name;
  line.col<Date>() = std::chrono::duration_cast<std::chrono::seconds>(date.time_since_epoch()).count();
  line.col<Body>() = body;
  line.col<Nick>() = nick;
161

162
  line.save(Database::db);
163 164

  return uuid;
165 166
}

167
std::vector<Database::MucLogLine> Database::get_muc_logs(const std::string& owner, const std::string& chan_name, const std::string& server,
168
                                                   int limit, const std::string& start, const std::string& end)
169
{
170 171 172 173
  auto request = Database::muc_log_lines.select();
  request.where() << Database::Owner{} << "=" << owner << \
          " and " << Database::IrcChanName{} << "=" << chan_name << \
          " and " << Database::IrcServerName{} << "=" << server;
174 175 176 177 178

  if (!start.empty())
    {
      const auto start_time = utils::parse_datetime(start);
      if (start_time != -1)
179
        request << " and " << Database::Date{} << ">=" << start_time;
180 181 182 183 184
    }
  if (!end.empty())
    {
      const auto end_time = utils::parse_datetime(end);
      if (end_time != -1)
185
        request << " and " << Database::Date{} << "<=" << end_time;
186
    }
187 188 189 190 191 192

  request.order_by() << Id{} << " DESC ";

  if (limit >= 0)
    request.limit() << limit;

louiz’'s avatar
louiz’ committed
193
  auto result = request.execute(*Database::db);
194 195

  return {result.crbegin(), result.crend()};
196 197
}

198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
void Database::add_roster_item(const std::string& local, const std::string& remote)
{
  auto roster_item = Database::roster.row();

  roster_item.col<Database::LocalJid>() = local;
  roster_item.col<Database::RemoteJid>() = remote;

  roster_item.save(Database::db);
}

void Database::delete_roster_item(const std::string& local, const std::string& remote)
{
  Query query("DELETE FROM "s + Database::roster.get_name());
  query << " WHERE " << Database::RemoteJid{} << "=" << remote << \
           " AND " << Database::LocalJid{} << "=" << local;

louiz’'s avatar
louiz’ committed
214
//  query.execute(*Database::db);
215 216 217 218 219 220 221 222
}

bool Database::has_roster_item(const std::string& local, const std::string& remote)
{
  auto query = Database::roster.select();
  query.where() << Database::LocalJid{} << "=" << local << \
        " and " << Database::RemoteJid{} << "=" << remote;

louiz’'s avatar
louiz’ committed
223
  auto res = query.execute(*Database::db);
224 225 226 227 228 229 230 231 232

  return !res.empty();
}

std::vector<Database::RosterItem> Database::get_contact_list(const std::string& local)
{
  auto query = Database::roster.select();
  query.where() << Database::LocalJid{} << "=" << local;

louiz’'s avatar
louiz’ committed
233
  return query.execute(*Database::db);
234 235 236 237 238 239
}

std::vector<Database::RosterItem> Database::get_full_roster()
{
  auto query = Database::roster.select();

louiz’'s avatar
louiz’ committed
240
  return query.execute(*Database::db);
241 242
}

243 244
void Database::close()
{
245
  Database::db = nullptr;
246
}
247

248 249 250 251 252 253 254 255 256
std::string Database::gen_uuid()
{
  char uuid_str[37];
  uuid_t uuid;
  uuid_generate(uuid);
  uuid_unparse(uuid, uuid_str);
  return uuid_str;
}

257
#endif