void check_class(uint16_t id, string name, unsigned long hash) { DCClass* dcc = g_dcf->get_class(id); if(name != dcc->get_name()) { // TODO: Try and update the database instead of exiting m_log->fatal() << "Class name '" << dcc->get_name() << "' from DCFile does not match" " name '" << name << "' in database, for dc_id " << id << endl; m_log->fatal() << "Database must be rebuilt." << endl; exit(1); } HashGenerator gen; dcc->generate_hash(gen); if(hash != gen.get_hash()) { // TODO: Try and update the database instead of exiting m_log->fatal() << "Class hash '" << gen.get_hash() << "' from DCFile does not match" " hash '" << hash << "' in database, for dc_id " << id << endl; m_log->fatal() << "Database must be rebuilt." << endl; exit(1); } // TODO: Check class_fields table exists }
bool get_field(uint32_t do_id, const DCField* field, vector<uint8_t> &value) { // Get class from the objects table DCClass* dcc = get_class(do_id); if(!dcc) { return false; // Object does not exist } bool stored = is_storable(dcc->get_number()); if(!stored) { return false; // Class has no database fields } vector<DCField*> fields; fields.push_back(const_cast<DCField*>(field)); map<DCField*, vector<uint8_t> > values; get_fields_from_table(do_id, dcc, fields, values); auto val_it = values.find(const_cast<DCField*>(field)); if(val_it == values.end()) { return false; } value = val_it->second; return true; }
void del_fields(uint32_t do_id, const vector<DCField*> &fields) { DCClass *dcc = get_class(do_id); bool storable = is_storable(dcc->get_number()); if(storable) { del_fields_in_table(do_id, dcc, fields); } }
void del_field(uint32_t do_id, DCField* field) { DCClass *dcc = get_class(do_id); bool storable = is_storable(dcc->get_number()); if(storable) { vector<DCField*> fields; fields.push_back(field); del_fields_in_table(do_id, dcc, fields); } }
bool set_field_if_equals(uint32_t do_id, DCField* field, const vector<uint8_t> &equal, vector<uint8_t> &value) { // Get class from the objects table DCClass* dcc = get_class(do_id); if(!dcc) { return false; // Object does not exist } bool stored = is_storable(dcc->get_number()); if(!stored) { return false; // Class has no database fields } if(!field->is_db()) { return false; } string val; indicator ind; m_sql << "SELECT " << field->get_name() << " FROM fields_" << dcc->get_name() << " WHERE object_id=" << do_id << ";", into(val, ind); if(ind != i_ok) { value.clear(); return false; } string packed_equal(equal.begin(), equal.end()); string eql = field->format_data(packed_equal, false); if(val != eql) { val = field->parse_string(val); value = vector<uint8_t>(val.begin(), val.end()); return false; } string packed_data(value.begin(), value.end()); val = field->format_data(packed_data, false); m_sql << "UPDATE fields_" << dcc->get_name() << " SET " << field->get_name() << "='" << val << "' WHERE object_id=" << do_id << ";"; return true; }
void set_fields(uint32_t do_id, const map<DCField*, vector<uint8_t> > &fields) { DCClass *dcc = get_class(do_id); bool storable = is_storable(dcc->get_number()); if(storable) { try { m_sql.begin(); // Start transaction set_fields_in_table(do_id, dcc, fields); m_sql.commit(); // End transaction } catch(const exception &e) { m_sql.rollback(); // Revert transaction } } }
bool get_fields(uint32_t do_id, const vector<DCField*> &fields, map<DCField*, vector<uint8_t> > &values) { // Get class from the objects table DCClass* dcc = get_class(do_id); if(!dcc) { return false; // Object does not exist } bool stored = is_storable(dcc->get_number()); if(!stored) { return false; // Class has no database fields } get_fields_from_table(do_id, dcc, fields, values); return true; }
bool get_object(uint32_t do_id, DatabaseObject& dbo) { m_log->trace() << "Getting object with id" << do_id << endl; // Get class from the objects table DCClass* dcc = get_class(do_id); if(!dcc) { return false; // Object does not exist } dbo.dc_id = dcc->get_number(); bool stored = is_storable(dcc->get_number()); if(stored) { get_all_from_table(do_id, dcc, dbo.fields); } return true; }
void delete_object(uint32_t do_id) { bool storable = false; DCClass* dcc = get_class(do_id); if(dcc) { // Note: needs to be called outside the transaction so it doesn't prevent deletion // of the object in the `objects` table storable = is_storable(dcc->get_number()); } m_log->debug() << "Deleting object with id " << do_id << "..." << endl; m_sql << "DELETE FROM objects WHERE id=" << do_id; if(dcc && storable) { m_log->trace() << "... object has stored field, also deleted." << endl; m_sql << "DELETE FROM fields_" << dcc->get_name() << " WHERE object_id=:id;", use(do_id); } push_id(do_id); }
void check_classes() { int dc_id; uint8_t storable; string dc_name; unsigned long dc_hash; // Prepare sql statements statement get_row_by_id = (m_sql.prepare << "SELECT hash, name FROM classes WHERE id=:id", into(dc_hash), into(dc_name), use(dc_id)); statement insert_class = (m_sql.prepare << "INSERT INTO classes VALUES (:id,:hash,:name,:stored)", use(dc_id), use(dc_hash), use(dc_name), use(storable)); // For each class, verify an entry exists and has the correct name and value for(dc_id = 0; dc_id < g_dcf->get_num_classes(); ++dc_id) { get_row_by_id.execute(true); if(m_sql.got_data()) { check_class(dc_id, dc_name, dc_hash); } else { DCClass* dcc = g_dcf->get_class(dc_id); // Create fields table for the class storable = create_fields_table(dcc); // Create class row in classes table HashGenerator gen; dcc->generate_hash(gen); dc_hash = gen.get_hash(); dc_name = dcc->get_name(); insert_class.execute(true); } } }
uint32_t create_object(const DatabaseObject& dbo) { string field_name; vector<uint8_t> field_value; DCClass *dcc = g_dcf->get_class(dbo.dc_id); bool storable = is_storable(dbo.dc_id); uint32_t do_id = pop_next_id(); if(!do_id) { return 0; } try { m_sql.begin(); // Start transaction m_sql << "INSERT INTO objects VALUES (" << do_id << "," << dbo.dc_id << ");"; if(storable) { // TODO: This would probably be a lot faster if it was all one statement. // Go ahead and simplify to one statement if you see a good way to do so. m_sql << "INSERT INTO fields_" << dcc->get_name() << "(object_id)" " VALUES(" << do_id << ");"; set_fields_in_table(do_id, dcc, dbo.fields); } m_sql.commit(); // End transaction } catch(const exception &e) { m_sql.rollback(); // Revert transaction return 0; } return do_id; }
virtual void handle_datagram(Datagram &in_dg, DatagramIterator &dgi) { channel_t sender = dgi.read_uint64(); unsigned short msg_type = dgi.read_uint16(); switch(msg_type) { case DBSERVER_CREATE_STORED_OBJECT: { unsigned int context = dgi.read_uint32(); // Start response with generic header Datagram resp; resp.add_server_header(sender, m_control_channel, DBSERVER_CREATE_STORED_OBJECT_RESP); resp.add_uint32(context); // Get DistributedClass unsigned short dc_id = dgi.read_uint16(); DCClass *dcc = gDCF->get_class(dc_id); if(!dcc) { m_log->error() << "Invalid DCClass when creating object: #" << dc_id << std::endl; resp.add_uint32(0); send(resp); return; } // Unpack fields to be passed to database DatabaseObject dbo(dc_id); unsigned short field_count = dgi.read_uint16(); m_log->spam() << "Unpacking fields..." << std::endl; try { for(unsigned int i = 0; i < field_count; ++i) { unsigned short field_id = dgi.read_uint16(); DCField *field = dcc->get_field_by_index(field_id); if(field) { if(field->is_db()) { dgi.unpack_field(field, dbo.fields[field]); } else { std::string tmp; dgi.unpack_field(field, tmp); } } } } catch(std::exception &e) { m_log->error() << "Error while unpacking fields, msg may be truncated. e.what(): " << e.what() << std::endl; resp.add_uint32(0); send(resp); return; } // Check for required fields, and populate with defaults m_log->spam() << "Checking all required fields exist..." << std::endl; for(int i = 0; i < dcc->get_num_inherited_fields(); ++i) { DCField *field = dcc->get_inherited_field(i); if(field->is_required() && field->is_db() && !field->as_molecular_field()) { if(dbo.fields.find(field) == dbo.fields.end()) { if(!field->has_default_value()) { m_log->error() << "Field " << field->get_name() << " missing when trying to create " "object of type " << dcc->get_name(); resp.add_uint32(0); send(resp); return; } else { dbo.fields[field] = field->get_default_value(); } } } } // Create object in database m_log->spam() << "Creating stored object..." << std::endl; unsigned int do_id = m_db_engine->create_object(dbo); if(do_id == 0 || do_id < m_min_id || do_id > m_max_id) { m_log->error() << "Ran out of DistributedObject ids while creating new object." << std::endl; resp.add_uint32(0); send(resp); return; } m_log->spam() << "... created with ID: " << do_id << std::endl; resp.add_uint32(do_id); send(resp); } break; case DBSERVER_SELECT_STORED_OBJECT_ALL: { unsigned int context = dgi.read_uint32(); Datagram resp; resp.add_server_header(sender, m_control_channel, DBSERVER_SELECT_STORED_OBJECT_ALL_RESP); resp.add_uint32(context); unsigned int do_id = dgi.read_uint32(); DatabaseObject dbo; if(m_db_engine->get_object(do_id, dbo)) { resp.add_uint8(1); resp.add_uint16(dbo.dc_id); resp.add_uint16(dbo.fields.size()); for(auto it = dbo.fields.begin(); it != dbo.fields.end(); ++it) { resp.add_uint16(it->first->get_number()); resp.add_data(it->second); } } else { resp.add_uint8(0); } send(resp); } break; case DBSERVER_DELETE_STORED_OBJECT: { if(dgi.read_uint32() == DBSERVER_DELETE_STORED_OBJECT_VERIFY_CODE) { unsigned int do_id = dgi.read_uint32(); m_db_engine->delete_object(do_id); m_log->debug() << "Deleted object with ID: " << do_id << std::endl; } else { m_log->warning() << "Wrong delete verify code." << std::endl; } } break; default: m_log->error() << "Recieved unknown MsgType: " << msg_type << std::endl; }; }
bool set_fields_if_equals(uint32_t do_id, const map<DCField*, vector<uint8_t> > &equals, map<DCField*, vector<uint8_t> > &values) { // Get class from the objects table DCClass* dcc = get_class(do_id); if(!dcc) { return false; // Object does not exist } bool stored = is_storable(dcc->get_number()); if(!stored) { return false; // Class has no database fields } bool failed = false; string value; indicator ind; vector<DCField*> null_fields; try { m_sql.begin(); // Start transaction for(auto it = equals.begin(); it != equals.end(); ++it) { DCField* field = it->first; if(field->is_db()) { m_sql << "SELECT " << field->get_name() << " FROM fields_" << dcc->get_name() << " WHERE object_id=" << do_id << ";", into(value, ind); if(ind != i_ok) { null_fields.push_back(field); failed = true; continue; } string packed_equal(it->second.begin(), it->second.end()); string equal = field->format_data(packed_equal, false); if(value == equal) { string packed_data(values[field].begin(), values[field].end()); string insert = field->format_data(packed_data, false); m_sql << "UPDATE fields_" << dcc->get_name() << " SET " << field->get_name() << "='" << insert << "' WHERE object_id=" << do_id << ";"; \ } else { failed = true; } value = field->parse_string(value); values[field] = vector<uint8_t>(value.begin(), value.end()); } } if(failed) { for(auto it = null_fields.begin(); it != null_fields.end(); ++it) { values.erase(*it); } m_sql.rollback(); // Revert transaction return false; } else { m_sql.commit(); // End transaction return true; } } catch(const exception &e) { m_sql.rollback(); // Revert transaction values.clear(); return false; } }
bool set_fields_if_empty(uint32_t do_id, map<DCField*, vector<uint8_t> > &values) { // Get class from the objects table DCClass* dcc = get_class(do_id); if(!dcc) { values.clear(); return false; // Object does not exist } bool stored = is_storable(dcc->get_number()); if(!stored) { values.clear(); return false; // Class has no database fields } bool failed = false; string value; indicator ind; try { m_sql.begin(); // Start transaction for(auto it = values.begin(); it != values.end(); ++it) { DCField* field = it->first; if(field->is_db()) { m_sql << "SELECT " << field->get_name() << " FROM fields_" << dcc->get_name() << " WHERE object_id=" << do_id << ";", into(value, ind); if(ind != i_null) { failed = true; string packed_data = field->parse_string(value); values[field] = vector<uint8_t>(packed_data.begin(), packed_data.end()); continue; } string packed_data(it->second.begin(), it->second.end()); value = it->first->format_data(packed_data, false); m_sql << "UPDATE fields_" << dcc->get_name() << " SET " << field->get_name() << "='" << value << "' WHERE object_id=" << do_id << ";"; } } if(failed) { m_sql.rollback(); // Revert transaction } else { m_sql.commit(); // End transaction } } catch(const exception &e) { m_sql.rollback(); // Revert transaction values.clear(); return false; } }