db_sql.cpp

Go to the documentation of this file.
00001 /*
00002  * (C) 2003-2013 Anope Team
00003  * Contact us at team@anope.org
00004  *
00005  * Please read COPYING and README for further details.
00006  *
00007  * Based on the original code of Epona by Lara.
00008  * Based on the original code of Services by Andy Church.
00009  */
00010 
00011 #include "module.h"
00012 #include "../extra/sql.h"
00013 
00014 using namespace SQL;
00015 
00016 class SQLSQLInterface : public Interface
00017 {
00018  public:
00019         SQLSQLInterface(Module *o) : Interface(o) { }
00020 
00021         void OnResult(const Result &r) anope_override
00022         {
00023                 Log(LOG_DEBUG) << "SQL successfully executed query: " << r.finished_query;
00024         }
00025 
00026         void OnError(const Result &r) anope_override
00027         {
00028                 if (!r.GetQuery().query.empty())
00029                         Log(LOG_DEBUG) << "Error executing query " << r.finished_query << ": " << r.GetError();
00030                 else
00031                         Log(LOG_DEBUG) << "Error executing query: " << r.GetError();
00032         }
00033 };
00034 
00035 class ResultSQLSQLInterface : public SQLSQLInterface
00036 {
00037         Reference<Serializable> obj;
00038 
00039 public:
00040         ResultSQLSQLInterface(Module *o, Serializable *ob) : SQLSQLInterface(o), obj(ob) { }
00041 
00042         void OnResult(const Result &r) anope_override
00043         {
00044                 SQLSQLInterface::OnResult(r);
00045                 if (r.GetID() > 0 && this->obj)
00046                         this->obj->id = r.GetID();
00047                 delete this;
00048         }
00049 
00050         void OnError(const Result &r) anope_override
00051         {
00052                 SQLSQLInterface::OnError(r);
00053                 delete this;
00054         }
00055 };
00056 
00057 class DBSQL : public Module, public Pipe
00058 {
00059         ServiceReference<Provider> sql;
00060         SQLSQLInterface sqlinterface;
00061         Anope::string prefix;
00062         std::set<Serializable *> updated_items;
00063         bool shutting_down;
00064         bool loading_databases;
00065         bool loaded;
00066         bool imported;
00067 
00068         void RunBackground(const Query &q, Interface *iface = NULL)
00069         {
00070                 if (!this->sql)
00071                 {
00072                         static time_t last_warn = 0;
00073                         if (last_warn + 300 < Anope::CurTime)
00074                         {
00075                                 last_warn = Anope::CurTime;
00076                                 Log(this) << "db_sql: Unable to execute query, is SQL configured correctly?";
00077                         }
00078                 }
00079                 else if (!Anope::Quitting)
00080                 {
00081                         if (iface == NULL)
00082                                 iface = &this->sqlinterface;
00083                         this->sql->Run(iface, q);
00084                 }
00085                 else
00086                         this->sql->RunQuery(q);
00087         }
00088 
00089  public:
00090         DBSQL(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE), sql("", ""), sqlinterface(this), shutting_down(false), loading_databases(false), loaded(false), imported(false)
00091         {
00092                 this->SetAuthor("Anope");
00093 
00094                 Implementation i[] = { I_OnReload, I_OnShutdown, I_OnRestart, I_OnLoadDatabase, I_OnSerializableConstruct, I_OnSerializableDestruct, I_OnSerializableUpdate, I_OnSerializeTypeCreate };
00095                 ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation));
00096 
00097                 this->OnReload();
00098         }
00099 
00100         void OnNotify() anope_override
00101         {
00102                 for (std::set<Serializable *>::iterator it = this->updated_items.begin(), it_end = this->updated_items.end(); it != it_end; ++it)
00103                 {
00104                         Serializable *obj = *it;
00105 
00106                         if (this->sql)
00107                         {
00108                                 Data data;
00109                                 obj->Serialize(data);
00110 
00111                                 if (obj->IsCached(data))
00112                                         continue;
00113 
00114                                 obj->UpdateCache(data);
00115 
00116                                 Serialize::Type *s_type = obj->GetSerializableType();
00117                                 if (!s_type)
00118                                         continue;
00119 
00120                                 std::vector<Query> create = this->sql->CreateTable(this->prefix + s_type->GetName(), data);
00121                                 for (unsigned i = 0; i < create.size(); ++i)
00122                                         this->RunBackground(create[i]);
00123 
00124                                 Query insert = this->sql->BuildInsert(this->prefix + s_type->GetName(), obj->id, data);
00125                                 if (this->imported)
00126                                         this->RunBackground(insert, new ResultSQLSQLInterface(this, obj));
00127                                 else
00128                                 {
00129                                         /* On the first loop we may be importing objects from another database module, so don't do asynchronous
00130                                          * queries in case the core has to shut down, it will cut short the import
00131                                          */
00132                                         Result r = this->sql->RunQuery(insert);
00133                                         if (r.GetID() > 0)
00134                                                 obj->id = r.GetID();
00135                                 }
00136                         }
00137                 }
00138 
00139                 this->updated_items.clear();
00140                 this->imported = true;
00141         }
00142 
00143         void OnReload() anope_override
00144         {
00145                 ConfigReader config;
00146                 Anope::string engine = config.ReadValue("db_sql", "engine", "", 0);
00147                 this->sql = ServiceReference<Provider>("SQL::Provider", engine);
00148                 this->prefix = config.ReadValue("db_sql", "prefix", "anope_db_", 0);
00149         }
00150 
00151         void OnShutdown() anope_override
00152         {
00153                 this->shutting_down = true;
00154                 this->OnNotify();
00155         }
00156 
00157         void OnRestart() anope_override
00158         {
00159                 this->OnShutdown();
00160         }
00161 
00162         EventReturn OnLoadDatabase() anope_override
00163         {
00164                 if (!this->sql)
00165                 {
00166                         Log(this) << "Unable to load databases, is SQL configured correctly?";
00167                         return EVENT_CONTINUE;
00168                 }
00169 
00170                 this->loading_databases = true;
00171 
00172                 const std::vector<Anope::string> type_order = Serialize::Type::GetTypeOrder();
00173                 for (unsigned i = 0; i < type_order.size(); ++i)
00174                 {
00175                         Serialize::Type *sb = Serialize::Type::Find(type_order[i]);
00176                         this->OnSerializeTypeCreate(sb);
00177                 }
00178 
00179                 this->loading_databases = false;
00180                 this->loaded = true;
00181 
00182                 return EVENT_STOP;
00183         }
00184 
00185         void OnSerializableConstruct(Serializable *obj) anope_override
00186         {
00187                 if (this->shutting_down || this->loading_databases)
00188                         return;
00189                 obj->UpdateTS();
00190                 this->updated_items.insert(obj);
00191                 this->Notify();
00192         }
00193 
00194         void OnSerializableDestruct(Serializable *obj) anope_override
00195         {
00196                 Serialize::Type *s_type = obj->GetSerializableType();
00197                 if (s_type && obj->id > 0)
00198                         this->RunBackground("DELETE FROM `" + this->prefix + s_type->GetName() + "` WHERE `id` = " + stringify(obj->id));
00199                 this->updated_items.erase(obj);
00200         }
00201 
00202         void OnSerializableUpdate(Serializable *obj) anope_override
00203         {
00204                 if (this->shutting_down || obj->IsTSCached())
00205                         return;
00206                 obj->UpdateTS();
00207                 this->updated_items.insert(obj);
00208                 this->Notify();
00209         }
00210 
00211         void OnSerializeTypeCreate(Serialize::Type *sb) anope_override
00212         {
00213                 if (!this->loading_databases && !this->loaded)
00214                         return;
00215 
00216                 Query query("SELECT * FROM `" + this->prefix + sb->GetName() + "`");
00217                 Result res = this->sql->RunQuery(query);
00218 
00219                 for (int j = 0; j < res.Rows(); ++j)
00220                 {
00221                         Data data;
00222 
00223                         const std::map<Anope::string, Anope::string> &row = res.Row(j);
00224                         for (std::map<Anope::string, Anope::string>::const_iterator rit = row.begin(), rit_end = row.end(); rit != rit_end; ++rit)
00225                                 data[rit->first] << rit->second;
00226 
00227                         Serializable *obj = sb->Unserialize(NULL, data);
00228                         try
00229                         {
00230                                 if (obj)
00231                                         obj->id = convertTo<unsigned int>(res.Get(j, "id"));
00232                         }
00233                         catch (const ConvertException &)
00234                         {
00235                                 Log(this) << "Unable to convert id for object #" << j << " of type " << sb->GetName();
00236                         }
00237 
00238                         if (obj)
00239                         {
00240                                 Data data2;
00241                                 /* The Unserialize operation is destructive so rebuild the data for UpdateCache */
00242                                 for (std::map<Anope::string, Anope::string>::const_iterator rit = row.begin(), rit_end = row.end(); rit != rit_end; ++rit)
00243                                         if (rit->first != "id" && rit->first != "timestamp")
00244                                                 data2[rit->first] << rit->second;
00245                                 obj->UpdateCache(data2); /* We know this is the most up to date copy */
00246                         }
00247                 }
00248         }
00249 };
00250 
00251 MODULE_INIT(DBSQL)
00252