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));
}
Beispiel #2
0
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;
	}
}
Beispiel #3
0
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);
}
Beispiel #4
0
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");
}
Beispiel #5
0
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);
}
Beispiel #6
0
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);
}
Beispiel #7
0
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);
}
Beispiel #8
0
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;
}
Beispiel #9
0
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);
	}
}
Beispiel #10
0
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."));
}
Beispiel #11
0
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;
}
Beispiel #12
0
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";
	
}
Beispiel #13
0
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;
}
Beispiel #14
0
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;
}
Beispiel #15
0
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;
}
Beispiel #16
0
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();
}