Skip to content

Commit

Permalink
init module sql update
Browse files Browse the repository at this point in the history
  • Loading branch information
zz authored and coolzoom committed Sep 4, 2023
1 parent 14ef98a commit 3556aae
Show file tree
Hide file tree
Showing 10 changed files with 284 additions and 1 deletion.
Empty file.
Empty file.
1 change: 1 addition & 0 deletions modules/mod_sample/sql/mangos/config.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
INSERT INTO `mangos`.`module_config` (`id`, `config`, `value`) VALUES ('1', 'module_sample', '0');
Empty file.
173 changes: 173 additions & 0 deletions src/game/World.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
#include "TransportMgr.h"

#include <chrono>
#include <ace/OS_NS_dirent.h>

INSTANTIATE_SINGLETON_1(World);

Expand Down Expand Up @@ -491,9 +492,178 @@ int32 World::GetModuleIntConfig(std::string conf, uint32 value)

}

void World::LoadModSQLUpdates()
{



// module
m_ModSQLUpdatesPath = sConfig.GetStringDefault("DatabaseUpdater.ModPathToUpdates", "");
if (!m_ModSQLUpdatesPath.size() || (*m_ModSQLUpdatesPath.rbegin() != '\\' && *m_ModSQLUpdatesPath.rbegin() != '/'))
#if PLATFORM == PLATFORM_WINDOWS
m_ModSQLUpdatesPath += '\\';
#else
m_ModSQLUpdatesPath += '/';
#endif

const struct
{
// db pointer
DatabaseMysql* db;
// path - modules/mod_xxx/sql/(path)
const char* path;
} updates[]
=
{
{ &LoginDatabase, "realmd" },
{ &WorldDatabase, "mangos" },
{ &CharacterDatabase, "characters" }
};

// directory path
std::string path;
// label used for console output
std::stringstream label;
// files to be applied
std::vector<std::string> files;
// already applied before (from db)
std::set<std::string> alreadyAppliedFiles;

// get folders in modules/
if (ACE_DIR* dira = ACE_OS::opendir(m_ModSQLUpdatesPath.c_str()))
{
while (ACE_DIRENT* directory = ACE_OS::readdir(dira))
{
// Skip the ".." and "." files.
if (ACE::isdotdir(directory->d_name) == true)
continue;

// refresh path
path = m_ModSQLUpdatesPath;
#if PLATFORM == PLATFORM_WINDOWS
// get mod directory list
path += directory->d_name;
path += '\\';
#else
// get mod directory list
path += directory->d_name;
path += '/';
#endif

ACE_stat stat_buf;
if (ACE_OS::lstat(path.c_str(), &stat_buf) == -1)
{
sLog.Out(LOG_BASIC, LOG_LVL_ERROR, "directory error %s: %s", path.c_str(), strerror(errno));
continue;
}
switch (stat_buf.st_mode & S_IFMT)
{
case S_IFDIR://is directory?
{

// iterate all three databases
for (uint32 i = 0; i < 3; i++)
{
// clear from previous iteration
files.clear();
// clear from previous iteration
alreadyAppliedFiles.clear();

// directory path
std::string pathsql;
pathsql = path;
// refresh path
#if PLATFORM == PLATFORM_WINDOWS
// get sub folder list
pathsql += "sql";
pathsql += '\\';
pathsql += updates[i].path;
pathsql += '\\';
#else
// get sub folder list
pathsql += "sql";
pathsql += '/';
pathsql += updates[i].path;
pathsql += '/';
#endif


//// Get updates that were alraedy applied before
//if (QueryResult* result = updates[i].db->Query("SELECT `update` FROM `updates`"))
//{
// do
// alreadyAppliedFiles.insert(result->Fetch()[0].GetString());
// while (result->NextRow());
//}

// Record current working directory
char cwd[PATH_MAX];
ACE_OS::getcwd(cwd, PATH_MAX);

// Change current directory to modules/mod_xxx/sql(path)
if (-1 == ACE_OS::chdir(pathsql.c_str()))
sLog.Out(LOG_BASIC, LOG_LVL_ERROR, "Can't change directory to %s: %s", pathsql.c_str(), strerror(errno));

// get files in modules/mod_xxx/sql/(path)/ directory
if (ACE_DIR* dir = ACE_OS::opendir(pathsql.c_str()))
{
while (ACE_DIRENT* entry = ACE_OS::readdir(dir))
{
// always apply if it is a conf file
if (!strcmp(entry->d_name + strlen(entry->d_name) - 9, ".conf.sql"))
files.push_back(entry->d_name);
// continue only if file is not already applied
if (alreadyAppliedFiles.find(entry->d_name) == alreadyAppliedFiles.end())
// make sure the file is an .sql one
if (!strcmp(entry->d_name + strlen(entry->d_name) - 4, ".sql"))
files.push_back(entry->d_name);
}
//ACE_OS::closedir(dir);
}
else
sLog.Out(LOG_BASIC, LOG_LVL_ERROR, "Can't open %s: %s", pathsql.c_str(), strerror(errno));

// sort our files in ascending order
std::sort(files.begin(), files.end());

// iterate not applied files now
for (size_t j = 0; j < files.size(); ++j)
{
label.str("");
label << "Applying " << files[j].c_str() << " (" << (j + 1) << '/' << files.size() << ')';
sLog.Out(LOG_BASIC, LOG_LVL_MINIMAL, label.str().c_str());

if (updates[i].db->ExecuteFile(files[j].c_str()))
{
updates[i].db->escape_string(files[j]);
//updates[i].db->DirectPExecute("INSERT INTO `updates` VALUES ('%s', NOW())", files[j].c_str());
}
else
sLog.Out(LOG_BASIC, LOG_LVL_ERROR, "Failed to apply %s. See db_errors.log for more details.", files[j].c_str());
}

// Return to original working directory
if (-1 == ACE_OS::chdir(cwd))
sLog.Out(LOG_BASIC, LOG_LVL_ERROR, "Can't change directory to %s: %s", cwd, strerror(errno));
}
}
break;

default: // Must be some other type of file (PIPE/FIFO/device)
break;
}
}
//ACE_OS::closedir(dira);
}
else
sLog.Out(LOG_BASIC, LOG_LVL_ERROR, "Can't open %s: %s", m_ModSQLUpdatesPath.c_str(), strerror(errno));

}

