#ifndef __COMM_PROTOBUF_DATABASE_ADAPTER_H__ #define __COMM_PROTOBUF_DATABASE_ADAPTER_H__ #include #include #include #include "DatabaseCampaignMessage.pb.h" #include "util/Database.hpp" #include "util/StringJoiner.hpp" #include namespace fail { class DatabaseProtobufAdapter { Database *db, *db_insert; MYSQL_STMT *stmt; std::stringstream insert_stmt; void error_create_table(); /** \class TypeBridge A type bridge bridges the gap between a protobuf type and the sql database. It defines how the result table is defined, and how the message types are mapped onto SQL types. The whole message is mapped into a top level TypeBridge_message */ struct TypeBridge { bool primary_key; // !< Should the field be put into the // !< primary key const google::protobuf::FieldDescriptor *desc; virtual ~TypeBridge() {}; TypeBridge(const google::protobuf::FieldDescriptor *desc); /* The field name in the SQL Table */ virtual std::string name() { return desc->name(); } /* The expression in the create stmt for this type. */ virtual std::string sql_create_stmt() { return name() + " " + sql_type() + (desc->is_required() ? " NOT NULL": ""); }; /* The mapped type */ virtual std::string sql_type() = 0; virtual int element_size() { return 0; }; virtual void copy_to(const google::protobuf::Message *msg, int i, void *) { }; /* A common function that handles NULL values for fields */ bool insert_null(MYSQL_BIND *bind, const google::protobuf::Message *msg) { const google::protobuf::Reflection *ref = msg->GetReflection(); if (!ref->HasField(*msg, desc)) { bind->buffer_type = MYSQL_TYPE_NULL; return true; // handled } return false; } virtual void bind(MYSQL_BIND *bind, const google::protobuf::Message *msg) = 0; }; struct TypeBridge_repeated : TypeBridge { TypeBridge *inner; char *buffer; TypeBridge_repeated(const google::protobuf::FieldDescriptor *desc, TypeBridge *inner) : TypeBridge(desc), inner(inner), buffer(0) {}; virtual std::string sql_type() { return "blob"; }; virtual void bind(MYSQL_BIND *bind, const google::protobuf::Message *msg); }; struct TypeBridge_int32 : TypeBridge { int32_t buffer; TypeBridge_int32(const google::protobuf::FieldDescriptor *desc) : TypeBridge(desc){}; virtual std::string sql_type() { return "INT"; }; virtual int element_size() { return 4; }; virtual void copy_to(const google::protobuf::Message *msg, int i, void *); virtual void bind(MYSQL_BIND *bind, const google::protobuf::Message *msg); }; struct TypeBridge_uint32 : TypeBridge { uint32_t buffer; TypeBridge_uint32(const google::protobuf::FieldDescriptor *desc) : TypeBridge(desc){}; virtual std::string sql_type() { return "INT"; }; virtual int element_size() { return 4; }; virtual void copy_to(const google::protobuf::Message *msg, int i, void *); virtual void bind(MYSQL_BIND *bind, const google::protobuf::Message *msg); }; struct TypeBridge_int64 : TypeBridge { int64_t buffer; TypeBridge_int64(const google::protobuf::FieldDescriptor *desc) : TypeBridge(desc){}; virtual std::string sql_type() { return "BIGINT"; }; virtual int element_size() { return 8; }; virtual void copy_to(const google::protobuf::Message *msg, int i, void *); virtual void bind(MYSQL_BIND *bind, const google::protobuf::Message *msg); }; struct TypeBridge_uint64 : TypeBridge { uint64_t buffer; TypeBridge_uint64(const google::protobuf::FieldDescriptor *desc) : TypeBridge(desc){}; virtual std::string sql_type() { return "BIGINT"; }; virtual int element_size() { return 8; }; virtual void copy_to(const google::protobuf::Message *msg, int i, void *); virtual void bind(MYSQL_BIND *bind, const google::protobuf::Message *msg); }; struct TypeBridge_double : TypeBridge { double buffer; TypeBridge_double(const google::protobuf::FieldDescriptor *desc) : TypeBridge(desc){}; virtual std::string sql_type() { return "DOUBLE"; }; virtual int element_size() { return 8; }; virtual void copy_to(const google::protobuf::Message *msg, int i, void *); virtual void bind(MYSQL_BIND *bind, const google::protobuf::Message *msg); }; struct TypeBridge_float : TypeBridge { float buffer; TypeBridge_float(const google::protobuf::FieldDescriptor *desc) : TypeBridge(desc){}; virtual std::string sql_type() { return "FLOAT"; }; virtual int element_size() { return 4; }; virtual void copy_to(const google::protobuf::Message *msg, int i, void *); virtual void bind(MYSQL_BIND *bind, const google::protobuf::Message *msg); }; struct TypeBridge_bool : TypeBridge { bool buffer; TypeBridge_bool(const google::protobuf::FieldDescriptor *desc) : TypeBridge(desc){}; virtual std::string sql_type() { return "TINYINT"; }; virtual int element_size() { return 1; }; virtual void copy_to(const google::protobuf::Message *msg, int i, void *); virtual void bind(MYSQL_BIND *bind, const google::protobuf::Message *msg); }; struct TypeBridge_string : TypeBridge { TypeBridge_string(const google::protobuf::FieldDescriptor *desc) : TypeBridge(desc){}; virtual std::string sql_type() { return "TEXT"; }; virtual void bind(MYSQL_BIND *bind, const google::protobuf::Message *msg); }; struct TypeBridge_enum : TypeBridge { int32_t size; TypeBridge_enum(const google::protobuf::FieldDescriptor *desc) : TypeBridge(desc) {}; virtual std::string sql_type(); virtual void bind(MYSQL_BIND *bind, const google::protobuf::Message *msg); }; struct TypeBridge_message : TypeBridge { const google::protobuf::Descriptor *msg_type; int nesting_level; int field_count; std::vector types; std::vector *selector; /* Pointer to the toplevel message */ TypeBridge_message *parent; TypeBridge_message * top_level_msg() { TypeBridge_message *p = this; while (p->parent != 0) p = p->parent; return p; } std::vector repeated_message_stack; TypeBridge_message(const google::protobuf::FieldDescriptor *desc, const google::protobuf::Descriptor *msg_type, TypeBridge_message *parent) : TypeBridge(desc), msg_type(msg_type), parent(parent) { if (parent) nesting_level = parent->nesting_level+1; else nesting_level = 0; }; virtual std::string sql_create_stmt(); virtual std::string sql_type() { return ""; }; virtual void bind(MYSQL_BIND *bind, const google::protobuf::Message *msg); /* Returns the number of enclosed fields */ int gatherTypes(StringJoiner &insert_stmt, StringJoiner &primary_key); }; TypeBridge_message top_level_msg; std::string result_table_name; int field_size_at_pos(const google::protobuf::Message *msg, std::vector selector, int pos); public: DatabaseProtobufAdapter() : db(0), db_insert(0), stmt(0), top_level_msg(0, 0, 0) {} void set_database_handle(Database *db) { this->db = db; if (!db_insert) { db_insert = db; } } /** * Set a different database handle to be used in insert_row(). Necessary * if INSERTs are done in a separate thread, while the handle set with * set_database_handle() is still in use concurrently. */ void set_insert_database_handle(Database *db) { db_insert = db; } void create_table(const google::protobuf::Descriptor *); bool insert_row(const google::protobuf::Message *msg); std::string result_table() { return result_table_name; } }; } #endif