summaryrefslogtreecommitdiff
path: root/modules/database/sql.cpp
diff options
context:
space:
mode:
authorAdam <Adam@anope.org>2015-12-14 16:52:24 -0500
committerAdam <Adam@anope.org>2015-12-14 16:52:24 -0500
commit6302f980fe6dad83aab7d2dc1046dadea5ffd939 (patch)
tree4c27d33f55fc32ce34827977932dac4b3e5ceedd /modules/database/sql.cpp
parent64dac60071fab652745a6e7a06cf6b7bdbbd3625 (diff)
New source tree structure for modules. From commands/cs_access => chanserv/access etc.
Diffstat (limited to 'modules/database/sql.cpp')
-rw-r--r--modules/database/sql.cpp372
1 files changed, 372 insertions, 0 deletions
diff --git a/modules/database/sql.cpp b/modules/database/sql.cpp
new file mode 100644
index 000000000..fed0050e4
--- /dev/null
+++ b/modules/database/sql.cpp
@@ -0,0 +1,372 @@
+#include "module.h"
+#include "modules/sql.h"
+
+using namespace SQL;
+
+class DBMySQL : public Module, public Pipe
+ , public EventHook<Event::SerializeEvents>
+{
+ private:
+ bool transaction = false;
+ bool inited = false;
+ Anope::string prefix;
+ ServiceReference<Provider> SQL;
+
+ Result Run(const Query &query)
+ {
+ if (!SQL)
+ ;//XXX
+
+ if (!inited)
+ {
+ inited = true;
+ for (const Query &q : SQL->InitSchema(prefix))
+ SQL->RunQuery(q);
+ }
+
+ return SQL->RunQuery(query);
+ }
+
+ void StartTransaction()
+ {
+ if (!SQL || transaction)
+ return;
+
+ Run(SQL->BeginTransaction());
+
+ transaction = true;
+ Notify();
+ }
+
+ void Commit()
+ {
+ if (!SQL || !transaction)
+ return;
+
+ Run(SQL->Commit());
+
+ transaction = false;
+ }
+
+ public:
+ DBMySQL(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE | VENDOR)
+ {
+ }
+
+ void OnNotify() override
+ {
+ Commit();
+ Serialize::Clear();
+ }
+
+ void OnReload(Configuration::Conf *conf) override
+ {
+ Configuration::Block *block = conf->GetModule(this);
+ this->SQL = ServiceReference<Provider>("SQL::Provider", block->Get<Anope::string>("engine"));
+ this->prefix = block->Get<Anope::string>("prefix", "anope_db_");
+ inited = false;
+ }
+
+ EventReturn OnSerializeList(Serialize::TypeBase *type, std::vector<Serialize::ID> &ids) override
+ {
+ StartTransaction();
+
+ ids.clear();
+
+ Query query = "SELECT `id` FROM `" + prefix + type->GetName() + "`";
+ Result res = Run(query);
+ for (int i = 0; i < res.Rows(); ++i)
+ {
+ Serialize::ID id = convertTo<Serialize::ID>(res.Get(i, "id"));
+ ids.push_back(id);
+ }
+
+ return EVENT_ALLOW;
+ }
+
+ EventReturn OnSerializeFind(Serialize::TypeBase *type, Serialize::FieldBase *field, const Anope::string &value, Serialize::ID &id) override
+ {
+ if (!SQL)
+ return EVENT_CONTINUE;
+
+ StartTransaction();
+
+ for (Query &q : SQL->CreateTable(prefix, type->GetName()))
+ Run(q);
+
+ for (Query &q : SQL->AlterTable(prefix, type->GetName(), field->GetName(), false))
+ Run(q);
+
+ for (const Query &q : SQL->CreateIndex(prefix + type->GetName(), field->GetName()))
+ Run(q);
+
+ Query query("SELECT `id` FROM `" + prefix + type->GetName() + "` WHERE `" + field->GetName() + "` = @value@");
+ query.SetValue("value", value);
+ Result res = Run(query);
+ if (res.Rows())
+ try
+ {
+ id = convertTo<Serialize::ID>(res.Get(0, "id"));
+ return EVENT_ALLOW;
+ }
+ catch (const ConvertException &)
+ {
+ }
+ return EVENT_CONTINUE;
+ }
+
+ private:
+ bool GetValue(Serialize::Object *object, Serialize::FieldBase *field, SQL::Result::Value &v)
+ {
+ StartTransaction();
+
+ Query query = "SELECT `" + field->GetName() + "` FROM `" + prefix + object->GetSerializableType()->GetName() + "` WHERE `id` = @id@";
+ query.SetValue("id", object->id);
+ Result res = Run(query);
+
+ if (res.Rows() == 0)
+ return false;
+
+ v = res.GetValue(0, field->GetName());
+ return true;
+ }
+
+ public:
+ EventReturn OnSerializeGet(Serialize::Object *object, Serialize::FieldBase *field, Anope::string &value) override
+ {
+ SQL::Result::Value v;
+
+ if (!GetValue(object, field, v))
+ {
+ field->CacheMiss(object);
+ return EVENT_CONTINUE;
+ }
+
+ value = v.value;
+ return EVENT_ALLOW;
+ }
+
+ EventReturn OnSerializeGetRefs(Serialize::Object *object, Serialize::TypeBase *type, std::vector<Serialize::Edge> &edges) override
+ {
+ StartTransaction();
+
+ edges.clear();
+
+ Query query;
+ if (type)
+ query = "SELECT field," + prefix + "edges.id,other_id,j1.type,j2.type AS other_type FROM `" + prefix + "edges` "
+ "JOIN `" + prefix + "objects` AS j1 ON " + prefix + "edges.id = j1.id "
+ "JOIN `" + prefix + "objects` AS j2 ON " + prefix + "edges.other_id = j2.id "
+ "WHERE "
+ " (" + prefix + "edges.id = @id@ AND j2.type = @other_type@) "
+ "OR"
+ " (other_id = @id@ AND j1.type = @other_type@)";
+ else
+ query = "SELECT field," + prefix + "edges.id,other_id,j1.type,j2.type AS other_type FROM `" + prefix + "edges` "
+ "JOIN `" + prefix + "objects` AS j1 ON " + prefix + "edges.id = j1.id "
+ "JOIN `" + prefix + "objects` AS j2 ON " + prefix + "edges.other_id = j2.id "
+ "WHERE " + prefix + "edges.id = @id@ OR other_id = @id@";
+
+ query.SetValue("type", object->GetSerializableType()->GetName());
+ query.SetValue("id", object->id);
+ if (type)
+ query.SetValue("other_type", type->GetName());
+
+ Result res = Run(query);
+ for (int i = 0; i < res.Rows(); ++i)
+ {
+ Serialize::ID id = convertTo<Serialize::ID>(res.Get(i, "id"));
+
+ if (id == object->id)
+ {
+ // we want other type, this is my edge
+ Anope::string t = res.Get(i, "other_type");
+ Anope::string f = res.Get(i, "field");
+ id = convertTo<Serialize::ID>(res.Get(i, "other_id"));
+
+ //XXX sanity checks
+ Serialize::FieldBase *obj_field = object->GetSerializableType()->GetField(f);
+
+ Serialize::TypeBase *obj_type = Serialize::TypeBase::Find(t);
+ Serialize::Object *other = obj_type->Require(id);
+
+ edges.emplace_back(other, obj_field, true);
+ }
+ else
+ {
+ // edge to me
+ Anope::string t = res.Get(i, "type");
+ Anope::string f = res.Get(i, "field");
+
+ //XXX sanity checks
+ Serialize::TypeBase *obj_type = Serialize::TypeBase::Find(t);
+ Serialize::FieldBase *obj_field = obj_type->GetField(f);
+ Serialize::Object *other = obj_type->Require(id);
+
+ // other type, other field,
+ edges.emplace_back(other, obj_field, false);
+ }
+ }
+
+ return EVENT_ALLOW;
+ }
+
+ EventReturn OnSerializeDeref(Serialize::ID id, Serialize::TypeBase *type) override
+ {
+ StartTransaction();
+
+ Query query = "SELECT `id` FROM `" + prefix + type->GetName() + "` WHERE `id` = @id@";
+ query.SetValue("id", id);
+ Result res = Run(query);
+ if (res.Rows() == 0)
+ return EVENT_CONTINUE;
+ return EVENT_ALLOW;
+ }
+
+ EventReturn OnSerializeGetSerializable(Serialize::Object *object, Serialize::FieldBase *field, Anope::string &type, Serialize::ID &value) override
+ {
+ StartTransaction();
+
+ Query query = "SELECT `" + field->GetName() + "`,j1.type AS " + field->GetName() + "_type FROM `" + prefix + object->GetSerializableType()->GetName() + "` "
+ "JOIN `" + prefix + "objects` AS j1 ON " + prefix + object->GetSerializableType()->GetName() + "." + field->GetName() + " = j1.id "
+ "WHERE " + prefix + object->GetSerializableType()->GetName() + ".id = @id@";
+ query.SetValue("id", object->id);
+ Result res = Run(query);
+
+ if (res.Rows() == 0)
+ return EVENT_CONTINUE;
+
+ type = res.Get(0, field->GetName() + "_type");
+ try
+ {
+ value = convertTo<Serialize::ID>(res.Get(0, field->GetName()));
+ }
+ catch (const ConvertException &ex)
+ {
+ return EVENT_STOP;
+ }
+
+ return EVENT_ALLOW;
+ }
+
+ private:
+ void DoSet(Serialize::Object *object, Serialize::FieldBase *field, bool is_object, const Anope::string *value)
+ {
+ if (!SQL)
+ return;
+
+ StartTransaction();
+
+ for (Query &q : SQL->CreateTable(prefix, object->GetSerializableType()->GetName()))
+ Run(q);
+
+ for (Query &q : SQL->AlterTable(prefix, object->GetSerializableType()->GetName(), field->GetName(), is_object))
+ Run(q);
+
+ Query q;
+ q.SetValue("id", object->id);
+ if (value)
+ q.SetValue(field->GetName(), *value);
+ else
+ q.SetNull(field->GetName());
+
+ for (Query &q2 : SQL->Replace(prefix + object->GetSerializableType()->GetName(), q, { "id" }))
+ Run(q2);
+ }
+
+ public:
+ EventReturn OnSerializeSet(Serialize::Object *object, Serialize::FieldBase *field, const Anope::string &value) override
+ {
+ DoSet(object, field, false, &value);
+ return EVENT_STOP;
+ }
+
+ EventReturn OnSerializeSetSerializable(Serialize::Object *object, Serialize::FieldBase *field, Serialize::Object *value) override
+ {
+ if (!SQL)
+ return EVENT_CONTINUE;
+
+ StartTransaction();
+
+ if (value)
+ {
+ Anope::string v = stringify(value->id);
+ DoSet(object, field, true, &v);
+
+ Query query;
+ query.SetValue("field", field->GetName());
+ query.SetValue("id", object->id);
+ query.SetValue("other_id", value->id);
+
+ for (Query &q : SQL->Replace(prefix + "edges", query, { "id", "field" }))
+ Run(q);
+ }
+ else
+ {
+ DoSet(object, field, true, nullptr);
+
+ Query query("DELETE FROM `" + prefix + "edges` WHERE `id` = @id@ AND `field` = @field@");
+ query.SetValue("id", object->id);
+ query.SetValue("field", field->GetName());
+ Run(query);
+ }
+
+ return EVENT_STOP;
+ }
+
+ EventReturn OnSerializeUnset(Serialize::Object *object, Serialize::FieldBase *field) override
+ {
+ DoSet(object, field, false, nullptr);
+ field->CacheMiss(object);
+ return EVENT_STOP;
+ }
+
+ EventReturn OnSerializeUnsetSerializable(Serialize::Object *object, Serialize::FieldBase *field) override
+ {
+ DoSet(object, field, true, nullptr);
+ field->CacheMiss(object);
+
+ Query query("DELETE FROM `" + prefix + "edges` WHERE `id` = @id@ AND `field` = @field@");
+ query.SetValue("id", object->id);
+ query.SetValue("field", field->GetName());
+ Run(query);
+
+ return EVENT_STOP;
+ }
+
+ EventReturn OnSerializeHasField(Serialize::Object *object, Serialize::FieldBase *field) override
+ {
+ SQL::Result::Value v;
+
+ return GetValue(object, field, v) && !v.null ? EVENT_STOP : EVENT_CONTINUE;
+ }
+
+ EventReturn OnSerializableGetId(Serialize::ID &id) override
+ {
+ StartTransaction();
+
+ id = SQL->GetID(prefix);
+ return EVENT_ALLOW;
+ }
+
+ void OnSerializableCreate(Serialize::Object *object) override
+ {
+ StartTransaction();
+
+ Query q = Query("INSERT INTO `" + prefix + "objects` (`id`,`type`) VALUES (@id@, @type@)");
+ q.SetValue("id", object->id);
+ q.SetValue("type", object->GetSerializableType()->GetName());
+ Run(q);
+ }
+
+ void OnSerializableDelete(Serialize::Object *object) override
+ {
+ StartTransaction();
+
+ Query query("DELETE FROM `" + prefix + object->GetSerializableType()->GetName() + "` WHERE `id` = " + stringify(object->id));
+ Run(query);
+ }
+};
+
+MODULE_INIT(DBMySQL)
+