diff options
-rw-r--r-- | data/chanstats.example.conf | 22 | ||||
-rw-r--r-- | data/example.conf | 13 | ||||
-rw-r--r-- | modules/database/db_sql.cpp | 144 | ||||
-rw-r--r-- | modules/database/db_sql_live.cpp | 50 | ||||
-rw-r--r-- | modules/extra/m_chanstats.cpp | 2 | ||||
-rw-r--r-- | modules/extra/m_mysql.cpp | 6 | ||||
-rw-r--r-- | modules/extra/m_sqlite.cpp | 6 | ||||
-rw-r--r-- | modules/extra/sql.h | 2 | ||||
-rw-r--r-- | src/nickalias.cpp | 2 |
9 files changed, 129 insertions, 118 deletions
diff --git a/data/chanstats.example.conf b/data/chanstats.example.conf index 2ada76b98..ab07b2713 100644 --- a/data/chanstats.example.conf +++ b/data/chanstats.example.conf @@ -8,17 +8,11 @@ module { name = "m_chanstats" } chanstats { /* - * WARNING: DO NOT USE THE SAME DATABASE AS db_mysql! db_mysql drops ALL tables in the db! - * For this, we add a second mysql{ } block after the chanstats{ } configuration block - * and give it another engine name, so m_mysql opens a second connection to the mysql server. - * / - - /* * The name of this engine. - * This must match with the name in the mysql{ } block + * This must match with the name of a SQL engine block */ - engine = "mysql/chanstats" + engine = "mysql/main" smileyshappy = ":) :-) ;) :D :-D" @@ -33,18 +27,6 @@ chanstats CSDefChanstats = 1 } -mysql -{ - name = "mysql/chanstats" - database = "anope" - server = "127.0.0.1" - username = "anope" - password = "anope" - port = 3306 -} - - - module { name = "cs_set_chanstats" } command { service = "ChanServ"; name = "SET CHANSTATS"; command = "chanserv/set/chanstats"; } diff --git a/data/example.conf b/data/example.conf index e27067e9a..3b8fc6628 100644 --- a/data/example.conf +++ b/data/example.conf @@ -1091,11 +1091,10 @@ db_flatfile * db_sql * * This module allows saving and loading databases using one of the SQL engines. - * This module does not use any real time reading or writing to SQL. Changes to - * the SQL tables not done by Anope will have no effect and will be overwritten. + * This module loads the databases once on startup, then incrementally updates + * objects in the database as they are changed within Anope in real time. Changes + * to the SQL tables not done by Anope will have no effect and will be overwritten. * - * WARNING: This module will delete every table in the database it uses. Be sure - * to put Anope in its own separate database. */ #module { name = "db_sql" } @@ -1116,6 +1115,12 @@ db_sql * For MySQL, this should probably be mysql/main. */ engine = "sqlite/main" + + /* + * An optional prefix to prepended to the name of each created table. + * Do not use the same prefix for other programs. + */ + #prefix = "anope_db_" } /* diff --git a/modules/database/db_sql.cpp b/modules/database/db_sql.cpp index c2ba84a5f..b7d32c17c 100644 --- a/modules/database/db_sql.cpp +++ b/modules/database/db_sql.cpp @@ -30,38 +30,66 @@ class SQLSQLInterface : public SQLInterface } }; -class DBSQL : public Module +class ResultSQLSQLInterface : public SQLSQLInterface { - service_reference<SQLProvider> sql; - SQLSQLInterface sqlinterface; + dynamic_reference<Serializable> obj; - void RunBackground(const SQLQuery &q) +public: + ResultSQLSQLInterface(Module *o, Serializable *ob) : SQLSQLInterface(o), obj(ob) { } + + void OnResult(const SQLResult &r) anope_override { - if (!quitting) - this->sql->Run(&sqlinterface, q); - else - this->sql->RunQuery(q); + SQLSQLInterface::OnResult(r); + if (r.GetID() > 0 && this->obj) + this->obj->id = r.GetID(); + delete this; } - void DropTable(const Anope::string &table) + void OnError(const SQLResult &r) anope_override { - SQLQuery query("DROP TABLE `" + table + "`"); - this->RunBackground(query); + SQLSQLInterface::OnError(r); + delete this; } +}; - void DropAll() +class DBSQL : public Module, public Pipe +{ + service_reference<SQLProvider> sql; + SQLSQLInterface sqlinterface; + Anope::string prefix; + std::set<dynamic_reference<Serializable> > updated_items; + bool quitting; + + void RunBackground(const SQLQuery &q, SQLInterface *iface = NULL) { - SQLResult r = this->sql->RunQuery(this->sql->GetTables()); - for (int i = 0; i < r.Rows(); ++i) + if (!this->sql) { - const std::map<Anope::string, Anope::string> &map = r.Row(i); - for (std::map<Anope::string, Anope::string>::const_iterator it = map.begin(); it != map.end(); ++it) - this->DropTable(it->second); + static time_t last_warn = 0; + if (last_warn + 300 < Anope::CurTime) + { + last_warn = Anope::CurTime; + Log() << "db_sql: Unable to execute query, is SQL configured correctly?"; + } } + else if (!this->quitting) + { + if (iface == NULL) + iface = &this->sqlinterface; + this->sql->Run(iface, q); + } + else + this->sql->RunQuery(q); } - void Insert(const Anope::string &table, const Serialize::Data &data) + SQLQuery BuildInsert(const Anope::string &table, const Serialize::Data &data) { + if (this->sql) + { + std::vector<SQLQuery> create_queries = this->sql->CreateTable(table, data); + for (unsigned i = 0; i < create_queries.size(); ++i) + this->RunBackground(create_queries[i]); + } + Anope::string query_text = "INSERT INTO `" + table + "` ("; for (Serialize::Data::const_iterator it = data.begin(), it_end = data.end(); it != it_end; ++it) query_text += "`" + it->first + "`,"; @@ -76,57 +104,52 @@ class DBSQL : public Module 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); + return query; } public: - DBSQL(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE), sql("", ""), sqlinterface(this) + DBSQL(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE), sql("", ""), sqlinterface(this), quitting(false) { this->SetAuthor("Anope"); - Implementation i[] = { I_OnReload, I_OnSaveDatabase, I_OnLoadDatabase }; + Implementation i[] = { I_OnReload, I_OnLoadDatabase, I_OnSerializableConstruct, I_OnSerializableDestruct, I_OnSerializableUpdate }; ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); this->OnReload(); } - - void OnReload() anope_override + + ~DBSQL() { - ConfigReader config; - Anope::string engine = config.ReadValue("db_sql", "engine", "", 0); - this->sql = service_reference<SQLProvider>("SQLProvider", engine); + this->quitting = true; + this->OnNotify(); } - EventReturn OnSaveDatabase() anope_override + void OnNotify() anope_override { - if (!this->sql) + for (std::set<dynamic_reference<Serializable> >::iterator it = this->updated_items.begin(), it_end = this->updated_items.end(); it != it_end; ++it) { - Log() << "db_sql: Unable to save databases, is SQL configured correctly?"; - return EVENT_CONTINUE; - } - - const std::list<Serializable *> &items = Serializable::GetItems(); - if (items.empty()) - return EVENT_CONTINUE; - - this->DropAll(); - - for (std::list<Serializable *>::const_iterator it = items.begin(), it_end = items.end(); it != it_end; ++it) - { - Serializable *base = *it; - Serialize::Data data = base->serialize(); - - if (data.empty()) - continue; + dynamic_reference<Serializable> obj = *it; - std::vector<SQLQuery> create_queries = this->sql->CreateTable(base->serialize_name(), data); - for (unsigned i = 0; i < create_queries.size(); ++i) - this->RunBackground(create_queries[i]); + if (obj) + { + if (obj->IsCached()) + continue; + obj->UpdateCache(); - this->Insert(base->serialize_name(), data); + SQLQuery insert = this->BuildInsert(this->prefix + obj->serialize_name(), obj->serialize()); + this->RunBackground(insert, new ResultSQLSQLInterface(this, obj)); + } } - return EVENT_CONTINUE; + this->updated_items.clear(); + } + + void OnReload() anope_override + { + ConfigReader config; + Anope::string engine = config.ReadValue("db_sql", "engine", "", 0); + this->sql = service_reference<SQLProvider>("SQLProvider", engine); + this->prefix = config.ReadValue("db_sql", "prefix", "anope_db_", 0); } EventReturn OnLoadDatabase() anope_override @@ -142,11 +165,13 @@ class DBSQL : public Module { SerializeType *sb = SerializeType::Find(type_order[i]); - SQLQuery query("SELECT * FROM `" + sb->GetName() + "`"); + SQLQuery query("SELECT * FROM `" + this->prefix + sb->GetName() + "`"); SQLResult res = this->sql->RunQuery(query); + for (int j = 0; j < res.Rows(); ++j) { Serialize::Data data; + const std::map<Anope::string, Anope::string> &row = res.Row(j); for (std::map<Anope::string, Anope::string>::const_iterator rit = row.begin(), rit_end = row.end(); rit != rit_end; ++rit) data[rit->first] << rit->second; @@ -157,6 +182,23 @@ class DBSQL : public Module return EVENT_STOP; } + + void OnSerializableConstruct(Serializable *obj) anope_override + { + this->updated_items.insert(obj); + this->Notify(); + } + + void OnSerializableDestruct(Serializable *obj) anope_override + { + this->RunBackground("DELETE FROM `" + this->prefix + obj->serialize_name() + "` WHERE `id` = " + stringify(obj->id)); + } + + void OnSerializableUpdate(Serializable *obj) anope_override + { + this->updated_items.insert(obj); + this->Notify(); + } }; MODULE_INIT(DBSQL) diff --git a/modules/database/db_sql_live.cpp b/modules/database/db_sql_live.cpp index 4b939e23b..32cc2cda6 100644 --- a/modules/database/db_sql_live.cpp +++ b/modules/database/db_sql_live.cpp @@ -2,30 +2,11 @@ #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; + Anope::string prefix; service_reference<SQLProvider> SQL; time_t lastwarn; bool ro; @@ -114,7 +95,7 @@ class DBMySQL : public Module, public Pipe } public: - DBMySQL(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE), sqlinterface(this), SQL("", "") + DBMySQL(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE), SQL("", "") { this->lastwarn = 0; this->ro = false; @@ -146,12 +127,12 @@ class DBMySQL : public Module, public Pipe continue; working_objects.insert(obj); - SQLResult res = this->RunQueryResult(BuildInsert(obj->serialize_name(), obj->id, obj->serialize())); + SQLResult res = this->RunQueryResult(BuildInsert(this->prefix + 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); + SerializeType *stype = SerializeType::Find(obj->serialize_name()); + if (stype) + stype->objects.erase(obj->id); working_objects.erase(obj); } @@ -176,6 +157,7 @@ class DBMySQL : public Module, public Pipe ConfigReader config; this->engine = config.ReadValue("db_sql", "engine", "", 0); this->SQL = service_reference<SQLProvider>("SQLProvider", this->engine); + this->prefix = config.ReadValue("db_sql", "prefix", "anope_db_", 0); } void OnSerializableConstruct(Serializable *obj) anope_override @@ -190,10 +172,10 @@ class DBMySQL : public Module, public Pipe { 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); + this->RunQuery("DELETE FROM `" + this->prefix + obj->serialize_name() + "` WHERE `id` = " + stringify(obj->id)); + SerializeType *stype = SerializeType::Find(obj->serialize_name()); + if (stype) + stype->objects.erase(obj->id); } void OnSerializePtrAssign(Serializable *obj) anope_override @@ -206,7 +188,7 @@ class DBMySQL : public Module, public Pipe return; obj->UpdateCache(); - SQLResult res = this->RunQueryResult("SELECT * FROM `" + obj->serialize_name() + "` WHERE `id` = " + stringify(obj->id)); + SQLResult res = this->RunQueryResult("SELECT * FROM `" + this->prefix + obj->serialize_name() + "` WHERE `id` = " + stringify(obj->id)); if (res.Rows() == 0) obj->destroy(); @@ -232,12 +214,12 @@ class DBMySQL : public Module, public Pipe } } - void OnSerializeCheck(SerializeType *obj) + void OnSerializeCheck(SerializeType *obj) anope_override { if (!this->CheckInit() || obj->GetTimestamp() == Anope::CurTime) return; - SQLQuery query("SELECT * FROM `" + obj->GetName() + "` WHERE (`timestamp` > FROM_UNIXTIME(@ts@) OR `timestamp` IS NULL)"); + SQLQuery query("SELECT * FROM `" + this->prefix + obj->GetName() + "` WHERE (`timestamp` > FROM_UNIXTIME(@ts@) OR `timestamp` IS NULL)"); query.setValue("ts", obj->GetTimestamp()); obj->UpdateTimestamp(); @@ -295,12 +277,12 @@ class DBMySQL : public Module, public Pipe if (clear_null) { - query = "DELETE FROM `" + obj->GetName() + "` WHERE `timestamp` IS NULL"; + query = "DELETE FROM `" + this->prefix + obj->GetName() + "` WHERE `timestamp` IS NULL"; this->RunQuery(query); } } - void OnSerializableUpdate(Serializable *obj) + void OnSerializableUpdate(Serializable *obj) anope_override { this->updated_items.insert(obj); this->Notify(); diff --git a/modules/extra/m_chanstats.cpp b/modules/extra/m_chanstats.cpp index 8aa7e0a6a..9da3f0b10 100644 --- a/modules/extra/m_chanstats.cpp +++ b/modules/extra/m_chanstats.cpp @@ -66,7 +66,7 @@ class MChanstats : public Module if (!sql) return; - SQLResult r = this->sql->RunQuery(this->sql->GetTables()); + SQLResult r = this->sql->RunQuery(this->sql->GetTables("anope_bs_")); for (int i = 0; i < r.Rows(); ++i) { const std::map<Anope::string, Anope::string> &map = r.Row(i); diff --git a/modules/extra/m_mysql.cpp b/modules/extra/m_mysql.cpp index 0696955cd..b5f0e4406 100644 --- a/modules/extra/m_mysql.cpp +++ b/modules/extra/m_mysql.cpp @@ -127,7 +127,7 @@ class MySQLService : public SQLProvider std::vector<SQLQuery> CreateTable(const Anope::string &table, const Serialize::Data &data) anope_override; - SQLQuery GetTables() anope_override; + SQLQuery GetTables(const Anope::string &prefix) anope_override; void Connect(); @@ -391,9 +391,9 @@ std::vector<SQLQuery> MySQLService::CreateTable(const Anope::string &table, cons return queries; } -SQLQuery MySQLService::GetTables() +SQLQuery MySQLService::GetTables(const Anope::string &prefix) { - return SQLQuery("SHOW TABLES"); + return SQLQuery("SHOW TABLES LIKE '" + prefix + "%';"); } void MySQLService::Connect() diff --git a/modules/extra/m_sqlite.cpp b/modules/extra/m_sqlite.cpp index 1c82f3459..5102c003b 100644 --- a/modules/extra/m_sqlite.cpp +++ b/modules/extra/m_sqlite.cpp @@ -48,7 +48,7 @@ class SQLiteService : public SQLProvider std::vector<SQLQuery> CreateTable(const Anope::string &table, const Serialize::Data &data) anope_override; - SQLQuery GetTables(); + SQLQuery GetTables(const Anope::string &prefix); Anope::string BuildQuery(const SQLQuery &q); }; @@ -236,9 +236,9 @@ std::vector<SQLQuery> SQLiteService::CreateTable(const Anope::string &table, con return queries; } -SQLQuery SQLiteService::GetTables() +SQLQuery SQLiteService::GetTables(const Anope::string &prefix) { - return SQLQuery("SELECT name FROM sqlite_master WHERE type='table'"); + return SQLQuery("SELECT name FROM sqlite_master WHERE type='table' AND name LIKE '" + prefix + "%';"); } Anope::string SQLiteService::Escape(const Anope::string &query) diff --git a/modules/extra/sql.h b/modules/extra/sql.h index a986cf794..2e2e1840c 100644 --- a/modules/extra/sql.h +++ b/modules/extra/sql.h @@ -129,6 +129,6 @@ class SQLProvider : public Service virtual std::vector<SQLQuery> CreateTable(const Anope::string &table, const Serialize::Data &data) = 0; - virtual SQLQuery GetTables() = 0; + virtual SQLQuery GetTables(const Anope::string &prefix) = 0; }; diff --git a/src/nickalias.cpp b/src/nickalias.cpp index bdd010e23..e9c8cce08 100644 --- a/src/nickalias.cpp +++ b/src/nickalias.cpp @@ -69,7 +69,7 @@ NickAlias::~NickAlias() this->nc->aliases.erase(it); if (this->nc->aliases.empty()) { - delete this->nc; + this->nc->destroy(); this->nc = NULL; } else |