Skip to content

Commit 61c5224

Browse files
committed
Added dbLoadSchema
1 parent ddb54b9 commit 61c5224

File tree

5 files changed

+127
-12
lines changed

5 files changed

+127
-12
lines changed

addons/config.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ accounts:
99
database: db
1010
port: 3306 #optional
1111

12+
schemas:
13+
test: schema.sql
14+
1215
statements:
1316
#getMissionName: SELECT intel_missionen.`name` FROM intel_missionen WHERE intel_missionen.id > ?
1417
getMissionName: SELECT 1

src/config.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ void Config::reloadConfig() {
5252
auto port = value["port"].as<int>(3306);
5353

5454
auto acc = mariadb::account::create(ip, user, password, database, port);
55+
acc->set_connect_option(MYSQL_SET_CHARSET_NAME, r_string("utf8mb4"sv));
56+
//acc->set_connect_option(MARIADB_OPT_MULTI_STATEMENTS, true);
57+
//acc->set_connect_option(MARIADB_OPT_MULTI_RESULTS, true);
58+
//acc->set_auto_commit(false);
5559

5660
accounts[accountName] = acc;
5761
}
@@ -66,6 +70,13 @@ void Config::reloadConfig() {
6670
dynamicQueriesEnabled = config["global"]["enableDynamicQueries"].as<bool>(true);
6771

6872

73+
if (config["schemas"].IsMap())
74+
for (auto& it : config["schemas"]) {
75+
r_string schemaName = it.first.as<r_string>();
76+
auto value = it.second;
77+
78+
schemas[schemaName] = std::filesystem::path("@InterceptDB"sv) / value.as<std::string>();
79+
}
6980

7081

7182
}
@@ -94,7 +105,7 @@ void Config::initCommands() {
94105

95106
handle_cmd_reloadConfig = intercept::client::host::register_sqf_command("dbReloadConfig", "TODO", cmd_reloadConfig, game_data_type::STRING);
96107
handle_cmd_version = intercept::client::host::register_sqf_command("dbVersion", "TODO", [](game_state&) -> game_value {
97-
return "1.0";
108+
return "1.1";
98109
}, game_data_type::STRING);
99110

100111

src/config.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#pragma once
22
#include <intercept.hpp>
33
#include "../intercept/src/host/common/singleton.hpp"
4-
#include <vector>
4+
#include <filesystem>
55
#include "mariadb++/account.hpp"
66
#include "../intercept/src/client/headers/shared/containers.hpp"
77

@@ -22,6 +22,12 @@ class Config : public intercept::singleton<Config> {
2222
return found->second;
2323
}
2424

25+
std::optional<std::filesystem::path> getSchema(r_string name) {
26+
auto found = schemas.find(name);
27+
if (found == schemas.end()) return {};
28+
return found->second;
29+
}
30+
2531
bool areDynamicQueriesEnabled() const {
2632
return dynamicQueriesEnabled;
2733
}
@@ -33,6 +39,7 @@ class Config : public intercept::singleton<Config> {
3339
private:
3440
std::map<intercept::types::r_string, mariadb::account_ref> accounts;
3541
std::map<intercept::types::r_string, intercept::types::r_string> statements;
42+
std::map<intercept::types::r_string, std::filesystem::path> schemas;
3643
bool dynamicQueriesEnabled = true;
3744

3845

src/connection.cpp

Lines changed: 102 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <winsock2.h>
77
#include "threading.h"
88
#include "ittnotify.h"
9+
#include <fstream>
910

1011
__itt_domain* domainConnection = __itt_domain_create("connection");
1112

@@ -106,7 +107,8 @@ class callstack_item_WaitForQueryResult : public vm_context::callstack_item {
106107

107108
public:
108109

109-
callstack_item_WaitForQueryResult(ref<GameDataDBAsyncResult> inp) : res(inp) {}
110+
callstack_item_WaitForQueryResult(ref<GameDataDBAsyncResult> inp, bool scheduled = true) : res(inp), scheduled(scheduled){
111+
}
110112

111113
const char* getName() const override { return "stuff"; };
112114
int varCount() const override { return 0; };
@@ -121,12 +123,21 @@ class callstack_item_WaitForQueryResult : public vm_context::callstack_item {
121123

122124
game_instruction* next(int& d1, const game_state* s) override {
123125
if (res->data->fut.wait_for(std::chrono::nanoseconds(0)) == std::future_status::ready) {
124-
125126
//push result onto stack.
126127
auto gd_res = new GameDataDBResult();
127128
gd_res->res = res->data->res;
128129
s->get_vm_context()->scriptStack[_stackEndAtStart] = game_value(gd_res);
129130
d1 = 2; //done
131+
//#TODO fix this. Cannot currently because task wants invoker lock, which it won't get while we freeze the game here
132+
//} else if (!scheduled) {
133+
//
134+
// res->data->fut.wait();
135+
//
136+
// //push result onto stack.
137+
// auto gd_res = new GameDataDBResult();
138+
// gd_res->res = res->data->res;
139+
// s->get_vm_context()->scriptStack[_stackEndAtStart] = game_value(gd_res);
140+
// d1 = 2; //done
130141
} else {
131142
d1 = 3; //wait
132143
}
@@ -140,15 +151,14 @@ class callstack_item_WaitForQueryResult : public vm_context::callstack_item {
140151
};
141152

142153
ref<GameDataDBAsyncResult> res;
143-
144-
154+
bool scheduled;
145155
};
146156

147157
game_value Connection::cmd_execute(game_state& gs, game_value_parameter con, game_value_parameter qu) {
148158
auto session = con.get_as<GameDataDBConnection>()->session;
149159
auto query = qu.get_as<GameDataDBQuery>();
150160

151-
if (!gs.get_vm_context()->is_scheduled()) {
161+
if (!gs.get_vm_context()->is_scheduled()) { //#TODO just keep using the callstack item but tell it to wait
152162

153163
try {
154164
auto statement = session->create_statement(query->getQueryString());
@@ -199,7 +209,7 @@ game_value Connection::cmd_execute(game_state& gs, game_value_parameter con, gam
199209

200210

201211
gd_res->data->fut = Threading::get().pushTask(session,
202-
[stmt = query->getQueryString(), boundV = query->boundValues, result = gd_res->data](mariadb::connection_ref con) -> bool {
212+
[stmt = query->getQueryString(), boundV = query->boundValues, result = gd_res->data, &gs](mariadb::connection_ref con) -> bool {
203213
try {
204214
auto statement = con->create_statement(stmt);
205215
uint32_t idx = 0;
@@ -252,7 +262,7 @@ game_value Connection::cmd_executeAsync(game_state& gs, game_value_parameter con
252262
auto gd_res = new GameDataDBAsyncResult();
253263
gd_res->data = std::make_shared<GameDataDBAsyncResult::dataT>();
254264
gd_res->data->fut = Threading::get().pushTask(session,
255-
[stmt = query->getQueryString(), boundV = query->boundValues, result = gd_res->data](mariadb::connection_ref con) -> bool
265+
[stmt = query->getQueryString(), boundV = query->boundValues, result = gd_res->data, &gs](mariadb::connection_ref con) -> bool
256266
{
257267
__itt_task_begin(domainConnection, __itt_null, __itt_null, connection_cmd_executeAsync_task);
258268
try {
@@ -321,6 +331,90 @@ game_value Connection::cmd_addErrorHandler(game_state&, game_value_parameter con
321331
return {};
322332
}
323333

334+
game_value Connection::cmd_loadSchema(game_state& gs, game_value_parameter con, game_value_parameter name) {
335+
336+
337+
auto session = con.get_as<GameDataDBConnection>()->session;
338+
r_string schemaName = name;
339+
auto schemaPath = Config::get().getSchema(schemaName);
340+
341+
if (!schemaPath) {
342+
gs.set_script_error(game_state::game_evaluator::evaluator_error_type::foreign,
343+
"Schema name not found in config"sv);
344+
return {};
345+
}
346+
347+
if (!std::filesystem::exists(*schemaPath)) {
348+
gs.set_script_error(game_state::game_evaluator::evaluator_error_type::foreign,
349+
r_string("Schema file")+schemaPath->string()+" does not exist"sv);
350+
return {};
351+
}
352+
353+
std::ifstream t(*schemaPath);
354+
std::string str;
355+
356+
t.seekg(0, std::ios::end);
357+
str.resize(t.tellg());
358+
t.seekg(0, std::ios::beg);
359+
t.read(str.data(), str.length());
360+
t.close();
361+
362+
363+
if (!gs.get_vm_context()->is_scheduled()) {
364+
try {
365+
return session->execute(static_cast<r_string>(str));;
366+
}
367+
catch (mariadb::exception::connection & x) {
368+
invoker_lock l;
369+
auto exText = r_string("Intercept-DB exception ") + x.what() + "\nwhile executing loadSchema";
370+
gs.set_script_error(game_state::game_evaluator::evaluator_error_type::foreign,
371+
exText);
372+
sqf::diag_log(exText);
373+
return {};
374+
}
375+
}
376+
377+
//Set up callstack item to suspend while waiting
378+
379+
auto& cs = gs.get_vm_context()->callstack;
380+
381+
auto gd_res = new GameDataDBAsyncResult();
382+
gd_res->data = std::make_shared<GameDataDBAsyncResult::dataT>();
383+
384+
gd_res->data->fut = Threading::get().pushTask(session,
385+
[str, result = gd_res->data, &gs](mariadb::connection_ref con) -> bool {
386+
try {
387+
con->execute(static_cast<r_string>(str));
388+
return true;
389+
}
390+
catch (mariadb::exception::connection& x) {
391+
invoker_lock l;
392+
//if (con->account()->hasErrorHandler()) {
393+
// for (auto& it : con->account()->getErrorHandlers()) {
394+
//
395+
// auto res = sqf::call(it, { static_cast<r_string>(x.what()), static_cast<size_t>(x.error_id()), stmt });
396+
//
397+
// if (res.type_enum() == game_data_type::BOOL && static_cast<bool>(res)) return false; //If returned true then error was handled.
398+
// }
399+
//}
400+
auto exText = r_string("Intercept-DB exception ") + x.what() + "\nwhile executing loadSchema";
401+
gs.set_script_error(game_state::game_evaluator::evaluator_error_type::foreign,
402+
exText);
403+
sqf::diag_log(exText);
404+
405+
return false;
406+
}
407+
});
408+
409+
auto newItem = new callstack_item_WaitForQueryResult(gd_res);
410+
newItem->_parent = cs.back();
411+
newItem->_stackEndAtStart = gs.get_vm_context()->scriptStack.size() - 2;
412+
newItem->_stackEnd = newItem->_stackEndAtStart + 1;
413+
newItem->_varSpace.parent = &cs.back()->_varSpace;
414+
cs.emplace_back(newItem);
415+
return {};
416+
}
417+
324418
void Connection::initCommands() {
325419

326420
auto dbType = host::register_sqf_type("DBCON"sv, "databaseConnection"sv, "TODO"sv, "databaseConnection"sv, createGameDataDBConnection);
@@ -335,7 +429,5 @@ void Connection::initCommands() {
335429
handle_cmd_ping = host::register_sqf_command("dbPing", "TODO", Connection::cmd_ping, game_data_type::BOOL, GameDataDBConnection_typeE);
336430
handle_cmd_isConnected = host::register_sqf_command("dbIsConnected", "TODO", Connection::cmd_isConnected, game_data_type::BOOL, GameDataDBConnection_typeE);
337431
handle_cmd_addErrorHandler = host::register_sqf_command("dbAddErrorHandler", "TODO", Connection::cmd_addErrorHandler, game_data_type::NOTHING, GameDataDBConnection_typeE, game_data_type::CODE);
338-
339-
340-
432+
handle_cmd_loadSchema = host::register_sqf_command("dbLoadSchema", "TODO", Connection::cmd_loadSchema, game_data_type::NOTHING, GameDataDBConnection_typeE, game_data_type::STRING);
341433
}

src/connection.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class Connection {
1515
static game_value cmd_ping(game_state&, game_value_parameter con);
1616
static game_value cmd_isConnected(game_state&, game_value_parameter con);
1717
static game_value cmd_addErrorHandler(game_state&, game_value_parameter con, game_value_parameter handler);
18+
static game_value cmd_loadSchema(game_state&, game_value_parameter con, game_value_parameter name);
1819

1920

2021

@@ -30,4 +31,5 @@ class Connection {
3031
static inline types::registered_sqf_function handle_cmd_ping;
3132
static inline types::registered_sqf_function handle_cmd_isConnected;
3233
static inline types::registered_sqf_function handle_cmd_addErrorHandler;
34+
static inline types::registered_sqf_function handle_cmd_loadSchema;
3335
};

0 commit comments

Comments
 (0)