anope- supernets anope source code & configuration |
git clone git://git.acid.vegas/anope.git |
Log | Files | Refs | Archive | README |
m_sqlite.cpp (9331B)
1 /* 2 * 3 * (C) 2011-2022 Anope Team 4 * Contact us at team@anope.org 5 * 6 * Please read COPYING and README for further details. 7 */ 8 9 /* RequiredLibraries: sqlite3 */ 10 /* RequiredWindowsLibraries: sqlite3 */ 11 12 #include "module.h" 13 #include "modules/sql.h" 14 #include <sqlite3.h> 15 16 using namespace SQL; 17 18 /* SQLite3 API, based from InspIRCd */ 19 20 /** A SQLite result 21 */ 22 class SQLiteResult : public Result 23 { 24 public: 25 SQLiteResult(unsigned int i, const Query &q, const Anope::string &fq) : Result(i, q, fq) 26 { 27 } 28 29 SQLiteResult(const Query &q, const Anope::string &fq, const Anope::string &err) : Result(0, q, fq, err) 30 { 31 } 32 33 void AddRow(const std::map<Anope::string, Anope::string> &data) 34 { 35 this->entries.push_back(data); 36 } 37 }; 38 39 /** A SQLite database, there can be multiple 40 */ 41 class SQLiteService : public Provider 42 { 43 std::map<Anope::string, std::set<Anope::string> > active_schema; 44 45 Anope::string database; 46 47 sqlite3 *sql; 48 49 Anope::string Escape(const Anope::string &query); 50 51 public: 52 SQLiteService(Module *o, const Anope::string &n, const Anope::string &d); 53 54 ~SQLiteService(); 55 56 void Run(Interface *i, const Query &query) anope_override; 57 58 Result RunQuery(const Query &query); 59 60 std::vector<Query> CreateTable(const Anope::string &table, const Data &data) anope_override; 61 62 Query BuildInsert(const Anope::string &table, unsigned int id, Data &data); 63 64 Query GetTables(const Anope::string &prefix); 65 66 Anope::string BuildQuery(const Query &q); 67 68 Anope::string FromUnixtime(time_t); 69 }; 70 71 class ModuleSQLite : public Module 72 { 73 /* SQL connections */ 74 std::map<Anope::string, SQLiteService *> SQLiteServices; 75 public: 76 ModuleSQLite(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR) 77 { 78 } 79 80 ~ModuleSQLite() 81 { 82 for (std::map<Anope::string, SQLiteService *>::iterator it = this->SQLiteServices.begin(); it != this->SQLiteServices.end(); ++it) 83 delete it->second; 84 SQLiteServices.clear(); 85 } 86 87 void OnReload(Configuration::Conf *conf) anope_override 88 { 89 Configuration::Block *config = conf->GetModule(this); 90 91 for (std::map<Anope::string, SQLiteService *>::iterator it = this->SQLiteServices.begin(); it != this->SQLiteServices.end();) 92 { 93 const Anope::string &cname = it->first; 94 SQLiteService *s = it->second; 95 int i, num; 96 ++it; 97 98 for (i = 0, num = config->CountBlock("sqlite"); i < num; ++i) 99 if (config->GetBlock("sqlite", i)->Get<const Anope::string>("name", "sqlite/main") == cname) 100 break; 101 102 if (i == num) 103 { 104 Log(LOG_NORMAL, "sqlite") << "SQLite: Removing server connection " << cname; 105 106 delete s; 107 this->SQLiteServices.erase(cname); 108 } 109 } 110 111 for (int i = 0; i < config->CountBlock("sqlite"); ++i) 112 { 113 Configuration::Block *block = config->GetBlock("sqlite", i); 114 Anope::string connname = block->Get<const Anope::string>("name", "sqlite/main"); 115 116 if (this->SQLiteServices.find(connname) == this->SQLiteServices.end()) 117 { 118 Anope::string database = Anope::DataDir + "/" + block->Get<const Anope::string>("database", "anope"); 119 120 try 121 { 122 SQLiteService *ss = new SQLiteService(this, connname, database); 123 this->SQLiteServices[connname] = ss; 124 125 Log(LOG_NORMAL, "sqlite") << "SQLite: Successfully added database " << database; 126 } 127 catch (const SQL::Exception &ex) 128 { 129 Log(LOG_NORMAL, "sqlite") << "SQLite: " << ex.GetReason(); 130 } 131 } 132 } 133 } 134 }; 135 136 SQLiteService::SQLiteService(Module *o, const Anope::string &n, const Anope::string &d) 137 : Provider(o, n), database(d), sql(NULL) 138 { 139 int db = sqlite3_open_v2(database.c_str(), &this->sql, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0); 140 if (db != SQLITE_OK) 141 { 142 Anope::string exstr = "Unable to open SQLite database " + database; 143 if (this->sql) 144 { 145 exstr += ": "; 146 exstr += sqlite3_errmsg(this->sql); 147 sqlite3_close(this->sql); 148 } 149 throw SQL::Exception(exstr); 150 } 151 } 152 153 SQLiteService::~SQLiteService() 154 { 155 sqlite3_interrupt(this->sql); 156 sqlite3_close(this->sql); 157 } 158 159 void SQLiteService::Run(Interface *i, const Query &query) 160 { 161 Result res = this->RunQuery(query); 162 if (!res.GetError().empty()) 163 i->OnError(res); 164 else 165 i->OnResult(res); 166 } 167 168 Result SQLiteService::RunQuery(const Query &query) 169 { 170 Anope::string real_query = this->BuildQuery(query); 171 sqlite3_stmt *stmt; 172 int err = sqlite3_prepare_v2(this->sql, real_query.c_str(), real_query.length(), &stmt, NULL); 173 if (err != SQLITE_OK) 174 return SQLiteResult(query, real_query, sqlite3_errmsg(this->sql)); 175 176 std::vector<Anope::string> columns; 177 int cols = sqlite3_column_count(stmt); 178 columns.resize(cols); 179 for (int i = 0; i < cols; ++i) 180 columns[i] = sqlite3_column_name(stmt, i); 181 182 SQLiteResult result(0, query, real_query); 183 184 while ((err = sqlite3_step(stmt)) == SQLITE_ROW) 185 { 186 std::map<Anope::string, Anope::string> items; 187 for (int i = 0; i < cols; ++i) 188 { 189 const char *data = reinterpret_cast<const char *>(sqlite3_column_text(stmt, i)); 190 if (data && *data) 191 items[columns[i]] = data; 192 } 193 result.AddRow(items); 194 } 195 196 result.id = sqlite3_last_insert_rowid(this->sql); 197 198 sqlite3_finalize(stmt); 199 200 if (err != SQLITE_DONE) 201 return SQLiteResult(query, real_query, sqlite3_errmsg(this->sql)); 202 203 return result; 204 } 205 206 std::vector<Query> SQLiteService::CreateTable(const Anope::string &table, const Data &data) 207 { 208 std::vector<Query> queries; 209 std::set<Anope::string> &known_cols = this->active_schema[table]; 210 211 if (known_cols.empty()) 212 { 213 Log(LOG_DEBUG) << "m_sqlite: Fetching columns for " << table; 214 215 Result columns = this->RunQuery("PRAGMA table_info(" + table + ")"); 216 for (int i = 0; i < columns.Rows(); ++i) 217 { 218 const Anope::string &column = columns.Get(i, "name"); 219 220 Log(LOG_DEBUG) << "m_sqlite: Column #" << i << " for " << table << ": " << column; 221 known_cols.insert(column); 222 } 223 } 224 225 if (known_cols.empty()) 226 { 227 Anope::string query_text = "CREATE TABLE `" + table + "` (`id` INTEGER PRIMARY KEY, `timestamp` timestamp DEFAULT CURRENT_TIMESTAMP"; 228 229 for (Data::Map::const_iterator it = data.data.begin(), it_end = data.data.end(); it != it_end; ++it) 230 { 231 known_cols.insert(it->first); 232 233 query_text += ", `" + it->first + "` "; 234 if (data.GetType(it->first) == Serialize::Data::DT_INT) 235 query_text += "int(11)"; 236 else 237 query_text += "text"; 238 } 239 240 query_text += ")"; 241 242 queries.push_back(query_text); 243 244 query_text = "CREATE UNIQUE INDEX `" + table + "_id_idx` ON `" + table + "` (`id`)"; 245 queries.push_back(query_text); 246 247 query_text = "CREATE INDEX `" + table + "_timestamp_idx` ON `" + table + "` (`timestamp`)"; 248 queries.push_back(query_text); 249 250 query_text = "CREATE TRIGGER `" + table + "_trigger` AFTER UPDATE ON `" + table + "` FOR EACH ROW BEGIN UPDATE `" + table + "` SET `timestamp` = CURRENT_TIMESTAMP WHERE `id` = `old.id`; end;"; 251 queries.push_back(query_text); 252 } 253 else 254 for (Data::Map::const_iterator it = data.data.begin(), it_end = data.data.end(); it != it_end; ++it) 255 { 256 if (known_cols.count(it->first) > 0) 257 continue; 258 259 known_cols.insert(it->first); 260 261 Anope::string query_text = "ALTER TABLE `" + table + "` ADD `" + it->first + "` "; 262 if (data.GetType(it->first) == Serialize::Data::DT_INT) 263 query_text += "int(11)"; 264 else 265 query_text += "text"; 266 267 queries.push_back(query_text); 268 } 269 270 return queries; 271 } 272 273 Query SQLiteService::BuildInsert(const Anope::string &table, unsigned int id, Data &data) 274 { 275 /* Empty columns not present in the data set */ 276 const std::set<Anope::string> &known_cols = this->active_schema[table]; 277 for (std::set<Anope::string>::iterator it = known_cols.begin(), it_end = known_cols.end(); it != it_end; ++it) 278 if (*it != "id" && *it != "timestamp" && data.data.count(*it) == 0) 279 data[*it] << ""; 280 281 Anope::string query_text = "REPLACE INTO `" + table + "` ("; 282 if (id > 0) 283 query_text += "`id`,"; 284 for (Data::Map::const_iterator it = data.data.begin(), it_end = data.data.end(); it != it_end; ++it) 285 query_text += "`" + it->first + "`,"; 286 query_text.erase(query_text.length() - 1); 287 query_text += ") VALUES ("; 288 if (id > 0) 289 query_text += stringify(id) + ","; 290 for (Data::Map::const_iterator it = data.data.begin(), it_end = data.data.end(); it != it_end; ++it) 291 query_text += "@" + it->first + "@,"; 292 query_text.erase(query_text.length() - 1); 293 query_text += ")"; 294 295 Query query(query_text); 296 for (Data::Map::const_iterator it = data.data.begin(), it_end = data.data.end(); it != it_end; ++it) 297 { 298 Anope::string buf; 299 *it->second >> buf; 300 query.SetValue(it->first, buf); 301 } 302 303 return query; 304 } 305 306 Query SQLiteService::GetTables(const Anope::string &prefix) 307 { 308 return Query("SELECT name FROM sqlite_master WHERE type='table' AND name LIKE '" + prefix + "%';"); 309 } 310 311 Anope::string SQLiteService::Escape(const Anope::string &query) 312 { 313 char *e = sqlite3_mprintf("%q", query.c_str()); 314 Anope::string buffer = e; 315 sqlite3_free(e); 316 return buffer; 317 } 318 319 Anope::string SQLiteService::BuildQuery(const Query &q) 320 { 321 Anope::string real_query = q.query; 322 323 for (std::map<Anope::string, QueryData>::const_iterator it = q.parameters.begin(), it_end = q.parameters.end(); it != it_end; ++it) 324 real_query = real_query.replace_all_cs("@" + it->first + "@", (it->second.escape ? ("'" + this->Escape(it->second.data) + "'") : it->second.data)); 325 326 return real_query; 327 } 328 329 Anope::string SQLiteService::FromUnixtime(time_t t) 330 { 331 return "datetime('" + stringify(t) + "', 'unixepoch')"; 332 } 333 334 MODULE_INIT(ModuleSQLite)