update and fix MySQL UID feature.

Also added a port setting, a test connection button and a connection status label
This commit is contained in:
T1ti
2023-03-16 07:40:53 +01:00
parent 77de6a9c10
commit 905d97acf5
8 changed files with 224 additions and 29 deletions

View File

@@ -13,25 +13,119 @@ namespace
std::unique_ptr<sql::Connection> connect()
{
QSettings settings;
std::unique_ptr<sql::Connection> Con
(get_driver_instance()->connect
( settings.value("project/mysql/server").toString().toStdString()
, settings.value("project/mysql/user").toString().toStdString()
, settings.value("project/mysql/pwd").toString().toStdString()
)
);
Con->setSchema(settings.value("project/mysql/db").toString().toStdString());
return Con;
// if using release SQL binaries in debug mode it will crash https://bugs.mysql.com/bug.php?id=91238 unless using sql strings
// tcp://127.0.0.1:3306
sql::SQLString hostname = "tcp://" + settings.value("project/mysql/server").toString().toStdString() + ":" + settings.value("project/mysql/port", "3306").toString().toStdString();
sql::SQLString userName = settings.value("project/mysql/user").toString().toStdString();
sql::SQLString password = settings.value("project/mysql/pwd").toString().toStdString();
sql::SQLString schema = settings.value("project/mysql/db").toString().toStdString();
try
{
std::unique_ptr<sql::Connection> Con(get_driver_instance()->connect(hostname, userName, password));
// crete database if it deosn't exist
std::string createdb_statement = "CREATE DATABASE IF NOT EXISTS " + schema;
std::unique_ptr<sql::PreparedStatement> dbpstmt(Con->prepareStatement(createdb_statement));
std::unique_ptr<sql::ResultSet> res(dbpstmt->executeQuery());
Con->setSchema(schema);
// create table if it doesn't exist, querries from src/sql
std::unique_ptr<sql::PreparedStatement> tablepstmt(Con->prepareStatement("CREATE TABLE IF NOT EXISTS `UIDs` ("
"`_map_id` int(11) NOT NULL,"
"`UID` int(11) NOT NULL,"
"PRIMARY KEY(`_map_id`)"
") ENGINE = InnoDB DEFAULT CHARSET = latin1;"));
std::unique_ptr<sql::ResultSet> tableres(tablepstmt->executeQuery());
// std::unique_ptr<sql::PreparedStatement> alterpstmt(Con->prepareStatement("ALTER TABLE `UIDs` ADD PRIMARY KEY(`_map_id`);"));
// std::unique_ptr<sql::ResultSet> altres(alterpstmt->executeQuery());
return Con;
}
catch (sql::SQLException& e)
{
/*
// prompt would pop on every model spawn
QMessageBox prompt;
prompt.setIcon(QMessageBox::Warning);
prompt.setText("Failed to load MySQL database, check your settings.");
prompt.setWindowTitle("Noggit Database Error");
std::stringstream promptText;
promptText << "\n# ERR: " << e.what();
promptText << "\n (MySQL error code: " << e.getErrorCode();
prompt.setInformativeText(promptText.str().c_str());
prompt.exec();
*/
std::stringstream test3;
// test1 << "\n# ERR: SQLException in " << __FILE__;
// test2 << "\n(" << __FUNCTION__ << ") on line " << __LINE__ << endl;
// /* what() (derived from std::runtime_error) fetches error message */
test3 << "\n# ERR: " << e.what();
// test4 << "\n (MySQL error code: " << e.getErrorCode();
// test5 << "\n, SQLState: " << e.getSQLState() << " )" << endl;
return nullptr;
}
}
}
namespace mysql
{
bool testConnection(bool report_only_err)
{
QSettings settings;
// if using release SQL binaries in debug mode it will crash https://bugs.mysql.com/bug.php?id=91238 unless using sql strings
// tcp://127.0.0.1:3306
sql::SQLString hostname = "tcp://" + settings.value("project/mysql/server").toString().toStdString() + ":" + settings.value("project/mysql/port", "3306").toString().toStdString();
sql::SQLString userName = settings.value("project/mysql/user").toString().toStdString();
sql::SQLString password = settings.value("project/mysql/pwd").toString().toStdString();
sql::SQLString schema = settings.value("project/mysql/db").toString().toStdString();
QMessageBox prompt;
prompt.setWindowFlag(Qt::WindowStaysOnTopHint);
try
{
std::unique_ptr<sql::Connection> Con(get_driver_instance()->connect(hostname, userName, password));
prompt.setIcon(QMessageBox::Information);
prompt.setText("Succesfully connected to MySQL database.");
prompt.setWindowTitle("Success");
if (!report_only_err)
prompt.exec();
return true;
}
catch (sql::SQLException& e)
{
prompt.setIcon(QMessageBox::Warning);
prompt.setText("Failed to load MySQL database, check your settings.");
prompt.setWindowTitle("Noggit Database Error");
std::stringstream promptText;
promptText << "\n# ERR: " << e.what();
promptText << "\n (MySQL error code: " << e.getErrorCode() + ")";
prompt.setInformativeText(promptText.str().c_str());
prompt.exec();
return false;
}
}
bool hasMaxUIDStoredDB(std::size_t mapID)
{
auto Con(connect());
std::unique_ptr<sql::PreparedStatement> pstmt(Con->prepareStatement("SELECT * FROM UIDs WHERE MapId=(?)"));
if (Con == nullptr)
return false;
std::unique_ptr<sql::PreparedStatement> pstmt(Con->prepareStatement("SELECT * FROM `UIDs` WHERE `_map_id`=(?)"));
pstmt->setInt(1, mapID);
std::unique_ptr<sql::ResultSet> res(pstmt->executeQuery());
return res->rowsCount();
@@ -40,7 +134,9 @@ namespace mysql
std::uint32_t getGUIDFromDB(std::size_t mapID)
{
auto Con(connect());
std::unique_ptr<sql::PreparedStatement> pstmt(Con->prepareStatement("SELECT UID FROM UIDs WHERE MapId=(?)"));
if (Con == nullptr)
return 0;
std::unique_ptr<sql::PreparedStatement> pstmt(Con->prepareStatement("SELECT `UID` FROM `UIDs` WHERE `_map_id`=(?)"));
pstmt->setInt(1, mapID);
std::unique_ptr<sql::ResultSet> res(pstmt->executeQuery());
@@ -60,7 +156,9 @@ namespace mysql
void insertUIDinDB(std::size_t mapID, std::uint32_t NewUID)
{
auto Con(connect());
std::unique_ptr<sql::PreparedStatement> pstmt(Con->prepareStatement("INSERT INTO UIDs SET MapId=(?), UID=(?)"));
if (Con == nullptr)
return;
std::unique_ptr<sql::PreparedStatement> pstmt(Con->prepareStatement("INSERT INTO `UIDs` SET `_map_id`=(?), `UID`=(?)"));
pstmt->setInt(1, mapID);
pstmt->setInt(2, NewUID);
pstmt->executeUpdate();
@@ -69,7 +167,9 @@ namespace mysql
void updateUIDinDB (std::size_t mapID, std::uint32_t NewUID)
{
auto Con(connect());
std::unique_ptr<sql::PreparedStatement> pstmt(Con->prepareStatement("UPDATE UIDs SET UID=(?) WHERE MapId=(?)"));
if (Con == nullptr)
return;
std::unique_ptr<sql::PreparedStatement> pstmt(Con->prepareStatement("UPDATE `UIDs` SET `UID`=(?) WHERE `_map_id`=(?)"));
pstmt->setInt(1, NewUID);
pstmt->setInt(2, mapID);
pstmt->executeUpdate();

View File

@@ -7,6 +7,7 @@
namespace mysql
{
bool testConnection(bool report_only_err = false);
bool hasMaxUIDStoredDB(std::size_t mapID);
std::uint32_t getGUIDFromDB(std::size_t mapID);
void insertUIDinDB(std::size_t mapID, std::uint32_t NewUID);

View File

@@ -50,6 +50,12 @@
#include <variant>
#include <noggit/Selection.h>
#ifdef USE_MYSQL_UID_STORAGE
#include <mysql/mysql.h>
#include <QtCore/QSettings>
#endif
#include <noggit/scripting/scripting_tool.hpp>
#include <noggit/scripting/script_settings.hpp>
@@ -2597,6 +2603,19 @@ void MapView::createGUI()
connect(_main_window, &Noggit::Ui::Windows::NoggitWindow::exitPromptOpened, this, &MapView::on_exit_prompt);
set_editing_mode (editing_mode::ground);
// do we need to do this every tick ?
#ifdef USE_MYSQL_UID_STORAGE
if (_settings->value("project/mysql/enabled").toBool())
{
if (mysql::hasMaxUIDStoredDB(_world->getMapID()))
{
_status_database->setText("MySQL UID sync enabled: "
+ _settings->value("project/mysql/server").toString() + ":"
+ _settings->value("project/mysql/port").toString());
}
}
#endif
}
void MapView::on_exit_prompt()
@@ -2638,6 +2657,7 @@ MapView::MapView( math::degrees camera_yaw0
, _status_time (new QLabel (this))
, _status_fps (new QLabel (this))
, _status_culling (new QLabel (this))
, _status_database(new QLabel(this))
, _texBrush{new OpenGL::texture{}}
, _transform_gizmo(Noggit::Ui::Tools::ViewportGizmo::GizmoContext::MAP_VIEW)
, _tablet_manager(Noggit::TabletManager::instance()),
@@ -2695,6 +2715,12 @@ MapView::MapView( math::degrees camera_yaw0
, _main_window
, [=] { _main_window->statusBar()->removeWidget (_status_culling); }
);
_main_window->statusBar()->addWidget(_status_database);
connect(this
, &QObject::destroyed
, _main_window
, [=] { _main_window->statusBar()->removeWidget(_status_database); }
);
moving = strafing = updown = lookat = turn = 0.0f;
@@ -3974,6 +4000,18 @@ void MapView::tick (float dt)
updateDetailInfos();
/*
// do we need to do this every tick ?
#ifdef USE_MYSQL_UID_STORAGE
if (_settings->value("project/mysql/enabled").toBool())
{
_status_database->setText("MySQL UID sync enabled: "
+ _settings->value("project/mysql/server").toString() + ":"
+ _settings->value("project/mysql/port").toString());
}
#endif
*/
_status_area->setText
(QString::fromStdString (gAreaDB.getAreaName (_world->getAreaID (_camera.position))));

View File

@@ -367,6 +367,7 @@ private:
QLabel* _status_time;
QLabel* _status_fps;
QLabel* _status_culling;
QLabel* _status_database;
Noggit::BoolToggleProperty _locked_cursor_mode = {false};
Noggit::BoolToggleProperty _move_model_to_cursor_position = {true};

View File

@@ -681,7 +681,7 @@ uint32_t MapIndex::newGUID()
#ifdef USE_MYSQL_UID_STORAGE
QSettings settings;
if (settings->value ("project/mysql/enabled", false).toBool())
if (settings.value ("project/mysql/enabled", false).toBool())
{
mysql::updateUIDinDB(_map_id, highestGUID + 1); // update the highest uid in db, note that if the user don't save these uid won't be used (not really a problem tho)
}
@@ -1025,7 +1025,7 @@ void MapIndex::saveMaxUID()
#ifdef USE_MYSQL_UID_STORAGE
QSettings settings;
if (settings->value ("project/mysql/enabled", false).toBool())
if (settings.value ("project/mysql/enabled", false).toBool())
{
if (mysql::hasMaxUIDStoredDB(_map_id))
{
@@ -1047,9 +1047,9 @@ void MapIndex::loadMaxUID()
#ifdef USE_MYSQL_UID_STORAGE
QSettings settings;
if (settings->value ("project/mysql/enabled", false).toBool())
if (settings.value ("project/mysql/enabled", false).toBool())
{
highestGUID = std::max(mysql::getGUIDFromDB(map_id), highestGUID);
highestGUID = std::max(mysql::getGUIDFromDB(_map_id), highestGUID);
// save to make sure the db and disk uid are synced
saveMaxUID();
}

View File

@@ -126,12 +126,14 @@ namespace Noggit::Ui::Windows
#ifdef USE_MYSQL_UID_STORAGE
bool use_mysql = settings.value("project/mysql/enabled", false).toBool();
if ((use_myqsl && mysql::hasMaxUIDStoredDB(_world->getMapID()))
mysql::testConnection(true);
if ((use_mysql && mysql::hasMaxUIDStoredDB(_world->getMapID()))
|| uid_storage::hasMaxUIDStored(_world->getMapID())
)
{
_world->mapIndex.loadMaxUID();
enterMapAt(pos, camera_pitch, camera_yaw, from_bookmark);
enterMapAt(pos, camera_pitch, camera_yaw, uid_fix_mode::none, from_bookmark);
}
#else
if (uid_storage::hasMaxUIDStored(_world->getMapID()))

View File

@@ -19,6 +19,10 @@
#include <QDir>
#include <QApplication>
#ifdef USE_MYSQL_UID_STORAGE
#include <mysql/mysql.h>
#endif
#include <ui_SettingsPanel.h>
#include <ui_TitleBar.h>
@@ -85,6 +89,7 @@ namespace Noggit
#ifdef USE_MYSQL_UID_STORAGE
ui->MySQL_box->setEnabled(true);
ui->MySQL_box->setCheckable(true);
ui->mysql_warning->setVisible(false);
#endif
@@ -157,6 +162,13 @@ namespace Noggit
}
);
connect(ui->mysql_connect_test, &QPushButton::clicked, [this]
{
save_changes();
mysql::testConnection();
}
);
// load the values in the fields
discard_changes();
}
@@ -200,11 +212,31 @@ namespace Noggit
#ifdef USE_MYSQL_UID_STORAGE
ui->_mysql_box->setChecked (_settings->value ("project/mysql/enabled").toBool());
ui->_mysql_server_field->setText (_settings->value ("project/mysql/server").toString());
ui->_mysql_user_field->setText(_settings->value ("project/mysql/user").toString());
ui->_mysql_pwd_field->setText (_settings->value ("project/mysql/pwd").toString());
ui->_mysql_db_field->setText (_settings->value ("project/mysql/db").toString());
ui->MySQL_box->setChecked (_settings->value ("project/mysql/enabled").toBool());
auto server_str = _settings->value("project/mysql/server", "127.0.0.1").toString();
auto user_str = _settings->value("project/mysql/user", "127.0.0.1").toString();
auto pwd_str = _settings->value("project/mysql/pwd", "127.0.0.1").toString();
auto db_str = _settings->value("project/mysql/db", "127.0.0.1").toString();
auto port_int = _settings->value("project/mysql/port", "127.0.0.1").toInt();
// set some default
if (server_str.isEmpty())
server_str = "127.0.0.1";
if (user_str.isEmpty())
user_str = "root";
if (pwd_str.isEmpty())
pwd_str = "root";
if (db_str.isEmpty())
db_str = "noggit";
if (!port_int)
port_int = 3306;
ui->_mysql_server_field->setText (server_str);
ui->_mysql_user_field->setText(user_str);
ui->_mysql_pwd_field->setText (pwd_str);
ui->_mysql_db_field->setText (db_str);
ui->_mysql_port_field->setValue (port_int);
#endif
int wireframe_type = _settings->value("wireframe/type", 0).toInt();
@@ -245,11 +277,12 @@ namespace Noggit
_settings->setValue("nativeMenubar", ui->_nativeMenubar->isChecked());
#ifdef USE_MYSQL_UID_STORAGE
_settings->setValue ("project/mysql/enabled", _mysql_box->isChecked());
_settings->setValue ("project/mysql/server", _mysql_server_field->text());
_settings->setValue ("project/mysql/user", _mysql_user_field->text());
_settings->setValue ("project/mysql/pwd", _mysql_pwd_field->text());
_settings->setValue ("project/mysql/db", _mysql_db_field->text());
_settings->setValue ("project/mysql/enabled", ui->MySQL_box->isChecked());
_settings->setValue ("project/mysql/server", ui->_mysql_server_field->text());
_settings->setValue ("project/mysql/user", ui->_mysql_user_field->text());
_settings->setValue ("project/mysql/pwd", ui->_mysql_pwd_field->text());
_settings->setValue ("project/mysql/db", ui->_mysql_db_field->text());
_settings->setValue ("project/mysql/port", ui->_mysql_port_field->text());
#endif
_settings->setValue("wireframe/type", ui->radio_wire_cursor->isChecked());

View File

@@ -1295,6 +1295,26 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_66">
<item>
<widget class="QLabel" name="label_48">
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Port</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="_mysql_port_field"/>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="mysql_warning">
<property name="minimumSize">