// Initialize config values
void World::LoadConfigSettings(bool reload)
{

if (reload)
{
if (!sConfig.Reload())
Expand Down Expand Up @@ -1350,6 +1520,9 @@ void World::SetInitialWorldSettings()
// Time server startup
uint32 uStartTime = WorldTimer::getMSTime();

// Initialize module sql updates
LoadModSQLUpdates();

// Initialize module config settings
LoadModuleConfig();

Expand Down
2 changes: 2 additions & 0 deletions src/game/World.h
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,7 @@ class World
std::string GetModuleStringConfig(std::string config, std::string value);
int32 GetModuleIntConfig(std::string conf, uint32 value);

void LoadModSQLUpdates();
void SendWorldText(int32 string_id, ...);
void SendBroadcastTextToWorld(uint32 textId);

Expand Down Expand Up @@ -1062,6 +1063,7 @@ class World
};

std::unordered_map<std::string, ModuleConfig> _moduleConfig;
std::string m_ModSQLUpdatesPath;
};

extern uint32 realmID;
Expand Down
2 changes: 1 addition & 1 deletion src/mangosd/mangosd.conf.dist.in
Original file line number Diff line number Diff line change
Expand Up @@ -2961,4 +2961,4 @@ DynamicRespawn.PlayersMaxLevelDiff = 0
###################################################################################################################
# Database Updater for modules
###################################################################################################################
DatabaseUpdater.ModPathToUpdates = "D:\World of Warcraft\vanilla\source\modules"
DatabaseUpdater.ModPathToUpdates = "C:\repo\VMangos-core\modules"
11 changes: 11 additions & 0 deletions src/shared/Database/Database.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ class SqlConnection
//public methods for making requests
virtual bool Execute(char const* sql) = 0;

virtual bool ExecuteFile(char const* file) = 0;

//escape string generation
virtual unsigned long escape_string(char* to, char const* from, unsigned long length) { strncpy(to,from,length); return length; }

Expand Down Expand Up @@ -151,6 +153,15 @@ class Database
return guard->Execute(sql);
}

