void Database_SQLite3::openDatabase() { if (m_database) return; std::string dbp = m_savedir + DIR_DELIM + "map.sqlite"; // Open the database connection if (!fs::CreateAllDirs(m_savedir)) { infostream << "Database_SQLite3: Failed to create directory \"" << m_savedir << "\"" << std::endl; throw FileNotGoodException("Failed to create database " "save directory"); } bool needs_create = !fs::PathExists(dbp); if (sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL) != SQLITE_OK) { errorstream << "SQLite3 database failed to open: " << sqlite3_errmsg(m_database) << std::endl; throw FileNotGoodException("Cannot open database file"); } if (needs_create) { createDatabase(); } std::string query_str = std::string("PRAGMA synchronous = ") + itos(g_settings->getU16("sqlite_synchronous")); SQLOK(sqlite3_exec(m_database, query_str.c_str(), NULL, NULL, NULL)); }
void Database_SQLite3::verifyDatabase() { if(m_database) return; { std::string dbp = m_savedir + DIR_DELIM + "map.sqlite"; bool needs_create = false; int d; /* Open the database connection */ createDirs(m_savedir); // ? if(!fs::PathExists(dbp)) needs_create = true; d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL); if(d != SQLITE_OK) { infostream<<"WARNING: SQLite3 database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl; throw FileNotGoodException("Cannot open database file"); } if(needs_create) createDatabase(); std::string querystr = std::string("PRAGMA synchronous = ") + itos(g_settings->getU16("sqlite_synchronous")); d = sqlite3_exec(m_database, querystr.c_str(), NULL, NULL, NULL); if(d != SQLITE_OK) { infostream<<"WARNING: Database pragma set failed: " <<sqlite3_errmsg(m_database)<<std::endl; throw FileNotGoodException("Cannot set pragma"); } d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL); if(d != SQLITE_OK) { infostream<<"WARNING: SQLite3 database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl; throw FileNotGoodException("Cannot prepare read statement"); } d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL); if(d != SQLITE_OK) { infostream<<"WARNING: SQLite3 database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl; throw FileNotGoodException("Cannot prepare write statement"); } d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL); if(d != SQLITE_OK) { infostream<<"WARNING: SQLite3 database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl; throw FileNotGoodException("Cannot prepare read statement"); } infostream<<"ServerMap: SQLite3 database opened"<<std::endl; } }
void Database_Redis::listAllLoadableBlocks(std::list<v3s16> &dst) { redisReply *reply; reply = (redisReply*) redisCommand(ctx, "HKEYS %s", hash.c_str()); if(!reply) throw FileNotGoodException(std::string("redis command 'HKEYS %s' failed: ") + ctx->errstr); if(reply->type != REDIS_REPLY_ARRAY) throw FileNotGoodException("Failed to get keys from database"); for(size_t i = 0; i < reply->elements; i++) { assert(reply->element[i]->type == REDIS_REPLY_STRING); dst.push_back(getIntegerAsBlock(stoi64(reply->element[i]->str))); } freeReplyObject(reply); }
void Database_SQLite3::openDatabase() { if (m_database) return; std::string dbp = m_savedir + DIR_DELIM + "map.sqlite"; // Open the database connection if (!fs::CreateAllDirs(m_savedir)) { infostream << "Database_SQLite3: Failed to create directory \"" << m_savedir << "\"" << std::endl; throw FileNotGoodException("Failed to create database " "save directory"); } bool needs_create = !fs::PathExists(dbp); SQLOK(sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL), std::string("Failed to open SQLite3 database file ") + dbp); SQLOK(sqlite3_busy_handler(m_database, Database_SQLite3::busyHandler, m_busy_handler_data), "Failed to set SQLite3 busy handler"); if (needs_create) { createDatabase(); } std::string query_str = std::string("PRAGMA synchronous = ") + itos(g_settings->getU16("sqlite_synchronous")); SQLOK(sqlite3_exec(m_database, query_str.c_str(), NULL, NULL, NULL), "Failed to modify sqlite3 synchronous mode"); }
void Database_Redis::endSave() { redisReply *reply; reply = (redisReply*) redisCommand(ctx, "EXEC"); if(!reply) throw FileNotGoodException(std::string("redis command 'EXEC' failed: ") + ctx->errstr); freeReplyObject(reply); }
void Database_Redis::endSave() { redisReply *reply = static_cast<redisReply *>(redisCommand(ctx, "EXEC")); if (!reply) { throw FileNotGoodException(std::string( "Redis command 'EXEC' failed: ") + ctx->errstr); } freeReplyObject(reply); }
void Database_Redis::listAllLoadableBlocks(std::vector<v3s16> &dst) { redisReply *reply = static_cast<redisReply *>(redisCommand(ctx, "HKEYS %s", hash.c_str())); if (!reply) { throw FileNotGoodException(std::string( "Redis command 'HKEYS %s' failed: ") + ctx->errstr); } switch (reply->type) { case REDIS_REPLY_ARRAY: for (size_t i = 0; i < reply->elements; i++) { assert(reply->element[i]->type == REDIS_REPLY_STRING); dst.push_back(getIntegerAsBlock(stoi64(reply->element[i]->str))); } case REDIS_REPLY_ERROR: throw FileNotGoodException(std::string( "Failed to get keys from database: ") + reply->str); } freeReplyObject(reply); }
void FileLogOutput::open(const std::string &filename) { m_stream.open(filename.c_str(), std::ios::app | std::ios::ate); if (!m_stream.good()) throw FileNotGoodException("Failed to open log file " + filename + ": " + strerror(errno)); m_stream << "\n\n" "-------------" << std::endl << " Separator" << std::endl << "-------------\n" << std::endl; }
Database_Redis::Database_Redis(Settings &conf) { std::string tmp; try { tmp = conf.get("redis_address"); hash = conf.get("redis_hash"); } catch (SettingNotFoundException) { throw SettingNotFoundException("Set redis_address and " "redis_hash in world.mt to use the redis backend"); } const char *addr = tmp.c_str(); int port = conf.exists("redis_port") ? conf.getU16("redis_port") : 6379; ctx = redisConnect(addr, port); if (!ctx) { throw FileNotGoodException("Cannot allocate redis context"); } else if (ctx->err) { std::string err = std::string("Connection error: ") + ctx->errstr; redisFree(ctx); throw FileNotGoodException(err); } }
std::string Database_Redis::loadBlock(const v3s16 &pos) { std::string tmp = i64tos(getBlockAsInteger(pos)); redisReply *reply = static_cast<redisReply *>(redisCommand(ctx, "HGET %s %s", hash.c_str(), tmp.c_str())); if (!reply) { throw FileNotGoodException(std::string( "Redis command 'HGET %s %s' failed: ") + ctx->errstr); } switch (reply->type) { case REDIS_REPLY_STRING: { std::string str(reply->str, reply->len); // std::string copies the memory so this won't cause any problems freeReplyObject(reply); return str; } case REDIS_REPLY_ERROR: { std::string errstr = reply->str; freeReplyObject(reply); errorstream << "loadBlock: loading block " << PP(pos) << " failed: " << errstr << std::endl; throw FileNotGoodException(std::string( "Redis command 'HGET %s %s' errored: ") + errstr); } case REDIS_REPLY_NIL: { // block not found in database freeReplyObject(reply); return ""; } } errorstream << "loadBlock: loading block " << PP(pos) << " returned invalid reply type " << reply->type << ": " << reply->str << std::endl; freeReplyObject(reply); throw FileNotGoodException(std::string( "Redis command 'HGET %s %s' gave invalid reply.")); }
Database_Redis::Database_Redis(ServerMap *map, std::string savedir) { Settings conf; conf.readConfigFile((std::string(savedir) + DIR_DELIM + "world.mt").c_str()); std::string tmp; try { tmp = conf.get("redis_address"); hash = conf.get("redis_hash"); } catch(SettingNotFoundException e) { throw SettingNotFoundException("Set redis_address and redis_hash in world.mt to use the redis backend"); } const char *addr = tmp.c_str(); int port = conf.exists("redis_port") ? conf.getU16("redis_port") : 6379; ctx = redisConnect(addr, port); if(!ctx) throw FileNotGoodException("Cannot allocate redis context"); else if(ctx->err) { std::string err = std::string("Connection error: ") + ctx->errstr; redisFree(ctx); throw FileNotGoodException(err); } srvmap = map; }
void Database_SQLite3::createDatabase() { int e; assert(m_database); e = sqlite3_exec(m_database, "CREATE TABLE IF NOT EXISTS `blocks` (" "`pos` INT NOT NULL PRIMARY KEY," "`data` BLOB" ");" , NULL, NULL, NULL); if(e == SQLITE_ABORT) throw FileNotGoodException("Could not create sqlite3 database structure"); else infostream<<"ServerMap: SQLite3 database structure was created"; }
std::string Database_Redis::loadBlock(v3s16 blockpos) { std::string tmp = i64tos(getBlockAsInteger(blockpos)); redisReply *reply; reply = (redisReply*) redisCommand(ctx, "HGET %s %s", hash.c_str(), tmp.c_str()); if(!reply) throw FileNotGoodException(std::string("redis command 'HGET %s %s' failed: ") + ctx->errstr); if(reply->type != REDIS_REPLY_STRING) { freeReplyObject(reply); return ""; } std::string str(reply->str, reply->len); freeReplyObject(reply); // std::string copies the memory so this won't cause any problems return str; }
bool Database_Redis::deleteBlock(const v3s16 &pos) { std::string tmp = i64tos(getBlockAsInteger(pos)); redisReply *reply = static_cast<redisReply *>(redisCommand(ctx, "HDEL %s %s", hash.c_str(), tmp.c_str())); if (!reply) { throw FileNotGoodException(std::string( "Redis command 'HDEL %s %s' failed: ") + ctx->errstr); } else if (reply->type == REDIS_REPLY_ERROR) { errorstream << "WARNING: deleteBlock: deleting block " << PP(pos) << " failed: " << reply->str << std::endl; freeReplyObject(reply); return false; } freeReplyObject(reply); return true; }
void RollbackManager::migrate(const std::string & file_path) { std::cout << "Migrating from rollback.txt to rollback.sqlite." << std::endl; std::ifstream fh(file_path.c_str(), std::ios::in | std::ios::ate); if (!fh.good()) { throw FileNotGoodException("Unable to open rollback.txt"); } std::streampos file_size = fh.tellg(); if (file_size < 10) { errorstream << "Empty rollback log." << std::endl; return; } fh.seekg(0); sqlite3_stmt *stmt_begin; sqlite3_stmt *stmt_commit; SQLOK(sqlite3_prepare_v2(db, "BEGIN", -1, &stmt_begin, NULL)); SQLOK(sqlite3_prepare_v2(db, "COMMIT", -1, &stmt_commit, NULL)); std::string bit; int i = 0; time_t start = time(0); time_t t = start; SQLRES(sqlite3_step(stmt_begin), SQLITE_DONE); sqlite3_reset(stmt_begin); do { ActionRow row; row.id = 0; // Get the timestamp std::getline(fh, bit, ' '); bit = trim(bit); if (!atoi(bit.c_str())) { std::getline(fh, bit); continue; } row.timestamp = atoi(bit.c_str()); // Get the actor row.actor = getActorId(deSerializeJsonString(fh)); // Get the action type std::getline(fh, bit, '['); std::getline(fh, bit, ' '); if (bit == "modify_inventory_stack") { row.type = RollbackAction::TYPE_MODIFY_INVENTORY_STACK; row.location = trim(deSerializeJsonString(fh)); std::getline(fh, bit, ' '); row.list = trim(deSerializeJsonString(fh)); std::getline(fh, bit, ' '); std::getline(fh, bit, ' '); row.index = atoi(trim(bit).c_str()); std::getline(fh, bit, ' '); row.add = (int)(trim(bit) == "add"); row.stack.deSerialize(deSerializeJsonString(fh)); row.stack.id = getNodeId(row.stack.name); std::getline(fh, bit); } else if (bit == "set_node") { row.type = RollbackAction::TYPE_SET_NODE; std::getline(fh, bit, '('); std::getline(fh, bit, ','); row.x = atoi(trim(bit).c_str()); std::getline(fh, bit, ','); row.y = atoi(trim(bit).c_str()); std::getline(fh, bit, ')'); row.z = atoi(trim(bit).c_str()); std::getline(fh, bit, ' '); row.oldNode = getNodeId(trim(deSerializeJsonString(fh))); std::getline(fh, bit, ' '); std::getline(fh, bit, ' '); row.oldParam1 = atoi(trim(bit).c_str()); std::getline(fh, bit, ' '); row.oldParam2 = atoi(trim(bit).c_str()); row.oldMeta = trim(deSerializeJsonString(fh)); std::getline(fh, bit, ' '); row.newNode = getNodeId(trim(deSerializeJsonString(fh))); std::getline(fh, bit, ' '); std::getline(fh, bit, ' '); row.newParam1 = atoi(trim(bit).c_str()); std::getline(fh, bit, ' '); row.newParam2 = atoi(trim(bit).c_str()); row.newMeta = trim(deSerializeJsonString(fh)); std::getline(fh, bit, ' '); std::getline(fh, bit, ' '); std::getline(fh, bit); row.guessed = (int)(trim(bit) == "actor_is_guess"); } else { errorstream << "Unrecognized rollback action type \"" << bit << "\"!" << std::endl; continue; } registerRow(row); ++i; if (time(0) - t >= 1) { SQLRES(sqlite3_step(stmt_commit), SQLITE_DONE); sqlite3_reset(stmt_commit); t = time(0); std::cout << " Done: " << static_cast<int>((static_cast<float>(fh.tellg()) / static_cast<float>(file_size)) * 100) << "%" << " Speed: " << i / (t - start) << "/second \r" << std::flush; SQLRES(sqlite3_step(stmt_begin), SQLITE_DONE); sqlite3_reset(stmt_begin); } } while (fh.good()); SQLRES(sqlite3_step(stmt_commit), SQLITE_DONE); sqlite3_reset(stmt_commit); SQLOK(sqlite3_finalize(stmt_begin)); SQLOK(sqlite3_finalize(stmt_commit)); std::cout << " Done: 100% " << std::endl << "Now you can delete the old rollback.txt file." << std::endl; }
void Database_SQLite3::saveBlock(MapBlock *block) { DSTACK(__FUNCTION_NAME); /* Dummy blocks are not written */ if(block->isDummy()) { /*v3s16 p = block->getPos(); infostream<<"Database_SQLite3::saveBlock(): WARNING: Not writing dummy block " <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/ return; } // Format used for writing u8 version = SER_FMT_VER_HIGHEST; // Get destination v3s16 p3d = block->getPos(); #if 0 v2s16 p2d(p3d.X, p3d.Z); std::string sectordir = getSectorDir(p2d); createDirs(sectordir); std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d); std::ofstream o(fullpath.c_str(), std::ios_base::binary); if(o.good() == false) throw FileNotGoodException("Cannot open block data"); #endif /* [0] u8 serialization version [1] data */ verifyDatabase(); std::ostringstream o(std::ios_base::binary); o.write((char*)&version, 1); // Write basic data block->serialize(o, version, true); // Write block to database std::string tmp = o.str(); const char *bytes = tmp.c_str(); if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK) infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl; if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl; int written = sqlite3_step(m_database_write); if(written != SQLITE_DONE) infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") " <<sqlite3_errmsg(m_database)<<std::endl; // Make ready for later reuse sqlite3_reset(m_database_write); // We just wrote it to the disk so clear modified flag block->resetModified(); }