diff options
author | Adam <Adam@anope.org> | 2011-09-25 04:19:15 -0400 |
---|---|---|
committer | Adam <Adam@anope.org> | 2011-09-25 04:19:15 -0400 |
commit | 1f2399de364c09adcce4193895cd362d80ffdfc5 (patch) | |
tree | 5f40fc531f22c174b6e10bb7bc12842a4a21d30b /modules/extra/m_sqlite.cpp | |
parent | 43201ead9575a74e350710bc191f4ac67366aca7 (diff) |
Added a new database format and sqlite support. Also moved db-convert to a module.
Diffstat (limited to 'modules/extra/m_sqlite.cpp')
-rw-r--r-- | modules/extra/m_sqlite.cpp | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/modules/extra/m_sqlite.cpp b/modules/extra/m_sqlite.cpp new file mode 100644 index 000000000..adf244bbd --- /dev/null +++ b/modules/extra/m_sqlite.cpp @@ -0,0 +1,235 @@ +/* RequiredLibraries: sqlite3 */ + +#include "module.h" +#include <sqlite3.h> +#include "sql.h" + +/* SQLite3 API, based from InspiRCd */ + +/** A SQLite result + */ +class SQLiteResult : public SQLResult +{ + public: + SQLiteResult(const SQLQuery &q, const Anope::string &fq) : SQLResult(q, fq) + { + } + + SQLiteResult(const SQLQuery &q, const Anope::string &fq, const Anope::string &err) : SQLResult(q, fq, err) + { + } + + void addRow(const std::map<Anope::string, Anope::string> &data) + { + this->entries.push_back(data); + } +}; + +/** A SQLite database, there can be multiple + */ +class SQLiteService : public SQLProvider +{ + Anope::string database; + + sqlite3 *sql; + + Anope::string Escape(const Anope::string &query); + + public: + SQLiteService(Module *o, const Anope::string &n, const Anope::string &d); + + ~SQLiteService(); + + void Run(SQLInterface *i, const SQLQuery &query); + + SQLResult RunQuery(const SQLQuery &query); + + SQLQuery CreateTable(const Anope::string &table, const SerializableBase::serialized_data &data); + + Anope::string BuildQuery(const SQLQuery &q); +}; + +class ModuleSQLite : public Module +{ + /* SQL connections */ + std::map<Anope::string, SQLiteService *> SQLiteServices; + public: + ModuleSQLite(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, SUPPORTED) + { + Implementation i[] = { I_OnReload }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + + OnReload(); + } + + ~ModuleSQLite() + { + for (std::map<Anope::string, SQLiteService *>::iterator it = this->SQLiteServices.begin(); it != this->SQLiteServices.end(); ++it) + delete it->second; + SQLiteServices.clear(); + } + + void OnReload() + { + ConfigReader config; + int i, num; + + for (std::map<Anope::string, SQLiteService *>::iterator it = this->SQLiteServices.begin(); it != this->SQLiteServices.end();) + { + const Anope::string &cname = it->first; + SQLiteService *s = it->second; + ++it; + + for (i = 0, num = config.Enumerate("sqlite"); i < num; ++i) + if (config.ReadValue("sqlite", "name", "sqlite/main", i) == cname) + break; + + if (i == num) + { + Log(LOG_NORMAL, "sqlite") << "SQLite: Removing server connection " << cname; + + delete s; + this->SQLiteServices.erase(cname); + } + } + + for (i = 0, num = config.Enumerate("sqlite"); i < num; ++i) + { + Anope::string connname = config.ReadValue("sqlite", "name", "sqlite/main", i); + + if (this->SQLiteServices.find(connname) == this->SQLiteServices.end()) + { + Anope::string database = config.ReadValue("sqlite", "database", "anope", i); + + try + { + SQLiteService *ss = new SQLiteService(this, connname, database); + this->SQLiteServices[connname] = ss; + + Log(LOG_NORMAL, "sqlite") << "SQLite: Successfully added database " << database; + } + catch (const SQLException &ex) + { + Log(LOG_NORMAL, "sqlite") << "SQLite: " << ex.GetReason(); + } + } + } + } +}; + +SQLiteService::SQLiteService(Module *o, const Anope::string &n, const Anope::string &d) +: SQLProvider(o, n), database(d), sql(NULL) +{ + int db = sqlite3_open_v2(database.c_str(), &this->sql, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0); + if (db != SQLITE_OK) + throw SQLException("Unable to open SQLite database " + database + ": " + sqlite3_errmsg(this->sql)); +} + +SQLiteService::~SQLiteService() +{ + sqlite3_interrupt(this->sql); + sqlite3_close(this->sql); +} + +void SQLiteService::Run(SQLInterface *i, const SQLQuery &query) +{ + SQLResult res = this->RunQuery(query); + if (!res.GetError().empty()) + i->OnError(res); + else + i->OnResult(res); +} + +SQLResult SQLiteService::RunQuery(const SQLQuery &query) +{ + Anope::string real_query = this->BuildQuery(query); + sqlite3_stmt *stmt; + int err = sqlite3_prepare_v2(this->sql, real_query.c_str(), real_query.length(), &stmt, NULL); + if (err != SQLITE_OK) + return SQLiteResult(query, real_query, sqlite3_errmsg(this->sql)); + + std::vector<Anope::string> columns; + int cols = sqlite3_column_count(stmt); + columns.resize(cols); + for (int i = 0; i < cols; ++i) + columns[i] = sqlite3_column_name(stmt, i); + + SQLiteResult result(query, real_query); + + do + { + err = sqlite3_step(stmt); + if (err == SQLITE_ROW) + { + std::map<Anope::string, Anope::string> items; + for (int i = 0; i < cols; ++i) + { + const char *data = reinterpret_cast<const char *>(sqlite3_column_text(stmt, i)); + if (data && *data) + items[columns[i]] = data; + } + result.addRow(items); + } + } + while (err == SQLITE_ROW); + + if (err != SQLITE_DONE) + return SQLiteResult(query, real_query, sqlite3_errmsg(this->sql)); + + return result; +} + +SQLQuery SQLiteService::CreateTable(const Anope::string &table, const SerializableBase::serialized_data &data) +{ + Anope::string query_text = "CREATE TABLE `" + table + "` (", key_buf; + for (SerializableBase::serialized_data::const_iterator it = data.begin(), it_end = data.end(); it != it_end; ++it) + { + query_text += "`" + it->first + "` "; + if (it->second.getType() == Serialize::DT_INT) + query_text += "int(11)"; + else if (it->second.getMax() > 0) + query_text += "varchar(" + stringify(it->second.getMax()) + ")"; + else + query_text += "text"; + query_text += ","; + + if (it->second.getKey()) + { + if (key_buf.empty()) + key_buf = "UNIQUE ("; + key_buf += "`" + it->first + "`,"; + } + } + if (!key_buf.empty()) + { + key_buf.erase(key_buf.end() - 1); + key_buf += ")"; + query_text += " " + key_buf; + } + else + query_text.erase(query_text.end() - 1); + query_text += ")"; + + return SQLQuery(query_text); +} + +Anope::string SQLiteService::Escape(const Anope::string &query) +{ + char *e = sqlite3_mprintf("%q", query.c_str()); + Anope::string buffer = e; + sqlite3_free(e); + return buffer; +} + +Anope::string SQLiteService::BuildQuery(const SQLQuery &q) +{ + Anope::string real_query = q.query; + + for (std::map<Anope::string, Anope::string>::const_iterator it = q.parameters.begin(), it_end = q.parameters.end(); it != it_end; ++it) + real_query = real_query.replace_all_cs("@" + it->first + "@", "'" + this->Escape(it->second) + "'"); + + return real_query; +} + +MODULE_INIT(ModuleSQLite) + |