anope- supernets anope source code & configuration |
git clone git://git.acid.vegas/anope.git |
Log | Files | Refs | Archive | README |
db_sql_live.cpp (6406B)
1 /* 2 * 3 * (C) 2012-2022 Anope Team 4 * Contact us at team@anope.org 5 * 6 * Please read COPYING and README for further details. 7 */ 8 9 #include "module.h" 10 #include "modules/sql.h" 11 12 using namespace SQL; 13 14 class DBMySQL : public Module, public Pipe 15 { 16 private: 17 Anope::string prefix; 18 ServiceReference<Provider> SQL; 19 time_t lastwarn; 20 bool ro; 21 bool init; 22 std::set<Serializable *> updated_items; 23 24 bool CheckSQL() 25 { 26 if (SQL) 27 { 28 if (Anope::ReadOnly && this->ro) 29 { 30 Anope::ReadOnly = this->ro = false; 31 Log() << "Found SQL again, going out of readonly mode..."; 32 } 33 34 return true; 35 } 36 else 37 { 38 if (Anope::CurTime - Config->GetBlock("options")->Get<time_t>("updatetimeout", "5m") > lastwarn) 39 { 40 Log() << "Unable to locate SQL reference, going to readonly..."; 41 Anope::ReadOnly = this->ro = true; 42 this->lastwarn = Anope::CurTime; 43 } 44 45 return false; 46 } 47 } 48 49 bool CheckInit() 50 { 51 return init && SQL; 52 } 53 54 void RunQuery(const Query &query) 55 { 56 /* Can this be threaded? */ 57 this->RunQueryResult(query); 58 } 59 60 Result RunQueryResult(const Query &query) 61 { 62 if (this->CheckSQL()) 63 { 64 Result res = SQL->RunQuery(query); 65 if (!res.GetError().empty()) 66 Log(LOG_DEBUG) << "SQL-live got error " << res.GetError() << " for " + res.finished_query; 67 else 68 Log(LOG_DEBUG) << "SQL-live got " << res.Rows() << " rows for " << res.finished_query; 69 return res; 70 } 71 throw SQL::Exception("No SQL!"); 72 } 73 74 public: 75 DBMySQL(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE | VENDOR), SQL("", "") 76 { 77 this->lastwarn = 0; 78 this->ro = false; 79 this->init = false; 80 81 82 if (ModuleManager::FindFirstOf(DATABASE) != this) 83 throw ModuleException("If db_sql_live is loaded it must be the first database module loaded."); 84 } 85 86 void OnNotify() anope_override 87 { 88 if (!this->CheckInit()) 89 return; 90 91 for (std::set<Serializable *>::iterator it = this->updated_items.begin(), it_end = this->updated_items.end(); it != it_end; ++it) 92 { 93 Serializable *obj = *it; 94 95 if (obj && this->SQL) 96 { 97 Data data; 98 obj->Serialize(data); 99 100 if (obj->IsCached(data)) 101 continue; 102 103 obj->UpdateCache(data); 104 105 Serialize::Type *s_type = obj->GetSerializableType(); 106 if (!s_type) 107 continue; 108 109 std::vector<Query> create = this->SQL->CreateTable(this->prefix + s_type->GetName(), data); 110 for (unsigned i = 0; i < create.size(); ++i) 111 this->RunQueryResult(create[i]); 112 113 Result res = this->RunQueryResult(this->SQL->BuildInsert(this->prefix + s_type->GetName(), obj->id, data)); 114 if (res.GetID() && obj->id != res.GetID()) 115 { 116 /* In this case obj is new, so place it into the object map */ 117 obj->id = res.GetID(); 118 s_type->objects[obj->id] = obj; 119 } 120 } 121 } 122 123 this->updated_items.clear(); 124 } 125 126 EventReturn OnLoadDatabase() anope_override 127 { 128 init = true; 129 return EVENT_STOP; 130 } 131 132 void OnShutdown() anope_override 133 { 134 init = false; 135 } 136 137 void OnRestart() anope_override 138 { 139 init = false; 140 } 141 142 void OnReload(Configuration::Conf *conf) anope_override 143 { 144 Configuration::Block *block = conf->GetModule(this); 145 this->SQL = ServiceReference<Provider>("SQL::Provider", block->Get<const Anope::string>("engine")); 146 this->prefix = block->Get<const Anope::string>("prefix", "anope_db_"); 147 } 148 149 void OnSerializableConstruct(Serializable *obj) anope_override 150 { 151 if (!this->CheckInit()) 152 return; 153 obj->UpdateTS(); 154 this->updated_items.insert(obj); 155 this->Notify(); 156 } 157 158 void OnSerializableDestruct(Serializable *obj) anope_override 159 { 160 if (!this->CheckInit()) 161 return; 162 Serialize::Type *s_type = obj->GetSerializableType(); 163 if (s_type) 164 { 165 if (obj->id > 0) 166 this->RunQuery("DELETE FROM `" + this->prefix + s_type->GetName() + "` WHERE `id` = " + stringify(obj->id)); 167 s_type->objects.erase(obj->id); 168 } 169 this->updated_items.erase(obj); 170 } 171 172 void OnSerializeCheck(Serialize::Type *obj) anope_override 173 { 174 if (!this->CheckInit() || obj->GetTimestamp() == Anope::CurTime) 175 return; 176 177 Query query("SELECT * FROM `" + this->prefix + obj->GetName() + "` WHERE (`timestamp` >= " + this->SQL->FromUnixtime(obj->GetTimestamp()) + " OR `timestamp` IS NULL)"); 178 179 obj->UpdateTimestamp(); 180 181 Result res = this->RunQueryResult(query); 182 183 bool clear_null = false; 184 for (int i = 0; i < res.Rows(); ++i) 185 { 186 const std::map<Anope::string, Anope::string> &row = res.Row(i); 187 188 unsigned int id; 189 try 190 { 191 id = convertTo<unsigned int>(res.Get(i, "id")); 192 } 193 catch (const ConvertException &) 194 { 195 Log(LOG_DEBUG) << "Unable to convert id from " << obj->GetName(); 196 continue; 197 } 198 199 if (res.Get(i, "timestamp").empty()) 200 { 201 clear_null = true; 202 std::map<uint64_t, Serializable *>::iterator it = obj->objects.find(id); 203 if (it != obj->objects.end()) 204 delete it->second; // This also removes this object from the map 205 } 206 else 207 { 208 Data data; 209 210 for (std::map<Anope::string, Anope::string>::const_iterator it = row.begin(), it_end = row.end(); it != it_end; ++it) 211 data[it->first] << it->second; 212 213 Serializable *s = NULL; 214 std::map<uint64_t, Serializable *>::iterator it = obj->objects.find(id); 215 if (it != obj->objects.end()) 216 s = it->second; 217 218 Serializable *new_s = obj->Unserialize(s, data); 219 if (new_s) 220 { 221 // If s == new_s then s->id == new_s->id 222 if (s != new_s) 223 { 224 new_s->id = id; 225 obj->objects[id] = new_s; 226 227 /* The Unserialize operation is destructive so rebuild the data for UpdateCache. 228 * Also the old data may contain columns that we don't use, so we reserialize the 229 * object to know for sure our cache is consistent 230 */ 231 232 Data data2; 233 new_s->Serialize(data2); 234 new_s->UpdateCache(data2); /* We know this is the most up to date copy */ 235 } 236 } 237 else 238 { 239 if (!s) 240 this->RunQuery("UPDATE `" + prefix + obj->GetName() + "` SET `timestamp` = " + this->SQL->FromUnixtime(obj->GetTimestamp()) + " WHERE `id` = " + stringify(id)); 241 else 242 delete s; 243 } 244 } 245 } 246 247 if (clear_null) 248 { 249 query = "DELETE FROM `" + this->prefix + obj->GetName() + "` WHERE `timestamp` IS NULL"; 250 this->RunQuery(query); 251 } 252 } 253 254 void OnSerializableUpdate(Serializable *obj) anope_override 255 { 256 if (!this->CheckInit() || obj->IsTSCached()) 257 return; 258 obj->UpdateTS(); 259 this->updated_items.insert(obj); 260 this->Notify(); 261 } 262 }; 263 264 MODULE_INIT(DBMySQL)