/* * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #include "DB2DatabaseLoader.h" #include "Common.h" #include "DatabaseEnv.h" #include "DB2Meta.h" #include "Errors.h" #include "Log.h" #include static char const* nullStr = ""; char* DB2DatabaseLoader::Load(bool custom, uint32& records, char**& indexTable, std::vector& stringPool, uint32& minId) { // Even though this query is executed only once, prepared statement is used to send data from mysql server in binary format HotfixDatabasePreparedStatement* stmt = HotfixDatabase.GetPreparedStatement(_loadInfo->Statement); stmt->setBool(0, !custom); PreparedQueryResult result = HotfixDatabase.Query(stmt); if (!result) return nullptr; if (_loadInfo->Meta->GetDbFieldCount() != result->GetFieldCount()) return nullptr; // get struct size and index pos uint32 indexField = _loadInfo->Meta->GetDbIndexField(); uint32 recordSize = _loadInfo->Meta->GetRecordSize(); // we store flat holders pool as single memory block std::size_t stringFields = _loadInfo->GetStringFieldCount(false); // Resize index table uint32 indexTableSize = records; if (PreparedQueryResult maxIdResult = HotfixDatabase.Query(HotfixDatabase.GetPreparedStatement(HotfixDatabaseStatements(_loadInfo->Statement + HOTFIX_MAX_ID_STMT_OFFSET)))) if (uint32((*maxIdResult)[0].GetUInt64()) > records) indexTableSize = uint32((*maxIdResult)[0].GetUInt64()); if (indexTableSize > records) { char** tmpIdxTable = new char*[indexTableSize]; memset(tmpIdxTable, 0, indexTableSize * sizeof(char*)); memcpy(tmpIdxTable, indexTable, records * sizeof(char*)); delete[] indexTable; indexTable = tmpIdxTable; } char* tempDataTable = new char[result->GetRowCount() * recordSize]; memset(tempDataTable, 0, result->GetRowCount() * recordSize); uint32* newIndexes = new uint32[result->GetRowCount()]; if (stringFields) stringPool.reserve(std::max(stringPool.capacity(), stringPool.size() + stringFields * result->GetRowCount() + 1)); std::size_t newRecords = 0; do { Field* fields = result->Fetch(); uint32 offset = 0; uint32 indexValue = fields[indexField].GetUInt32(); bool isNew = false; // Attempt to overwrite existing data char* dataValue = indexTable[indexValue]; if (!dataValue) { newIndexes[newRecords] = indexValue; dataValue = &tempDataTable[newRecords++ * recordSize]; isNew = true; } uint32 f = 0; if (!_loadInfo->Meta->HasIndexFieldInData()) { *((uint32*)(&dataValue[offset])) = indexValue; offset += 4; ++f; } for (uint32 x = 0; x < _loadInfo->Meta->FieldCount; ++x) { for (uint32 z = 0; z < _loadInfo->Meta->Fields[x].ArraySize; ++z) { switch (_loadInfo->Fields[f].Type) { case FT_FLOAT: *((float*)(&dataValue[offset])) = fields[f].GetFloat(); offset += 4; break; case FT_INT: *((int32*)(&dataValue[offset])) = fields[f].GetInt32(); offset += 4; break; case FT_BYTE: *((int8*)(&dataValue[offset])) = fields[f].GetInt8(); offset += 1; break; case FT_SHORT: *((int16*)(&dataValue[offset])) = fields[f].GetInt16(); offset += 2; break; case FT_LONG: *((int64*)(&dataValue[offset])) = fields[f].GetInt64(); offset += 8; break; case FT_STRING: { LocalizedString* slot = (LocalizedString*)(&dataValue[offset]); if (isNew) for (char const*& localeStr : slot->Str) localeStr = nullStr; // Value in database in main table field must be for enUS locale if (char* str = AddString(&slot->Str[LOCALE_enUS], fields[f].GetStringView())) stringPool.push_back(str); offset += sizeof(LocalizedString); break; } case FT_STRING_NOT_LOCALIZED: { char const** slot = (char const**)(&dataValue[offset]); // Value in database in main table field must be for enUS locale if (char* str = AddString(slot, fields[f].GetStringView())) stringPool.push_back(str); else *slot = nullStr; offset += sizeof(char*); break; } default: ABORT_MSG("Unknown format character '%c' found in " STRING_VIEW_FMT " meta for field %s", _loadInfo->Fields[f].Type, STRING_VIEW_FMT_ARG(_storageName), _loadInfo->Fields[f].Name); break; } ++f; } } ASSERT(offset == recordSize); } while (result->NextRow()); if (!newRecords) { delete[] tempDataTable; delete[] newIndexes; return nullptr; } // Compact new data table to only contain new records not previously loaded from file char* dataTable = new char[newRecords * recordSize]; memcpy(dataTable, tempDataTable, newRecords * recordSize); // insert new records to index table for (std::size_t i = 0; i < newRecords; ++i) { uint32 newId = newIndexes[i]; indexTable[newId] = &dataTable[i * recordSize]; minId = std::min(minId, newId); } delete[] tempDataTable; delete[] newIndexes; records = indexTableSize; return dataTable; } void DB2DatabaseLoader::LoadStrings(bool custom, LocaleConstant locale, uint32 records, char** indexTable, std::vector& stringPool) { HotfixDatabasePreparedStatement* stmt = HotfixDatabase.GetPreparedStatement(HotfixDatabaseStatements(_loadInfo->Statement + HOTFIX_LOCALE_STMT_OFFSET)); stmt->setBool(0, !custom); stmt->setString(1, std::string_view(localeNames[locale])); PreparedQueryResult result = HotfixDatabase.Query(stmt); if (!result) return; std::size_t stringFields = _loadInfo->GetStringFieldCount(true); if (result->GetFieldCount() != stringFields + 1 /*ID*/) return; uint32 fieldCount = _loadInfo->Meta->FieldCount; uint32 recordSize = _loadInfo->Meta->GetRecordSize(); stringPool.reserve(std::max(stringPool.capacity(), stringPool.size() + stringFields * result->GetRowCount() + 1)); do { Field* fields = result->Fetch(); uint32 offset = 0; uint32 stringFieldNumInRecord = 0; uint32 indexValue = fields[0].GetUInt32(); if (indexValue >= records) continue; // Attempt to overwrite existing data if (char* dataValue = indexTable[indexValue]) { uint32 fieldIndex = 0; if (!_loadInfo->Meta->HasIndexFieldInData()) { offset += 4; ++fieldIndex; } for (uint32 x = 0; x < fieldCount; ++x) { for (uint32 z = 0; z < _loadInfo->Meta->Fields[x].ArraySize; ++z) { switch (_loadInfo->Fields[fieldIndex].Type) { case FT_FLOAT: case FT_INT: offset += 4; break; case FT_BYTE: offset += 1; break; case FT_SHORT: offset += 2; break; case FT_LONG: offset += 8; break; case FT_STRING: { // fill only not filled entries LocalizedString* db2str = (LocalizedString*)(&dataValue[offset]); if (char* str = AddString(&db2str->Str[locale], fields[1 + stringFieldNumInRecord].GetStringView())) stringPool.push_back(str); ++stringFieldNumInRecord; offset += sizeof(LocalizedString); break; } case FT_STRING_NOT_LOCALIZED: offset += sizeof(char*); break; default: ABORT_MSG("Unknown format character '%c' found in " STRING_VIEW_FMT " meta for field %s", _loadInfo->Fields[fieldIndex].Type, STRING_VIEW_FMT_ARG(_storageName), _loadInfo->Fields[fieldIndex].Name); break; } ++fieldIndex; } } ASSERT(offset == recordSize); } else TC_LOG_ERROR("sql.sql", "Hotfix locale table for storage {} references row that does not exist {} locale {}!", _storageName, indexValue, localeNames[locale]); } while (result->NextRow()); } char* DB2DatabaseLoader::AddString(char const** holder, std::string_view value) { if (!value.empty()) { char* str = new char[value.length() + 1]; memcpy(str, value.data(), value.length()); str[value.length()] = '\0'; *holder = str; return str; } return nullptr; }