inline bool ExecuteFile(char const* file)
{
if (!m_pAsyncConn)
return false;

SqlConnection::Lock guard(m_pAsyncConn);
return guard->ExecuteFile(file);
}

bool DirectPExecute(char const* format,...) ATTR_PRINTF(2,3);

// Async queries and query holders, implemented in DatabaseImpl.h
Expand Down
94 changes: 94 additions & 0 deletions src/shared/Database/DatabaseMysql.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,100 @@ QueryNamedResult* MySQLConnection::QueryNamed(char const* sql)
return new QueryNamedResult(queryResult,names);
}


bool MySQLConnection::ExecuteFile(char const* file)
{
if (!mMysql)
return false;

if (mysql_set_server_option(mMysql, MYSQL_OPTION_MULTI_STATEMENTS_ON))
{
sLog.Out(LOG_BASIC, LOG_LVL_ERROR, "Cannot turn multi-statements on: %s", mysql_error(mMysql));
return false;
}

mysql_autocommit(mMysql, 0);
if (mysql_real_query(mMysql, "START TRANSACTION", sizeof("START TRANSACTION") - 1))
{
sLog.Out(LOG_BASIC, LOG_LVL_ERROR, "Couldn't start transaction for db update file: %s", file);
return false;
}

bool in_transaction = true;
bool success = false;

if (FILE* fp = ACE_OS::fopen(file, "rb"))
{
#if PLATFORM == PLATFORM_UNIX
flock(fileno(fp), LOCK_SH);
#endif
//------

struct stat info;
fstat(fileno(fp), &info);

// if less than 1MB allocate on stack, else on heap
char* contents = (info.st_size > 1024 * 1024) ? new char[info.st_size] : (char*)alloca(info.st_size);

if (ACE_OS::fread(contents, info.st_size, 1, fp) == 1)
{
if (mysql_real_query(mMysql, contents, info.st_size))
{
sLog.Out(LOG_BASIC, LOG_LVL_ERROR, "Cannot execute file %s, size: %lu: %s", file, info.st_size, mysql_error(mMysql));
}
else
{
do
{
if (mysql_field_count(mMysql))
if (MYSQL_RES* result = mysql_use_result(mMysql))
mysql_free_result(result);
} while (0 == mysql_next_result(mMysql));

// check whether the last mysql_next_result ended with an error
if (*mysql_error(mMysql))
{
success = false;
sLog.Out(LOG_BASIC, LOG_LVL_ERROR, "Cannot execute file %s, size: %lu: %s", file, info.st_size, mysql_error(mMysql));
if (mysql_rollback(mMysql))
sLog.Out(LOG_BASIC, LOG_LVL_ERROR, "ExecuteFile(): Rollback ended with an error!");
else
in_transaction = false;
}
else
{
if (mysql_commit(mMysql))
sLog.Out(LOG_BASIC, LOG_LVL_ERROR, "mysql_commit() failed. Update %s will not be applied!", file);
else
in_transaction = false;
success = true;
}
}
}
else
{
sLog.Out(LOG_BASIC, LOG_LVL_ERROR, "Couldn't read file %s, size: %lu", file, info.st_size);
return false;
}

// if allocated on heap, free memory
if (info.st_size > 1024 * 1024)
delete[] contents;

//------
#if PLATFORM == PLATFORM_UNIX
flock(fileno(fp), LOCK_UN);
#endif
ACE_OS::fclose(fp);
}

mysql_set_server_option(mMysql, MYSQL_OPTION_MULTI_STATEMENTS_OFF);
mysql_autocommit(mMysql, 1);
if (in_transaction)
mysql_rollback(mMysql);
return success;
}

bool MySQLConnection::Execute(char const* sql)
{
if (!mMysql)
Expand Down
2 changes: 2 additions & 0 deletions src/shared/Database/DatabaseMysql.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ class MySQLConnection : public SqlConnection
QueryNamedResult* QueryNamed(char const* sql) override;
bool Execute(char const* sql) override;

bool ExecuteFile(char const* file);

unsigned long escape_string(char* to, char const* from, unsigned long length) override;

bool BeginTransaction() override;
Expand Down

0 comments on commit 3556aae

Please sign in to comment.