00001
00002
00003 #include "module.h"
00004 #include <sqlite3.h>
00005 #include "sql.h"
00006
00007 using namespace SQL;
00008
00009
00010
00013 class SQLiteResult : public Result
00014 {
00015 public:
00016 SQLiteResult(unsigned int i, const Query &q, const Anope::string &fq) : Result(i, q, fq)
00017 {
00018 }
00019
00020 SQLiteResult(const Query &q, const Anope::string &fq, const Anope::string &err) : Result(0, q, fq, err)
00021 {
00022 }
00023
00024 void AddRow(const std::map<Anope::string, Anope::string> &data)
00025 {
00026 this->entries.push_back(data);
00027 }
00028 };
00029
00032 class SQLiteService : public Provider
00033 {
00034 std::map<Anope::string, std::set<Anope::string> > active_schema;
00035
00036 Anope::string database;
00037
00038 sqlite3 *sql;
00039
00040 Anope::string Escape(const Anope::string &query);
00041
00042 public:
00043 SQLiteService(Module *o, const Anope::string &n, const Anope::string &d);
00044
00045 ~SQLiteService();
00046
00047 void Run(Interface *i, const Query &query) anope_override;
00048
00049 Result RunQuery(const Query &query);
00050
00051 std::vector<Query> CreateTable(const Anope::string &table, const Data &data) anope_override;
00052
00053 Query BuildInsert(const Anope::string &table, unsigned int id, Data &data);
00054
00055 Query GetTables(const Anope::string &prefix);
00056
00057 Anope::string BuildQuery(const Query &q);
00058
00059 Anope::string FromUnixtime(time_t);
00060 };
00061
00062 class ModuleSQLite : public Module
00063 {
00064
00065 std::map<Anope::string, SQLiteService *> SQLiteServices;
00066 public:
00067 ModuleSQLite(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, SUPPORTED)
00068 {
00069 Implementation i[] = { I_OnReload };
00070 ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation));
00071
00072 OnReload();
00073 }
00074
00075 ~ModuleSQLite()
00076 {
00077 for (std::map<Anope::string, SQLiteService *>::iterator it = this->SQLiteServices.begin(); it != this->SQLiteServices.end(); ++it)
00078 delete it->second;
00079 SQLiteServices.clear();
00080 }
00081
00082 void OnReload() anope_override
00083 {
00084 ConfigReader config;
00085 int i, num;
00086
00087 for (std::map<Anope::string, SQLiteService *>::iterator it = this->SQLiteServices.begin(); it != this->SQLiteServices.end();)
00088 {
00089 const Anope::string &cname = it->first;
00090 SQLiteService *s = it->second;
00091 ++it;
00092
00093 for (i = 0, num = config.Enumerate("sqlite"); i < num; ++i)
00094 if (config.ReadValue("sqlite", "name", "sqlite/main", i) == cname)
00095 break;
00096
00097 if (i == num)
00098 {
00099 Log(LOG_NORMAL, "sqlite") << "SQLite: Removing server connection " << cname;
00100
00101 delete s;
00102 this->SQLiteServices.erase(cname);
00103 }
00104 }
00105
00106 for (i = 0, num = config.Enumerate("sqlite"); i < num; ++i)
00107 {
00108 Anope::string connname = config.ReadValue("sqlite", "name", "sqlite/main", i);
00109
00110 if (this->SQLiteServices.find(connname) == this->SQLiteServices.end())
00111 {
00112 Anope::string database = Anope::DataDir + "/" + config.ReadValue("sqlite", "database", "anope", i);
00113
00114 try
00115 {
00116 SQLiteService *ss = new SQLiteService(this, connname, database);
00117 this->SQLiteServices[connname] = ss;
00118
00119 Log(LOG_NORMAL, "sqlite") << "SQLite: Successfully added database " << database;
00120 }
00121 catch (const SQL::Exception &ex)
00122 {
00123 Log(LOG_NORMAL, "sqlite") << "SQLite: " << ex.GetReason();
00124 }
00125 }
00126 }
00127 }
00128 };
00129
00130 SQLiteService::SQLiteService(Module *o, const Anope::string &n, const Anope::string &d)
00131 : Provider(o, n), database(d), sql(NULL)
00132 {
00133 int db = sqlite3_open_v2(database.c_str(), &this->sql, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0);
00134 if (db != SQLITE_OK)
00135 throw SQL::Exception("Unable to open SQLite database " + database + ": " + sqlite3_errmsg(this->sql));
00136 }
00137
00138 SQLiteService::~SQLiteService()
00139 {
00140 sqlite3_interrupt(this->sql);
00141 sqlite3_close(this->sql);
00142 }
00143
00144 void SQLiteService::Run(Interface *i, const Query &query)
00145 {
00146 Result res = this->RunQuery(query);
00147 if (!res.GetError().empty())
00148 i->OnError(res);
00149 else
00150 i->OnResult(res);
00151 }
00152
00153 Result SQLiteService::RunQuery(const Query &query)
00154 {
00155 Anope::string real_query = this->BuildQuery(query);
00156 sqlite3_stmt *stmt;
00157 int err = sqlite3_prepare_v2(this->sql, real_query.c_str(), real_query.length(), &stmt, NULL);
00158 if (err != SQLITE_OK)
00159 return SQLiteResult(query, real_query, sqlite3_errmsg(this->sql));
00160
00161 std::vector<Anope::string> columns;
00162 int cols = sqlite3_column_count(stmt);
00163 columns.resize(cols);
00164 for (int i = 0; i < cols; ++i)
00165 columns[i] = sqlite3_column_name(stmt, i);
00166
00167 SQLiteResult result(0, query, real_query);
00168
00169 while ((err = sqlite3_step(stmt)) == SQLITE_ROW)
00170 {
00171 std::map<Anope::string, Anope::string> items;
00172 for (int i = 0; i < cols; ++i)
00173 {
00174 const char *data = reinterpret_cast<const char *>(sqlite3_column_text(stmt, i));
00175 if (data && *data)
00176 items[columns[i]] = data;
00177 }
00178 result.AddRow(items);
00179 }
00180
00181 result.id = sqlite3_last_insert_rowid(this->sql);
00182
00183 sqlite3_finalize(stmt);
00184
00185 if (err != SQLITE_DONE)
00186 return SQLiteResult(query, real_query, sqlite3_errmsg(this->sql));
00187
00188 return result;
00189 }
00190
00191 std::vector<Query> SQLiteService::CreateTable(const Anope::string &table, const Data &data)
00192 {
00193 std::vector<Query> queries;
00194 std::set<Anope::string> &known_cols = this->active_schema[table];
00195
00196 if (known_cols.empty())
00197 {
00198 Log(LOG_DEBUG) << "m_sqlite: Fetching columns for " << table;
00199
00200 Result columns = this->RunQuery("PRAGMA table_info(" + table + ")");
00201 for (int i = 0; i < columns.Rows(); ++i)
00202 {
00203 const Anope::string &column = columns.Get(i, "name");
00204
00205 Log(LOG_DEBUG) << "m_sqlite: Column #" << i << " for " << table << ": " << column;
00206 known_cols.insert(column);
00207 }
00208 }
00209
00210 if (known_cols.empty())
00211 {
00212 Anope::string query_text = "CREATE TABLE `" + table + "` (`id` INTEGER PRIMARY KEY, `timestamp` timestamp DEFAULT CURRENT_TIMESTAMP";
00213
00214 for (Data::Map::const_iterator it = data.data.begin(), it_end = data.data.end(); it != it_end; ++it)
00215 {
00216 known_cols.insert(it->first);
00217
00218 query_text += ", `" + it->first + "` ";
00219 if (data.GetType(it->first) == Serialize::Data::DT_INT)
00220 query_text += "int(11)";
00221 else
00222 query_text += "text";
00223 }
00224
00225 query_text += ")";
00226
00227 queries.push_back(query_text);
00228
00229 query_text = "CREATE UNIQUE INDEX `" + table + "_id_idx` ON `" + table + "` (`id`)";
00230 queries.push_back(query_text);
00231
00232 query_text = "CREATE INDEX `" + table + "_timestamp_idx` ON `" + table + "` (`timestamp`)";
00233 queries.push_back(query_text);
00234
00235 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;";
00236 queries.push_back(query_text);
00237 }
00238 else
00239 for (Data::Map::const_iterator it = data.data.begin(), it_end = data.data.end(); it != it_end; ++it)
00240 {
00241 if (known_cols.count(it->first) > 0)
00242 continue;
00243
00244 known_cols.insert(it->first);
00245
00246 Anope::string query_text = "ALTER TABLE `" + table + "` ADD `" + it->first + "` ";
00247 if (data.GetType(it->first) == Serialize::Data::DT_INT)
00248 query_text += "int(11)";
00249 else
00250 query_text += "text";
00251
00252 queries.push_back(query_text);
00253 }
00254
00255 return queries;
00256 }
00257
00258 Query SQLiteService::BuildInsert(const Anope::string &table, unsigned int id, Data &data)
00259 {
00260
00261 const std::set<Anope::string> &known_cols = this->active_schema[table];
00262 for (std::set<Anope::string>::iterator it = known_cols.begin(), it_end = known_cols.end(); it != it_end; ++it)
00263 if (*it != "id" && *it != "timestamp" && data.data.count(*it) == 0)
00264 data[*it] << "";
00265
00266 Anope::string query_text = "REPLACE INTO `" + table + "` (";
00267 if (id > 0)
00268 query_text += "`id`,";
00269 for (Data::Map::const_iterator it = data.data.begin(), it_end = data.data.end(); it != it_end; ++it)
00270 query_text += "`" + it->first + "`,";
00271 query_text.erase(query_text.length() - 1);
00272 query_text += ") VALUES (";
00273 if (id > 0)
00274 query_text += stringify(id) + ",";
00275 for (Data::Map::const_iterator it = data.data.begin(), it_end = data.data.end(); it != it_end; ++it)
00276 query_text += "@" + it->first + "@,";
00277 query_text.erase(query_text.length() - 1);
00278 query_text += ")";
00279
00280 Query query(query_text);
00281 for (Data::Map::const_iterator it = data.data.begin(), it_end = data.data.end(); it != it_end; ++it)
00282 {
00283 Anope::string buf;
00284 *it->second >> buf;
00285 query.SetValue(it->first, buf);
00286 }
00287
00288 return query;
00289 }
00290
00291 Query SQLiteService::GetTables(const Anope::string &prefix)
00292 {
00293 return Query("SELECT name FROM sqlite_master WHERE type='table' AND name LIKE '" + prefix + "%';");
00294 }
00295
00296 Anope::string SQLiteService::Escape(const Anope::string &query)
00297 {
00298 char *e = sqlite3_mprintf("%q", query.c_str());
00299 Anope::string buffer = e;
00300 sqlite3_free(e);
00301 return buffer;
00302 }
00303
00304 Anope::string SQLiteService::BuildQuery(const Query &q)
00305 {
00306 Anope::string real_query = q.query;
00307
00308 for (std::map<Anope::string, QueryData>::const_iterator it = q.parameters.begin(), it_end = q.parameters.end(); it != it_end; ++it)
00309 real_query = real_query.replace_all_cs("@" + it->first + "@", (it->second.escape ? ("'" + this->Escape(it->second.data) + "'") : it->second.data));
00310
00311 return real_query;
00312 }
00313
00314 Anope::string SQLiteService::FromUnixtime(time_t t)
00315 {
00316 return "datetime('" + stringify(t) + "', 'unixepoch')";
00317 }
00318
00319 MODULE_INIT(ModuleSQLite)
00320