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
107108public:
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
147157game_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 () + " \n while 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 () + " \n while 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+
324418void 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}
0 commit comments