From 573e49a7ead331219eb6f0d3ca9cf83e793a5c9c Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 23 Apr 2012 05:08:26 -0400 Subject: Reworked live SQL support yet again --- modules/database/db_flatfile.cpp | 12 +- modules/database/db_old.cpp | 4 +- modules/database/db_plain.cpp | 101 ++++---- modules/database/db_sql.cpp | 50 +--- modules/database/db_sql_live.cpp | 311 +++++++++++++++++++++++ modules/database/db_sql_live_read.cpp | 179 -------------- modules/database/db_sql_live_write.cpp | 440 --------------------------------- 7 files changed, 384 insertions(+), 713 deletions(-) create mode 100644 modules/database/db_sql_live.cpp delete mode 100644 modules/database/db_sql_live_read.cpp delete mode 100644 modules/database/db_sql_live_write.cpp (limited to 'modules/database') diff --git a/modules/database/db_flatfile.cpp b/modules/database/db_flatfile.cpp index 259851b18..14e894e0c 100644 --- a/modules/database/db_flatfile.cpp +++ b/modules/database/db_flatfile.cpp @@ -91,8 +91,8 @@ class DBFlatFile : public Module } SerializeType *st = NULL; - Serializable::serialized_data data; - std::multimap objects; + Serialize::Data data; + std::multimap objects; for (Anope::string buf, token; std::getline(db, buf.str());) { spacesepstream sep(buf); @@ -121,11 +121,11 @@ class DBFlatFile : public Module { SerializeType *stype = SerializeType::Find(type_order[i]); - std::multimap::iterator it = objects.find(stype), it_end = objects.upper_bound(stype); + std::multimap::iterator it = objects.find(stype), it_end = objects.upper_bound(stype); if (it == objects.end()) continue; for (; it != it_end; ++it) - it->first->Create(it->second); + it->first->Unserialize(NULL, it->second); } db.close(); @@ -155,10 +155,10 @@ class DBFlatFile : public Module for (std::list::const_iterator it = items.begin(), it_end = items.end(); it != it_end; ++it) { Serializable *base = *it; - Serializable::serialized_data data = base->serialize(); + Serialize::Data data = base->serialize(); db << "OBJECT " << base->serialize_name() << "\n"; - for (Serializable::serialized_data::iterator dit = data.begin(), dit_end = data.end(); dit != dit_end; ++dit) + for (Serialize::Data::iterator dit = data.begin(), dit_end = data.end(); dit != dit_end; ++dit) db << "DATA " << dit->first << " " << dit->second.astr() << "\n"; db << "END\n"; } diff --git a/modules/database/db_old.cpp b/modules/database/db_old.cpp index c455aa265..2783a29b2 100644 --- a/modules/database/db_old.cpp +++ b/modules/database/db_old.cpp @@ -516,7 +516,7 @@ static void LoadNicks() m->sender = sbuf; READ(read_string(m->text, f)); m->owner = nc->display; - nc->memos.memos.push_back(m); + nc->memos.memos->push_back(m); } READ(read_uint16(&u16, f)); READ(read_int16(&i16, f)); @@ -798,7 +798,7 @@ static void LoadChannels() m->sender = sbuf; READ(read_string(m->text, f)); m->owner = ci->name; - ci->memos.memos.push_back(m); + ci->memos.memos->push_back(m); } READ(read_string(buffer, f)); diff --git a/modules/database/db_plain.cpp b/modules/database/db_plain.cpp index edf41c7f0..935ef95a2 100644 --- a/modules/database/db_plain.cpp +++ b/modules/database/db_plain.cpp @@ -17,6 +17,11 @@ Anope::string DatabaseFile; std::stringstream db_buffer; +struct ExtensibleString : Anope::string, ExtensibleItem +{ + ExtensibleString(const Anope::string &s) : Anope::string(s) { } +}; + class DatabaseException : public CoreException { public: @@ -60,7 +65,7 @@ EventReturn OnDatabaseRead(const std::vector ¶ms) LoadOperInfo(otherparams); return EVENT_CONTINUE; - } +} EventReturn OnDatabaseReadMetadata(NickCore *nc, const Anope::string &key, const std::vector ¶ms) { @@ -93,7 +98,7 @@ EventReturn OnDatabaseReadMetadata(NickCore *nc, const Anope::string &key, const m->SetFlag(MF_RECEIPT); } m->text = params[params.size() - 1]; - nc->memos.memos.push_back(m); + nc->memos.memos->push_back(m); } else if (key.equals_ci("MIG")) nc->memos.ignores.push_back(params[0].ci_str()); @@ -173,7 +178,7 @@ EventReturn OnDatabaseReadMetadata(ChannelInfo *ci, const Anope::string &key, co if (!provider) throw DatabaseException("Old access entry for nonexistant provider"); - ChanAccess *access = provider->Create(); + ChanAccess *access = const_cast(provider->Create()); access->ci = ci; access->mask = params[0]; access->Unserialize(params[1]); @@ -189,7 +194,7 @@ EventReturn OnDatabaseReadMetadata(ChannelInfo *ci, const Anope::string &key, co if (!provider) throw DatabaseException("Access entry for nonexistant provider " + params[0]); - ChanAccess *access = provider->Create(); + ChanAccess *access = const_cast(provider->Create()); access->ci = ci; access->mask = params[1]; access->Unserialize(params[2]); @@ -222,18 +227,18 @@ EventReturn OnDatabaseReadMetadata(ChannelInfo *ci, const Anope::string &key, co } else if (key.equals_ci("LOG")) { - LogSetting l; - - l.ci = ci; - l.service_name = params[0]; - l.command_service = params[1]; - l.command_name = params[2]; - l.method = params[3]; - l.creator = params[4]; - l.created = params[5].is_pos_number_only() ? convertTo(params[5]) : Anope::CurTime; - l.extra = params.size() > 6 ? params[6] : ""; - - ci->log_settings.push_back(l); + LogSetting *l = new LogSetting(); + + l->ci = ci; + l->service_name = params[0]; + l->command_service = params[1]; + l->command_name = params[2]; + l->method = params[3]; + l->creator = params[4]; + l->created = params[5].is_pos_number_only() ? convertTo(params[5]) : Anope::CurTime; + l->extra = params.size() > 6 ? params[6] : ""; + + ci->log_settings->push_back(l); } else if (key.equals_ci("MLOCK")) { @@ -241,12 +246,12 @@ EventReturn OnDatabaseReadMetadata(ChannelInfo *ci, const Anope::string &key, co Anope::string mode_name = params[1]; Anope::string setter = params[2]; time_t mcreated = params[3].is_pos_number_only() ? convertTo(params[3]) : Anope::CurTime; - Anope::string param = params.size() > 4 ? params[4] : ""; - for (size_t i = CMODE_BEGIN + 1; i < CMODE_END; ++i) + Anope::string param = params.size() > 4 ? params[4] : ""; + for (size_t i = CMODE_BEGIN + 1; i < CMODE_END; ++i) if (ChannelModeNameStrings[i] == mode_name) { ChannelModeName n = static_cast(i); - ci->mode_locks.insert(std::make_pair(n, ModeLock(ci, set, n, param, setter, mcreated))); + ci->mode_locks->insert(std::make_pair(n, new ModeLock(ci, set, n, param, setter, mcreated))); break; } } @@ -263,7 +268,7 @@ EventReturn OnDatabaseReadMetadata(ChannelInfo *ci, const Anope::string &key, co m->SetFlag(MF_RECEIPT); } m->text = params[params.size() - 1]; - ci->memos.memos.push_back(m); + ci->memos.memos->push_back(m); } else if (key.equals_ci("MIG")) ci->memos.ignores.push_back(params[0].ci_str()); @@ -666,9 +671,9 @@ class DBPlain : public Module db_buffer << "VER 2" << endl; - for (nickcore_map::const_iterator nit = NickCoreList.begin(), nit_end = NickCoreList.end(); nit != nit_end; ++nit) + for (nickcore_map::const_iterator nit = NickCoreList->begin(), nit_end = NickCoreList->end(); nit != nit_end; ++nit) { - NickCore *nc = nit->second; + const NickCore *nc = nit->second; db_buffer << "NC " << nc->display << " " << nc->pass << endl; @@ -683,34 +688,35 @@ class DBPlain : public Module if (!nc->access.empty()) { - for (std::vector::iterator it = nc->access.begin(), it_end = nc->access.end(); it != it_end; ++it) + for (std::vector::const_iterator it = nc->access.begin(), it_end = nc->access.end(); it != it_end; ++it) db_buffer << "MD ACCESS " << *it << endl; } if (!nc->cert.empty()) { - for (std::vector::iterator it = nc->cert.begin(), it_end = nc->cert.end(); it != it_end; ++it) + for (std::vector::const_iterator it = nc->cert.begin(), it_end = nc->cert.end(); it != it_end; ++it) db_buffer << "MD CERT " << *it << endl; } if (nc->FlagCount()) db_buffer << "MD FLAGS " << nc->ToString() << endl; - MemoInfo *mi = &nc->memos; - for (unsigned k = 0, end = mi->memos.size(); k < end; ++k) + const MemoInfo *mi = &nc->memos; + for (unsigned k = 0, end = mi->memos->size(); k < end; ++k) { - db_buffer << "MD MI " << mi->memos[k]->time << " " << mi->memos[k]->sender; - if (mi->memos[k]->HasFlag(MF_UNREAD)) + const Memo *m = mi->GetMemo(k); + db_buffer << "MD MI " << m->time << " " << m->sender; + if (m->HasFlag(MF_UNREAD)) db_buffer << " UNREAD"; - if (mi->memos[k]->HasFlag(MF_RECEIPT)) + if (m->HasFlag(MF_RECEIPT)) db_buffer << " RECEIPT"; - db_buffer << " :" << mi->memos[k]->text << endl; + db_buffer << " :" << m->text << endl; } for (unsigned k = 0, end = mi->ignores.size(); k < end; ++k) db_buffer << "MD MIG " << Anope::string(mi->ignores[k]) << endl; //FOREACH_MOD(I_OnDatabaseWriteMetadata, OnDatabaseWriteMetadata(WriteMetadata, nc)); } - for (nickalias_map::const_iterator it = NickAliasList.begin(), it_end = NickAliasList.end(); it != it_end; ++it) + for (nickalias_map::const_iterator it = NickAliasList->begin(), it_end = NickAliasList->end(); it != it_end; ++it) { - NickAlias *na = it->second; + const NickAlias *na = it->second; db_buffer << "NA " << na->nc->display << " " << na->nick << " " << na->time_registered << " " << na->last_seen << endl; if (!na->last_usermask.empty()) @@ -729,7 +735,7 @@ class DBPlain : public Module //FOREACH_MOD(I_OnDatabaseWriteMetadata, OnDatabaseWriteMetadata(WriteMetadata, na)); } - for (Anope::insensitive_map::const_iterator it = BotListByNick.begin(), it_end = BotListByNick.end(); it != it_end; ++it) + for (Anope::insensitive_map::const_iterator it = BotListByNick->begin(), it_end = BotListByNick->end(); it != it_end; ++it) { BotInfo *bi = it->second; @@ -741,9 +747,9 @@ class DBPlain : public Module db_buffer << "MD FLAGS " << bi->ToString() << endl; } - for (registered_channel_map::const_iterator cit = RegisteredChannelList.begin(), cit_end = RegisteredChannelList.end(); cit != cit_end; ++cit) + for (registered_channel_map::const_iterator cit = RegisteredChannelList->begin(), cit_end = RegisteredChannelList->end(); cit != cit_end; ++cit) { - ChannelInfo *ci = cit->second; + const ChannelInfo *ci = cit->second; db_buffer << "CH " << ci->name << " " << ci->time_registered << " " << ci->last_used << endl; db_buffer << "MD BANTYPE " << ci->bantype << endl; @@ -774,7 +780,7 @@ class DBPlain : public Module } for (unsigned k = 0, end = ci->GetAccessCount(); k < end; ++k) { - ChanAccess *access = ci->GetAccess(k); + const ChanAccess *access = ci->GetAccess(k); db_buffer << "MD ACCESS2 " << access->provider->name << " " << access->mask << " " << access->Serialize() << " " << access->last_seen << " " << access->creator << " " << access->created << endl; } for (unsigned k = 0, end = ci->GetAkickCount(); k < end; ++k) @@ -785,28 +791,29 @@ class DBPlain : public Module db_buffer << ci->GetAkick(k)->reason; db_buffer << endl; } - for (unsigned k = 0, end = ci->log_settings.size(); k < end; ++k) + for (unsigned k = 0, end = ci->log_settings->size(); k < end; ++k) { - LogSetting &l = ci->log_settings[k]; + const LogSetting &l = *ci->log_settings->at(k); db_buffer << "MD LOG " << l.service_name << " " << l.command_service << " " << l.command_name << " " << l.method << " " << l.creator << " " << l.created << " " << l.extra << endl; } - for (std::multimap::const_iterator it = ci->GetMLock().begin(), it_end = ci->GetMLock().end(); it != it_end; ++it) + for (ChannelInfo::ModeList::const_iterator it = ci->GetMLock().begin(), it_end = ci->GetMLock().end(); it != it_end; ++it) { - const ModeLock &ml = it->second; + const ModeLock &ml = *it->second; ChannelMode *cm = ModeManager::FindChannelModeByName(ml.name); if (cm != NULL) db_buffer << "MD MLOCK " << (ml.set ? 1 : 0) << " " << cm->NameAsString() << " " << ml.setter << " " << ml.created << " " << ml.param << endl; } - MemoInfo *memos = &ci->memos; - for (unsigned k = 0, end = memos->memos.size(); k < end; ++k) + const MemoInfo *memos = &ci->memos; + for (unsigned k = 0, end = memos->memos->size(); k < end; ++k) { - db_buffer << "MD MI " << memos->memos[k]->time << " " << memos->memos[k]->sender; - if (memos->memos[k]->HasFlag(MF_UNREAD)) + const Memo *m = memos->GetMemo(k); + db_buffer << "MD MI " << m->time << " " << m->sender; + if (m->HasFlag(MF_UNREAD)) db_buffer << " UNREAD"; - if (memos->memos[k]->HasFlag(MF_RECEIPT)) + if (m->HasFlag(MF_RECEIPT)) db_buffer << " RECEIPT"; - db_buffer << " :" << memos->memos[k]->text << endl; + db_buffer << " :" << m->text << endl; } for (unsigned k = 0, end = memos->ignores.size(); k < end; ++k) db_buffer << "MD MIG " << Anope::string(memos->ignores[k]) << endl; @@ -839,7 +846,7 @@ class DBPlain : public Module XLineManager *xl = *it; for (unsigned i = 0, end = xl->GetCount(); i < end; ++i) { - XLine *x = xl->GetEntry(i); + const XLine *x = xl->GetEntry(i); db_buffer << "OS SXLINE " << xl->Type() << " " << x->GetUser() << " " << x->GetHost() << " " << x->By << " " << x->Created << " " << x->Expires << " :" << x->Reason << endl; } } diff --git a/modules/database/db_sql.cpp b/modules/database/db_sql.cpp index c8379ca12..c2ba84a5f 100644 --- a/modules/database/db_sql.cpp +++ b/modules/database/db_sql.cpp @@ -60,41 +60,20 @@ class DBSQL : public Module } } - void AlterTable(const Anope::string &table, std::set &data, const Serializable::serialized_data &newd) - { - for (Serializable::serialized_data::const_iterator it = newd.begin(), it_end = newd.end(); it != it_end; ++it) - { - if (data.count(it->first) > 0) - continue; - - data.insert(it->first); - - Anope::string query_text = "ALTER TABLE `" + table + "` ADD `" + it->first + "` "; - if (it->second.getType() == Serialize::DT_INT) - query_text += "int(11)"; - else if (it->second.getMax() > 0) - query_text += "varchar(" + stringify(it->second.getMax()) + ")"; - else - query_text += "text"; - - this->RunBackground(SQLQuery(query_text)); - } - } - - void Insert(const Anope::string &table, const Serializable::serialized_data &data) + void Insert(const Anope::string &table, const Serialize::Data &data) { Anope::string query_text = "INSERT INTO `" + table + "` ("; - for (Serializable::serialized_data::const_iterator it = data.begin(), it_end = data.end(); it != it_end; ++it) + for (Serialize::Data::const_iterator it = data.begin(), it_end = data.end(); it != it_end; ++it) query_text += "`" + it->first + "`,"; query_text.erase(query_text.end() - 1); query_text += ") VALUES ("; - for (Serializable::serialized_data::const_iterator it = data.begin(), it_end = data.end(); it != it_end; ++it) + for (Serialize::Data::const_iterator it = data.begin(), it_end = data.end(); it != it_end; ++it) query_text += "@" + it->first + "@,"; query_text.erase(query_text.end() - 1); query_text += ")"; SQLQuery query(query_text); - for (Serializable::serialized_data::const_iterator it = data.begin(), it_end = data.end(); it != it_end; ++it) + for (Serialize::Data::const_iterator it = data.begin(), it_end = data.end(); it != it_end; ++it) query.setValue(it->first, it->second.astr()); this->RunBackground(query); @@ -132,28 +111,21 @@ class DBSQL : public Module this->DropAll(); - std::map > table_layout; for (std::list::const_iterator it = items.begin(), it_end = items.end(); it != it_end; ++it) { Serializable *base = *it; - Serializable::serialized_data data = base->serialize(); + Serialize::Data data = base->serialize(); if (data.empty()) continue; - std::set &layout = table_layout[base->serialize_name()]; - if (layout.empty()) - { - this->RunBackground(this->sql->CreateTable(base->serialize_name(), data)); - - for (Serializable::serialized_data::iterator it2 = data.begin(), it2_end = data.end(); it2 != it2_end; ++it2) - layout.insert(it2->first); - } - else - this->AlterTable(base->serialize_name(), layout, data); + std::vector create_queries = this->sql->CreateTable(base->serialize_name(), data); + for (unsigned i = 0; i < create_queries.size(); ++i) + this->RunBackground(create_queries[i]); this->Insert(base->serialize_name(), data); } + return EVENT_CONTINUE; } @@ -174,12 +146,12 @@ class DBSQL : public Module SQLResult res = this->sql->RunQuery(query); for (int j = 0; j < res.Rows(); ++j) { - Serializable::serialized_data data; + Serialize::Data data; const std::map &row = res.Row(j); for (std::map::const_iterator rit = row.begin(), rit_end = row.end(); rit != rit_end; ++rit) data[rit->first] << rit->second; - sb->Create(data); + sb->Unserialize(NULL, data); } } diff --git a/modules/database/db_sql_live.cpp b/modules/database/db_sql_live.cpp new file mode 100644 index 000000000..4b939e23b --- /dev/null +++ b/modules/database/db_sql_live.cpp @@ -0,0 +1,311 @@ +#include "module.h" +#include "../extra/sql.h" +#include "../commands/os_session.h" + +class MySQLInterface : public SQLInterface +{ + public: + MySQLInterface(Module *o) : SQLInterface(o) { } + + void OnResult(const SQLResult &r) anope_override + { + Log(LOG_DEBUG) << "SQLive successfully executed query: " << r.finished_query; + } + + void OnError(const SQLResult &r) anope_override + { + if (!r.GetQuery().query.empty()) + Log(LOG_DEBUG) << "Error executing query " << r.finished_query << ": " << r.GetError(); + else + Log(LOG_DEBUG) << "Error executing query: " << r.GetError(); + } +}; + +class DBMySQL : public Module, public Pipe +{ + private: + MySQLInterface sqlinterface; + Anope::string engine; + service_reference SQL; + time_t lastwarn; + bool ro; + bool init; + std::set > updated_items; + + bool CheckSQL() + { + if (SQL) + { + if (readonly && this->ro) + { + readonly = this->ro = false; + const BotInfo *bi = findbot(Config->OperServ); + if (bi) + ircdproto->SendGlobops(bi, "Found SQL again, going out of readonly mode..."); + } + + return true; + } + else + { + if (Anope::CurTime - Config->UpdateTimeout > lastwarn) + { + const BotInfo *bi = findbot(Config->OperServ); + if (bi) + ircdproto->SendGlobops(bi, "Unable to locate SQL reference, going to readonly..."); + readonly = this->ro = true; + this->lastwarn = Anope::CurTime; + } + + return false; + } + } + + bool CheckInit() + { + return init && SQL; + } + + void RunQuery(const SQLQuery &query) + { + /* Can this be threaded? */ + this->RunQueryResult(query); + } + + SQLResult RunQueryResult(const SQLQuery &query) + { + if (this->CheckSQL()) + { + SQLResult res = SQL->RunQuery(query); + if (!res.GetError().empty()) + Log(LOG_DEBUG) << "SQlive got error " << res.GetError() << " for " + res.finished_query; + else + Log(LOG_DEBUG) << "SQLive got " << res.Rows() << " rows for " << res.finished_query; + return res; + } + throw SQLException("No SQL!"); + } + + SQLQuery BuildInsert(const Anope::string &table, unsigned int id, const Serialize::Data &data) + { + if (this->SQL) + { + std::vector create_queries = this->SQL->CreateTable(table, data); + for (unsigned i = 0; i < create_queries.size(); ++i) + this->RunQuery(create_queries[i]); + } + + Anope::string query_text = "INSERT INTO `" + table + "` (`id`"; + for (Serialize::Data::const_iterator it = data.begin(), it_end = data.end(); it != it_end; ++it) + query_text += ",`" + it->first + "`"; + query_text += ") VALUES (" + stringify(id); + for (Serialize::Data::const_iterator it = data.begin(), it_end = data.end(); it != it_end; ++it) + query_text += ",@" + it->first + "@"; + query_text += ") ON DUPLICATE KEY UPDATE "; + for (Serialize::Data::const_iterator it = data.begin(), it_end = data.end(); it != it_end; ++it) + query_text += "`" + it->first + "`=VALUES(`" + it->first + "`),"; + query_text.erase(query_text.end() - 1); + + SQLQuery query(query_text); + for (Serialize::Data::const_iterator it = data.begin(), it_end = data.end(); it != it_end; ++it) + query.setValue(it->first, it->second.astr()); + + return query; + } + + public: + DBMySQL(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE), sqlinterface(this), SQL("", "") + { + this->lastwarn = 0; + this->ro = false; + this->init = false; + + Implementation i[] = { I_OnReload, I_OnShutdown, I_OnLoadDatabase, I_OnSerializableConstruct, I_OnSerializableDestruct, I_OnSerializePtrAssign, I_OnSerializeCheck, I_OnSerializableUpdate }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + + OnReload(); + } + + void OnNotify() anope_override + { + if (!this->CheckInit()) + return; + + for (std::set >::iterator it = this->updated_items.begin(), it_end = this->updated_items.end(); it != it_end; ++it) + { + dynamic_reference obj = *it; + + if (obj) + { + if (obj->IsCached()) + continue; + obj->UpdateCache(); + + static std::set working_objects; // XXX + if (working_objects.count(obj)) + continue; + working_objects.insert(obj); + + SQLResult res = this->RunQueryResult(BuildInsert(obj->serialize_name(), obj->id, obj->serialize())); + if (res.GetID() > 0) + obj->id = res.GetID(); + SerializeType *type = SerializeType::Find(obj->serialize_name()); + if (type) + type->objects.erase(obj->id); + + working_objects.erase(obj); + } + } + + this->updated_items.clear(); + } + + EventReturn OnLoadDatabase() anope_override + { + init = true; + return EVENT_STOP; + } + + void OnShutdown() anope_override + { + init = false; + } + + void OnReload() anope_override + { + ConfigReader config; + this->engine = config.ReadValue("db_sql", "engine", "", 0); + this->SQL = service_reference("SQLProvider", this->engine); + } + + void OnSerializableConstruct(Serializable *obj) anope_override + { + if (!this->CheckInit()) + return; + this->updated_items.insert(obj); + this->Notify(); + } + + void OnSerializableDestruct(Serializable *obj) anope_override + { + if (!this->CheckInit()) + return; + this->RunQuery("DELETE FROM `" + obj->serialize_name() + "` WHERE `id` = " + stringify(obj->id)); + SerializeType *type = SerializeType::Find(obj->serialize_name()); + if (type) + type->objects.erase(obj->id); + } + + void OnSerializePtrAssign(Serializable *obj) anope_override + { + SerializeType *stype = SerializeType::Find(obj->serialize_name()); + if (stype == NULL || !this->CheckInit() || stype->GetTimestamp() == Anope::CurTime) + return; + + if (obj->IsCached()) + return; + obj->UpdateCache(); + + SQLResult res = this->RunQueryResult("SELECT * FROM `" + obj->serialize_name() + "` WHERE `id` = " + stringify(obj->id)); + + if (res.Rows() == 0) + obj->destroy(); + else + { + const std::map &row = res.Row(0); + + if (res.Get(0, "timestamp").empty()) + { + obj->destroy(); + stype->objects.erase(obj->id); + } + else + { + Serialize::Data data; + + for (std::map::const_iterator it = row.begin(), it_end = row.end(); it != it_end; ++it) + data[it->first] << it->second; + + if (stype->Unserialize(obj, data) == NULL) + obj->destroy(); + } + } + } + + void OnSerializeCheck(SerializeType *obj) + { + if (!this->CheckInit() || obj->GetTimestamp() == Anope::CurTime) + return; + + SQLQuery query("SELECT * FROM `" + obj->GetName() + "` WHERE (`timestamp` > FROM_UNIXTIME(@ts@) OR `timestamp` IS NULL)"); + query.setValue("ts", obj->GetTimestamp()); + + obj->UpdateTimestamp(); + + SQLResult res = this->RunQueryResult(query); + + bool clear_null = false; + for (int i = 0; i < res.Rows(); ++i) + { + const std::map &row = res.Row(i); + + unsigned int id; + try + { + id = convertTo(res.Get(i, "id")); + } + catch (const ConvertException &) + { + Log(LOG_DEBUG) << "Unable to convert id from " << obj->GetName(); + continue; + } + + if (res.Get(i, "timestamp").empty()) + { + clear_null = true; + std::map::iterator it = obj->objects.find(id); + if (it != obj->objects.end()) + { + it->second->destroy(); + obj->objects.erase(it); + } + } + else + { + Serialize::Data data; + + for (std::map::const_iterator it = row.begin(), it_end = row.end(); it != it_end; ++it) + data[it->first] << it->second; + + Serializable *s = NULL; + std::map::iterator it = obj->objects.find(id); + if (it != obj->objects.end()) + s = it->second; + + Serializable *new_s = obj->Unserialize(s, data); + if (new_s) + { + new_s->id = id; + obj->objects[id] = new_s; + } + else + s->destroy(); + } + } + + if (clear_null) + { + query = "DELETE FROM `" + obj->GetName() + "` WHERE `timestamp` IS NULL"; + this->RunQuery(query); + } + } + + void OnSerializableUpdate(Serializable *obj) + { + this->updated_items.insert(obj); + this->Notify(); + } +}; + +MODULE_INIT(DBMySQL) + diff --git a/modules/database/db_sql_live_read.cpp b/modules/database/db_sql_live_read.cpp deleted file mode 100644 index 38e5f6fef..000000000 --- a/modules/database/db_sql_live_read.cpp +++ /dev/null @@ -1,179 +0,0 @@ -#include "module.h" -#include "../extra/sql.h" - -class SQLCache : public Timer -{ - typedef std::map cache_map; - cache_map cache; - public: - - SQLCache() : Timer(300, Anope::CurTime, true) { } - - bool Check(const Anope::string &item) - { - cache_map::iterator it = this->cache.find(item); - if (it != this->cache.end() && Anope::CurTime - it->second < 5) - return true; - - this->cache[item] = Anope::CurTime; - return false; - } - - void Tick(time_t) anope_override - { - for (cache_map::iterator it = this->cache.begin(), next_it; it != this->cache.end(); it = next_it) - { - next_it = it; - ++next_it; - - if (Anope::CurTime - it->second > 5) - this->cache.erase(it); - } - } -}; - -class MySQLLiveModule : public Module -{ - service_reference SQL; - - SQLCache chan_cache, nick_cache, core_cache; - - SQLResult RunQuery(const SQLQuery &query) - { - if (!this->SQL) - throw SQLException("Unable to locate SQL reference, is db_sql loaded and configured correctly?"); - - SQLResult res = SQL->RunQuery(query); - if (!res.GetError().empty()) - throw SQLException(res.GetError()); - return res; - } - - public: - MySQLLiveModule(const Anope::string &modname, const Anope::string &creator) : - Module(modname, creator, DATABASE), SQL("", "") - { - this->OnReload(); - - Implementation i[] = { I_OnReload, I_OnFindChan, I_OnFindNick, I_OnFindCore, I_OnShutdown }; - ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); - } - - void OnReload() anope_override - { - ConfigReader config; - Anope::string engine = config.ReadValue("db_sql", "engine", "", 0); - this->SQL = service_reference("SQLProvider", engine); - } - - void OnShutdown() anope_override - { - Implementation i[] = { I_OnFindChan, I_OnFindNick, I_OnFindCore }; - for (size_t j = 0; j < 3; ++j) - ModuleManager::Detach(i[j], this); - } - - void OnFindChan(const Anope::string &chname) anope_override - { - if (chan_cache.Check(chname)) - return; - - try - { - SQLQuery query("SELECT * FROM `ChannelInfo` WHERE `name` = @name@"); - query.setValue("name", chname); - SQLResult res = this->RunQuery(query); - - if (res.Rows() == 0) - { - delete cs_findchan(chname); - return; - } - - SerializeType *sb = SerializeType::Find("ChannelInfo"); - if (sb == NULL) - return; - - Serializable::serialized_data data; - const std::map &row = res.Row(0); - for (std::map::const_iterator rit = row.begin(), rit_end = row.end(); rit != rit_end; ++rit) - data[rit->first] << rit->second; - - sb->Create(data); - } - catch (const SQLException &ex) - { - Log(LOG_DEBUG) << "OnFindChan: " << ex.GetReason(); - } - } - - void OnFindNick(const Anope::string &nick) anope_override - { - if (nick_cache.Check(nick)) - return; - - try - { - SQLQuery query("SELECT * FROM `NickAlias` WHERE `nick` = @nick@"); - query.setValue("nick", nick); - SQLResult res = this->RunQuery(query); - - if (res.Rows() == 0) - { - delete findnick(nick); - return; - } - - SerializeType *sb = SerializeType::Find("NickAlias"); - if (sb == NULL) - return; - - Serializable::serialized_data data; - const std::map &row = res.Row(0); - for (std::map::const_iterator rit = row.begin(), rit_end = row.end(); rit != rit_end; ++rit) - data[rit->first] << rit->second; - - sb->Create(data); - } - catch (const SQLException &ex) - { - Log(LOG_DEBUG) << "OnFindNick: " << ex.GetReason(); - } - } - - void OnFindCore(const Anope::string &nick) anope_override - { - if (core_cache.Check(nick)) - return; - - try - { - SQLQuery query("SELECT * FROM `NickCore` WHERE `display` = @display@"); - query.setValue("display", nick); - SQLResult res = this->RunQuery(query); - - if (res.Rows() == 0) - { - delete findcore(nick); - return; - } - - SerializeType *sb = SerializeType::Find("NickCore"); - if (sb == NULL) - return; - - Serializable::serialized_data data; - const std::map &row = res.Row(0); - for (std::map::const_iterator rit = row.begin(), rit_end = row.end(); rit != rit_end; ++rit) - data[rit->first] << rit->second; - - sb->Create(data); - } - catch (const SQLException &ex) - { - Log(LOG_DEBUG) << "OnFindCore: " << ex.GetReason(); - } - } -}; - -MODULE_INIT(MySQLLiveModule) diff --git a/modules/database/db_sql_live_write.cpp b/modules/database/db_sql_live_write.cpp deleted file mode 100644 index 0692f7f30..000000000 --- a/modules/database/db_sql_live_write.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include "module.h" -#include "../extra/sql.h" -#include "../commands/os_session.h" - -class MySQLInterface : public SQLInterface -{ - public: - MySQLInterface(Module *o) : SQLInterface(o) { } - - void OnResult(const SQLResult &r) anope_override - { - Log(LOG_DEBUG) << "SQL successfully executed query: " << r.finished_query; - } - - void OnError(const SQLResult &r) anope_override - { - if (!r.GetQuery().query.empty()) - Log(LOG_DEBUG) << "Error executing query " << r.finished_query << ": " << r.GetError(); - else - Log(LOG_DEBUG) << "Error executing query: " << r.GetError(); - } -}; - -class DBMySQL : public Module -{ - private: - MySQLInterface sqlinterface; - service_reference SQL; - std::set tables; - - void RunQuery(const SQLQuery &query) - { - if (SQL) - { - if (readonly && this->ro) - { - readonly = this->ro = false; - BotInfo *bi = findbot(Config->OperServ); - if (bi) - ircdproto->SendGlobops(bi, "Found SQL again, going out of readonly mode..."); - } - - SQL->Run(&sqlinterface, query); - } - else - { - if (Anope::CurTime - Config->UpdateTimeout > lastwarn) - { - BotInfo *bi = findbot(Config->OperServ); - if (bi) - ircdproto->SendGlobops(bi, "Unable to locate SQL reference, going to readonly..."); - readonly = this->ro = true; - this->lastwarn = Anope::CurTime; - } - } - } - - void Insert(const Anope::string &table, const Serializable::serialized_data &data) - { - if (tables.count(table) == 0 && SQL) - { - this->RunQuery(this->SQL->CreateTable(table, data)); - tables.insert(table); - } - - Anope::string query_text = "INSERT INTO `" + table + "` ("; - for (Serializable::serialized_data::const_iterator it = data.begin(), it_end = data.end(); it != it_end; ++it) - query_text += "`" + it->first + "`,"; - query_text.erase(query_text.end() - 1); - query_text += ") VALUES ("; - for (Serializable::serialized_data::const_iterator it = data.begin(), it_end = data.end(); it != it_end; ++it) - query_text += "@" + it->first + "@,"; - query_text.erase(query_text.end() - 1); - query_text += ") ON DUPLICATE KEY UPDATE "; - for (Serializable::serialized_data::const_iterator it = data.begin(), it_end = data.end(); it != it_end; ++it) - query_text += "`" + it->first + "`=VALUES(`" + it->first + "`),"; - query_text.erase(query_text.end() - 1); - - SQLQuery query(query_text); - for (Serializable::serialized_data::const_iterator it = data.begin(), it_end = data.end(); it != it_end; ++it) - query.setValue(it->first, it->second.astr()); - - this->RunQuery(query); - } - - void Delete(const Anope::string &table, const Serializable::serialized_data &data) - { - Anope::string query_text = "DELETE FROM `" + table + "` WHERE ", arg_text; - for (Serializable::serialized_data::const_iterator it = data.begin(), it_end = data.end(); it != it_end; ++it) - { - if (!arg_text.empty()) - arg_text += " AND "; - arg_text += "`" + it->first + "` = @" + it->first + "@"; - } - - query_text += arg_text; - - SQLQuery query(query_text); - for (Serializable::serialized_data::const_iterator it = data.begin(), it_end = data.end(); it != it_end; ++it) - query.setValue(it->first, it->second.astr()); - - this->RunQuery(query); - } - - public: - time_t lastwarn; - bool ro; - - DBMySQL(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE), sqlinterface(this), SQL("", "") - { - this->lastwarn = 0; - this->ro = false; - - Implementation i[] = { I_OnReload, I_OnServerConnect }; - ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); - - OnReload(); - - if (CurrentUplink) - OnServerConnect(); - } - - void OnReload() anope_override - { - ConfigReader config; - Anope::string engine = config.ReadValue("db_sql", "engine", "", 0); - this->SQL = service_reference("SQLProvider", engine); - } - - void OnServerConnect() anope_override - { - Implementation i[] = { - /* Misc */ - I_OnPostCommand, - /* NickServ */ - I_OnNickAddAccess, I_OnNickEraseAccess, I_OnNickClearAccess, - I_OnDelCore, I_OnNickForbidden, I_OnNickGroup, - I_OnNickRegister, I_OnChangeCoreDisplay, - I_OnNickSuspended, I_OnDelNick, - /* ChanServ */ - I_OnAccessAdd, I_OnAccessDel, I_OnAccessClear, I_OnLevelChange, - I_OnDelChan, I_OnChanRegistered, I_OnChanSuspend, - I_OnAkickAdd, I_OnAkickDel, I_OnMLock, I_OnUnMLock, - /* BotServ */ - I_OnBotCreate, I_OnBotChange, I_OnBotDelete, - I_OnBotAssign, I_OnBotUnAssign, - I_OnBadWordAdd, I_OnBadWordDel, - /* MemoServ */ - I_OnMemoSend, I_OnMemoDel, - /* OperServ */ - I_OnExceptionAdd, I_OnExceptionDel, - I_OnAddXLine, I_OnDelXLine, - /* HostServ */ - I_OnSetVhost, I_OnDeleteVhost - }; - ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); - } - - void OnPostCommand(CommandSource &source, Command *command, const std::vector ¶ms) anope_override - { - User *u = source.u; - - if (command->name.find("nickserv/set/") == 0) - { - NickAlias *na = findnick(source.u->nick); - if (na) - this->Insert(na->nc->serialize_name(), na->nc->serialize()); - - } - else if (command->name.find("nickserv/saset/") == 0) - { - NickAlias *na = findnick(params[0]); - if (na) - this->Insert(na->nc->serialize_name(), na->nc->serialize()); - } - else if (command->name.find("chanserv/set") == 0 || command->name.find("chanserv/saset") == 0) - { - ChannelInfo *ci = params.size() > 0 ? cs_findchan(params[0]) : NULL; - if (ci) - this->Insert(ci->serialize_name(), ci->serialize()); - } - else if (command->name == "botserv/kick" && params.size() > 2) - { - ChannelInfo *ci = cs_findchan(params[0]); - if (!ci) - return; - if (!ci->AccessFor(u).HasPriv("SET") && !u->HasPriv("botserv/administration")) - return; - this->Insert(ci->serialize_name(), ci->serialize()); - } - else if (command->name == "botserv/set" && params.size() > 1) - { - ChannelInfo *ci = cs_findchan(params[0]); - BotInfo *bi = NULL; - if (!ci) - bi = findbot(params[0]); - if (bi && params[1].equals_ci("PRIVATE") && u->HasPriv("botserv/set/private")) - this->Insert(bi->serialize_name(), bi->serialize()); - else if (ci && !ci->AccessFor(u).HasPriv("SET") && !u->HasPriv("botserv/administration")) - this->Insert(ci->serialize_name(), ci->serialize()); - } - else if (command->name == "memoserv/ignore" && params.size() > 0) - { - Anope::string target = params[0]; - if (target[0] != '#') - { - NickCore *nc = u->Account(); - if (nc) - this->Insert(nc->serialize_name(), nc->serialize()); - } - else - { - ChannelInfo *ci = cs_findchan(target); - if (ci && ci->AccessFor(u).HasPriv("MEMO")) - this->Insert(ci->serialize_name(), ci->serialize()); - } - } - } - - void OnNickAddAccess(NickCore *nc, const Anope::string &entry) anope_override - { - this->Insert(nc->serialize_name(), nc->serialize()); - } - - void OnNickEraseAccess(NickCore *nc, const Anope::string &entry) anope_override - { - this->Insert(nc->serialize_name(), nc->serialize()); - } - - void OnNickClearAccess(NickCore *nc) anope_override - { - this->Insert(nc->serialize_name(), nc->serialize()); - } - - void OnDelCore(NickCore *nc) anope_override - { - this->Delete(nc->serialize_name(), nc->serialize()); - } - - void OnNickForbidden(NickAlias *na) anope_override - { - this->Insert(na->serialize_name(), na->serialize()); - } - - void OnNickGroup(User *u, NickAlias *) anope_override - { - OnNickRegister(findnick(u->nick)); - } - - void InsertAlias(NickAlias *na) - { - this->Insert(na->serialize_name(), na->serialize()); - } - - void InsertCore(NickCore *nc) - { - this->Insert(nc->serialize_name(), nc->serialize()); - } - - void OnNickRegister(NickAlias *na) anope_override - { - this->InsertCore(na->nc); - this->InsertAlias(na); - } - - void OnChangeCoreDisplay(NickCore *nc, const Anope::string &newdisplay) anope_override - { - Serializable::serialized_data data = nc->serialize(); - this->Delete(nc->serialize_name(), data); - data.erase("display"); - data["display"] << newdisplay; - this->Insert(nc->serialize_name(), data); - - for (std::list::iterator it = nc->aliases.begin(), it_end = nc->aliases.end(); it != it_end; ++it) - { - NickAlias *na = *it; - data = na->serialize(); - - this->Delete(na->serialize_name(), data); - data.erase("nc"); - data["nc"] << newdisplay; - this->Insert(na->serialize_name(), data); - } - } - - void OnNickSuspend(NickAlias *na) anope_override - { - this->Insert(na->serialize_name(), na->serialize()); - } - - void OnDelNick(NickAlias *na) anope_override - { - this->Delete(na->serialize_name(), na->serialize()); - } - - void OnAccessAdd(ChannelInfo *ci, User *, ChanAccess *access) anope_override - { - this->Insert(access->serialize_name(), access->serialize()); - } - - void OnAccessDel(ChannelInfo *ci, User *u, ChanAccess *access) anope_override - { - this->Delete(access->serialize_name(), access->serialize()); - } - - void OnAccessClear(ChannelInfo *ci, User *u) anope_override - { - for (unsigned i = 0; i < ci->GetAccessCount(); ++i) - this->OnAccessDel(ci, NULL, ci->GetAccess(i)); - } - - void OnLevelChange(User *u, ChannelInfo *ci, const Anope::string &priv, int16_t what) anope_override - { - this->Insert(ci->serialize_name(), ci->serialize()); - } - - void OnDelChan(ChannelInfo *ci) anope_override - { - this->Delete(ci->serialize_name(), ci->serialize()); - } - - void OnChanRegistered(ChannelInfo *ci) anope_override - { - this->Insert(ci->serialize_name(), ci->serialize()); - } - - void OnChanSuspend(ChannelInfo *ci) anope_override - { - this->Insert(ci->serialize_name(), ci->serialize()); - } - - void OnAkickAdd(User *u, ChannelInfo *ci, AutoKick *ak) anope_override - { - this->Insert(ak->serialize_name(), ak->serialize()); - } - - void OnAkickDel(User *u, ChannelInfo *ci, AutoKick *ak) anope_override - { - this->Delete(ak->serialize_name(), ak->serialize()); - } - - EventReturn OnMLock(ChannelInfo *ci, ModeLock *lock) anope_override - { - this->Insert(lock->serialize_name(), lock->serialize()); - return EVENT_CONTINUE; - } - - EventReturn OnUnMLock(ChannelInfo *ci, ModeLock *lock) anope_override - { - this->Delete(lock->serialize_name(), lock->serialize()); - return EVENT_CONTINUE; - } - - void OnBotCreate(BotInfo *bi) anope_override - { - this->Insert(bi->serialize_name(), bi->serialize()); - } - - void OnBotChange(BotInfo *bi) anope_override - { - OnBotCreate(bi); - } - - void OnBotDelete(BotInfo *bi) anope_override - { - this->Delete(bi->serialize_name(), bi->serialize()); - } - - EventReturn OnBotAssign(User *sender, ChannelInfo *ci, BotInfo *bi) anope_override - { - this->Insert(ci->serialize_name(), ci->serialize()); - return EVENT_CONTINUE; - } - - EventReturn OnBotUnAssign(User *sender, ChannelInfo *ci) anope_override - { - this->Insert(ci->serialize_name(), ci->serialize()); - return EVENT_CONTINUE; - } - - void OnBadWordAdd(ChannelInfo *ci, BadWord *bw) anope_override - { - this->Insert(bw->serialize_name(), bw->serialize()); - } - - void OnBadWordDel(ChannelInfo *ci, BadWord *bw) anope_override - { - this->Delete(bw->serialize_name(), bw->serialize()); - } - - void OnMemoSend(const Anope::string &source, const Anope::string &target, MemoInfo *mi, Memo *m) anope_override - { - this->Insert(m->serialize_name(), m->serialize()); - } - - void OnMemoDel(const NickCore *nc, MemoInfo *mi, Memo *m) anope_override - { - this->Delete(m->serialize_name(), m->serialize()); - } - - void OnMemoDel(ChannelInfo *ci, MemoInfo *mi, Memo *m) anope_override - { - this->Delete(m->serialize_name(), m->serialize()); - } - - EventReturn OnExceptionAdd(Exception *ex) anope_override - { - this->Insert(ex->serialize_name(), ex->serialize()); - return EVENT_CONTINUE; - } - - void OnExceptionDel(User *, Exception *ex) anope_override - { - this->Delete(ex->serialize_name(), ex->serialize()); - } - - EventReturn OnAddXLine(User *u, XLine *x, XLineManager *xlm) anope_override - { - this->Insert(x->serialize_name(), x->serialize()); - return EVENT_CONTINUE; - } - - void OnDelXLine(User *, XLine *x, XLineManager *xlm) anope_override - { - this->Delete(x->serialize_name(), x->serialize()); - } - - void OnDeleteVhost(NickAlias *na) anope_override - { - this->Insert(na->serialize_name(), na->serialize()); - } - - void OnSetVhost(NickAlias *na) anope_override - { - this->Insert(na->serialize_name(), na->serialize()); - } -}; - -MODULE_INIT(DBMySQL) - -- cgit