| 1 | /* | 
|---|
| 2 | * This Source Code Form is subject to the terms of the Mozilla Public | 
|---|
| 3 | * License, v. 2.0.  If a copy of the MPL was not distributed with this | 
|---|
| 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. | 
|---|
| 5 | * | 
|---|
| 6 | * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V. | 
|---|
| 7 | */ | 
|---|
| 8 |  | 
|---|
| 9 | /* | 
|---|
| 10 | * (author) M. Kersten | 
|---|
| 11 | * @+ Session Scenarios | 
|---|
| 12 | * In MonetDB multiple languages, optimizers, and execution engines can | 
|---|
| 13 | * be combined at run time to satisfy a wide user-community. | 
|---|
| 14 | * Such an assemblage of components is called a @emph{scenario} | 
|---|
| 15 | * and consists of a @emph{reader}, @emph{parser}, @emph{optimizer}, | 
|---|
| 16 | * @emph{tactic scheduler} and @emph{engine}. These hooks allow | 
|---|
| 17 | * for both linked-in and external components. | 
|---|
| 18 | * | 
|---|
| 19 | * The languages supported are SQL, the Monet Assembly Language (MAL), and profiler. | 
|---|
| 20 | * The default scenario handles MAL instructions, which is used | 
|---|
| 21 | * to illustrate the behavior of the scenario steps. | 
|---|
| 22 | * | 
|---|
| 23 | * The MAL reader component handles interaction with | 
|---|
| 24 | * a front-end to obtain a string for subsequent compilation and | 
|---|
| 25 | * execution. The reader uses the common stream package to read | 
|---|
| 26 | * data in large chunks, if possible. In interactive mode the lines | 
|---|
| 27 | * are processed one at a time. | 
|---|
| 28 | * | 
|---|
| 29 | * The MAL parser component turns the string into | 
|---|
| 30 | * an internal representation of the MAL program. | 
|---|
| 31 | * During this phase semantic checks are performed, such that | 
|---|
| 32 | * we end up with a type correct program. | 
|---|
| 33 | * | 
|---|
| 34 | * The code block is subsequently sent to an MAL optimizer. | 
|---|
| 35 | * In the default case the program is left untouched. For other languages, | 
|---|
| 36 | * the optimizer deploys language specific code transformations, | 
|---|
| 37 | * e.g., foreign-key optimizations in joins and remote query execution. | 
|---|
| 38 | * All optimization information is statically derived from the | 
|---|
| 39 | * code blocks and possible catalogues maintained for the query language | 
|---|
| 40 | * at hand. Optimizers leave advice and their findings in properties | 
|---|
| 41 | * in the symbol table, see @ref{Property Management}. | 
|---|
| 42 | * | 
|---|
| 43 | * Once the program has thus been refined, the | 
|---|
| 44 | * MAL scheduler prepares for execution using tactical optimizations. | 
|---|
| 45 | * For example, it may parallelize the code, generate an ad-hoc | 
|---|
| 46 | * user-defined function, or prepare for efficient replication management. | 
|---|
| 47 | * In the default case, the program is handed over to the MAL interpreter | 
|---|
| 48 | * without any further modification. | 
|---|
| 49 | * | 
|---|
| 50 | * The final stage is to choose an execution paradigm, | 
|---|
| 51 | * i.e. interpretative (default), compilation of an ad-hoc user | 
|---|
| 52 | * defined function, dataflow driven interpretation, | 
|---|
| 53 | * or vectorized pipe-line execution by a dedicated engine. | 
|---|
| 54 | * | 
|---|
| 55 | * A failure encountered in any of the steps terminates the scenario | 
|---|
| 56 | * cycle. It returns to the user for a new command. | 
|---|
| 57 | * | 
|---|
| 58 | * @+ Scenario management | 
|---|
| 59 | * Scenarios are captured in modules; they can be dynamically loaded | 
|---|
| 60 | * and remain active until the system is brought to a halt. | 
|---|
| 61 | * The first time a scenario @sc{xyz} is used, the system looks for a scenario | 
|---|
| 62 | * initialization routine @sc{xyzinitSystem()} and executes it. | 
|---|
| 63 | * It is typically used to prepare the server for language specific interactions. | 
|---|
| 64 | * Thereafter its components are set to those required by | 
|---|
| 65 | * the scenario and the client initialization takes place. | 
|---|
| 66 | * | 
|---|
| 67 | * When the last user interested in a particular scenario leaves the | 
|---|
| 68 | * scene, we activate its finalization routine calling @sc{xyzexitSystem()}. | 
|---|
| 69 | * It typically perform cleanup, backup and monitoring functions. | 
|---|
| 70 | * | 
|---|
| 71 | * A scenario is interpreted in a strictly linear fashion, | 
|---|
| 72 | * i.e. performing a symbolic optimization before scheduling decisions | 
|---|
| 73 | * are taken. | 
|---|
| 74 | * The routines associated with each state in | 
|---|
| 75 | * the scenario may patch the code so as to assure that subsequent | 
|---|
| 76 | * execution can use a different scenario, e.g., to handle dynamic | 
|---|
| 77 | * code fragments. | 
|---|
| 78 | * | 
|---|
| 79 | * The state of execution is maintained in the scenario record for | 
|---|
| 80 | * each individual client. Sharing this information between clients | 
|---|
| 81 | * should be dealt with in the implementation of the scenario managers. | 
|---|
| 82 | * Upon need, the client can postpone a session scenario by | 
|---|
| 83 | * pushing a new one(language, optimize, tactic, | 
|---|
| 84 | * processor). Propagation of the state information is | 
|---|
| 85 | * encapsulated a scenario2scenario() call. Not all transformations | 
|---|
| 86 | * may be legal. | 
|---|
| 87 | * | 
|---|
| 88 | * @+ Scenario administration | 
|---|
| 89 | * Administration of scenarios follows the access rules | 
|---|
| 90 | * defined for code modules in general. | 
|---|
| 91 | * | 
|---|
| 92 | */ | 
|---|
| 93 | #include "monetdb_config.h" | 
|---|
| 94 | #include "mal_scenario.h" | 
|---|
| 95 | #include "mal_linker.h"		/* for getAddress() */ | 
|---|
| 96 | #include "mal_client.h" | 
|---|
| 97 | #include "mal_authorize.h" | 
|---|
| 98 | #include "mal_exception.h" | 
|---|
| 99 | #include "mal_profiler.h" | 
|---|
| 100 | #include "mal_private.h" | 
|---|
| 101 |  | 
|---|
| 102 | #ifdef HAVE_SYS_TIMES_H | 
|---|
| 103 | # include <sys/times.h> | 
|---|
| 104 | #endif | 
|---|
| 105 |  | 
|---|
| 106 | static struct SCENARIO scenarioRec[MAXSCEN] = { | 
|---|
| 107 | { "mal", "mal", | 
|---|
| 108 | 0, 0,			/* hardwired MALinit*/ | 
|---|
| 109 | 0, 0,			/* implicit */ | 
|---|
| 110 | "MALinitClient", (MALfcn) &MALinitClient, | 
|---|
| 111 | "MALexitClient", (MALfcn) &MALexitClient, | 
|---|
| 112 | "MALreader", (MALfcn) &MALreader, | 
|---|
| 113 | "MALparser", (MALfcn) &MALparser, | 
|---|
| 114 | "MALoptimizer", 0, | 
|---|
| 115 | 0, 0, | 
|---|
| 116 | "MALengine", (MALfcn) &MALengine, | 
|---|
| 117 | "MALcallback", (MALfcn) &MALcallback }, | 
|---|
| 118 | {0, 0,		/* name */ | 
|---|
| 119 | 0, 0,		/* init */ | 
|---|
| 120 | 0, 0,		/* exit */ | 
|---|
| 121 | 0, 0,		/* initClient */ | 
|---|
| 122 | 0, 0,		/* exitClient */ | 
|---|
| 123 | 0, 0,		/* reader */ | 
|---|
| 124 | 0, 0,		/* parser */ | 
|---|
| 125 | 0, 0,		/* optimizer */ | 
|---|
| 126 | 0, 0,		/* scheduler */ | 
|---|
| 127 | 0, 0,		/* callback */ | 
|---|
| 128 | 0, 0		/* engine */ | 
|---|
| 129 | } | 
|---|
| 130 | }; | 
|---|
| 131 |  | 
|---|
| 132 | static str fillScenario(Client c, Scenario scen); | 
|---|
| 133 | static MT_Lock scenarioLock = MT_LOCK_INITIALIZER( "scenarioLock"); | 
|---|
| 134 |  | 
|---|
| 135 |  | 
|---|
| 136 | /* | 
|---|
| 137 | * Currently each user can define a new scenario, provided we have a free slot. | 
|---|
| 138 | * Scenarios not hardwired can always be dropped. | 
|---|
| 139 | */ | 
|---|
| 140 | Scenario | 
|---|
| 141 | getFreeScenario(void) | 
|---|
| 142 | { | 
|---|
| 143 | int i; | 
|---|
| 144 | Scenario scen = NULL; | 
|---|
| 145 |  | 
|---|
| 146 | MT_lock_set(&scenarioLock); | 
|---|
| 147 | for (i = 0; i < MAXSCEN && scenarioRec[i].name; i++) | 
|---|
| 148 | ; | 
|---|
| 149 | if (i < MAXSCEN) | 
|---|
| 150 | scen = scenarioRec + i; | 
|---|
| 151 | MT_lock_unset(&scenarioLock); | 
|---|
| 152 |  | 
|---|
| 153 | return scen; | 
|---|
| 154 | } | 
|---|
| 155 |  | 
|---|
| 156 | /* | 
|---|
| 157 | * A scenario is initialized only once per session. | 
|---|
| 158 | * All other requests are silently ignored. | 
|---|
| 159 | * After initialization, all state functions should have been set. | 
|---|
| 160 | * Initialization includes searching for the scenario startup file in | 
|---|
| 161 | * the etc/MonetDB directory. This creates a dependency, because the | 
|---|
| 162 | * malInclude also needs a scenario. To break this cycle, the system should | 
|---|
| 163 | * call once the routine default scenario for each client first. | 
|---|
| 164 | */ | 
|---|
| 165 | static str | 
|---|
| 166 | initScenario(Client c, Scenario s) | 
|---|
| 167 | { | 
|---|
| 168 | str l = s->language; | 
|---|
| 169 | str msg = MAL_SUCCEED; | 
|---|
| 170 |  | 
|---|
| 171 | if (s->initSystemCmd) | 
|---|
| 172 | return(fillScenario(c, s)); | 
|---|
| 173 | /* prepare for conclicts */ | 
|---|
| 174 | MT_lock_set(&mal_contextLock); | 
|---|
| 175 | if (s->initSystem && s->initSystemCmd == 0) { | 
|---|
| 176 | s->initSystemCmd = (MALfcn) getAddress(s->initSystem); | 
|---|
| 177 | if (s->initSystemCmd) { | 
|---|
| 178 | msg = (*s->initSystemCmd) (c); | 
|---|
| 179 | } else { | 
|---|
| 180 | char buf[BUFSIZ]; | 
|---|
| 181 | snprintf(buf,BUFSIZ, "%s.init", l); | 
|---|
| 182 | msg = createException(MAL, buf, "Scenario not initialized"); | 
|---|
| 183 | } | 
|---|
| 184 | } | 
|---|
| 185 | if (msg) { | 
|---|
| 186 | MT_lock_unset(&mal_contextLock); | 
|---|
| 187 | return msg; | 
|---|
| 188 | } | 
|---|
| 189 |  | 
|---|
| 190 | if (s->exitSystem && s->exitSystemCmd == 0) | 
|---|
| 191 | s->exitSystemCmd = (MALfcn) getAddress(s->exitSystem); | 
|---|
| 192 | if (s->initClient && s->initClientCmd == 0) | 
|---|
| 193 | s->initClientCmd = (MALfcn) getAddress(s->initClient); | 
|---|
| 194 | if (s->exitClient && s->exitClientCmd == 0) | 
|---|
| 195 | s->exitClientCmd = (MALfcn) getAddress(s->exitClient); | 
|---|
| 196 | if (s->reader && s->readerCmd == 0) | 
|---|
| 197 | s->readerCmd = (MALfcn) getAddress(s->reader); | 
|---|
| 198 | if (s->parser && s->parserCmd == 0) | 
|---|
| 199 | s->parserCmd = (MALfcn) getAddress(s->parser); | 
|---|
| 200 | if (s->optimizer && s->optimizerCmd == 0) | 
|---|
| 201 | s->optimizerCmd = (MALfcn) getAddress(s->optimizer); | 
|---|
| 202 | if (s->tactics && s->tacticsCmd == 0) | 
|---|
| 203 | s->tacticsCmd = (MALfcn) getAddress(s->tactics); | 
|---|
| 204 | if (s->callback && s->callbackCmd == 0) | 
|---|
| 205 | s->callbackCmd = (MALfcn) getAddress(s->callback); | 
|---|
| 206 | if (s->engine && s->engineCmd == 0) | 
|---|
| 207 | s->engineCmd = (MALfcn) getAddress(s->engine); | 
|---|
| 208 | MT_lock_unset(&mal_contextLock); | 
|---|
| 209 | return(fillScenario(c, s)); | 
|---|
| 210 | } | 
|---|
| 211 |  | 
|---|
| 212 | str | 
|---|
| 213 | defaultScenario(Client c) | 
|---|
| 214 | { | 
|---|
| 215 | return initScenario(c, scenarioRec); | 
|---|
| 216 | } | 
|---|
| 217 |  | 
|---|
| 218 | /* | 
|---|
| 219 | * The Monet debugger provides an option to inspect the scenarios currently | 
|---|
| 220 | * defined. | 
|---|
| 221 | * | 
|---|
| 222 | */ | 
|---|
| 223 | static void | 
|---|
| 224 | print_scenarioCommand(stream *f, str cmd, MALfcn funcptr) | 
|---|
| 225 | { | 
|---|
| 226 | if (cmd) | 
|---|
| 227 | mnstr_printf(f, " \"%s%s\",", cmd, (funcptr? "": "?")); | 
|---|
| 228 | else | 
|---|
| 229 | mnstr_printf(f, " nil,"); | 
|---|
| 230 | } | 
|---|
| 231 |  | 
|---|
| 232 | void | 
|---|
| 233 | showScenario(stream *f, Scenario scen) | 
|---|
| 234 | { | 
|---|
| 235 | mnstr_printf(f, "[ \"%s\",", scen->name); | 
|---|
| 236 | print_scenarioCommand(f, scen->initSystem, scen->initSystemCmd); | 
|---|
| 237 | print_scenarioCommand(f, scen->exitSystem, scen->exitSystemCmd); | 
|---|
| 238 | print_scenarioCommand(f, scen->initClient, scen->initClientCmd); | 
|---|
| 239 | print_scenarioCommand(f, scen->exitClient, scen->exitClientCmd); | 
|---|
| 240 | print_scenarioCommand(f, scen->parser, scen->parserCmd); | 
|---|
| 241 | print_scenarioCommand(f, scen->optimizer, scen->optimizerCmd); | 
|---|
| 242 | print_scenarioCommand(f, scen->tactics, scen->tacticsCmd); | 
|---|
| 243 | print_scenarioCommand(f, scen->callback, scen->callbackCmd); | 
|---|
| 244 | print_scenarioCommand(f, scen->engine, scen->engineCmd); | 
|---|
| 245 | mnstr_printf(f, "]\n"); | 
|---|
| 246 | } | 
|---|
| 247 |  | 
|---|
| 248 | Scenario | 
|---|
| 249 | findScenario(str nme) | 
|---|
| 250 | { | 
|---|
| 251 | int i; | 
|---|
| 252 | Scenario scen = scenarioRec; | 
|---|
| 253 |  | 
|---|
| 254 | for (i = 0; i < MAXSCEN && scen->name; i++, scen++) | 
|---|
| 255 | if (strcmp(scen->name, nme) == 0) | 
|---|
| 256 | return scen; | 
|---|
| 257 | return NULL; | 
|---|
| 258 | } | 
|---|
| 259 |  | 
|---|
| 260 | /* | 
|---|
| 261 | * Functions may become resolved only after the corresponding module | 
|---|
| 262 | * has been loaded. This should be announced as part of the module | 
|---|
| 263 | * prelude code. | 
|---|
| 264 | * Beware that after the update, we also have to adjust the client records. | 
|---|
| 265 | * They contain a copy of the functions addresses. | 
|---|
| 266 | */ | 
|---|
| 267 | void | 
|---|
| 268 | updateScenario(str nme, str fnme, MALfcn fcn) | 
|---|
| 269 | { | 
|---|
| 270 | int phase = -1; | 
|---|
| 271 | Scenario scen = findScenario(nme); | 
|---|
| 272 |  | 
|---|
| 273 | if (scen == NULL) | 
|---|
| 274 | return; | 
|---|
| 275 | if (scen->initSystem && strcmp(scen->initSystem, fnme) == 0) | 
|---|
| 276 | scen->initSystemCmd = fcn; | 
|---|
| 277 | if (scen->exitSystem && strcmp(scen->exitSystem, fnme) == 0) | 
|---|
| 278 | scen->exitSystemCmd = fcn; | 
|---|
| 279 | if (scen->initClient && strcmp(scen->initClient, fnme) == 0) { | 
|---|
| 280 | scen->initClientCmd = fcn; | 
|---|
| 281 | phase = MAL_SCENARIO_INITCLIENT; | 
|---|
| 282 | } | 
|---|
| 283 | if (scen->exitClient && strcmp(scen->exitClient, fnme) == 0) { | 
|---|
| 284 | scen->exitClientCmd = fcn; | 
|---|
| 285 | phase = MAL_SCENARIO_EXITCLIENT; | 
|---|
| 286 | } | 
|---|
| 287 | if (scen->reader && strcmp(scen->reader, fnme) == 0) { | 
|---|
| 288 | scen->readerCmd = fcn; | 
|---|
| 289 | phase = MAL_SCENARIO_READER; | 
|---|
| 290 | } | 
|---|
| 291 | if (scen->parser && strcmp(scen->parser, fnme) == 0) { | 
|---|
| 292 | scen->parserCmd = fcn; | 
|---|
| 293 | phase = MAL_SCENARIO_PARSER; | 
|---|
| 294 | } | 
|---|
| 295 | if (scen->optimizer && strcmp(scen->optimizer, fnme) == 0) { | 
|---|
| 296 | scen->optimizerCmd = fcn; | 
|---|
| 297 | phase = MAL_SCENARIO_OPTIMIZE; | 
|---|
| 298 | } | 
|---|
| 299 | if (scen->tactics && strcmp(scen->tactics, fnme) == 0) { | 
|---|
| 300 | scen->tacticsCmd = fcn; | 
|---|
| 301 | phase = MAL_SCENARIO_SCHEDULER; | 
|---|
| 302 | } | 
|---|
| 303 | if (scen->callback && strcmp(scen->callback, fnme) == 0) { | 
|---|
| 304 | scen->callbackCmd = fcn; | 
|---|
| 305 | phase = MAL_SCENARIO_CALLBACK; | 
|---|
| 306 | } | 
|---|
| 307 | if (scen->engine && strcmp(scen->engine, fnme) == 0) { | 
|---|
| 308 | scen->engineCmd = fcn; | 
|---|
| 309 | phase = MAL_SCENARIO_ENGINE; | 
|---|
| 310 | } | 
|---|
| 311 | if (phase != -1) { | 
|---|
| 312 | Client c1; | 
|---|
| 313 |  | 
|---|
| 314 | for (c1 = mal_clients; c1 < mal_clients + MAL_MAXCLIENTS; c1++) { | 
|---|
| 315 | if (c1->scenario && | 
|---|
| 316 | strcmp(c1->scenario, scen->name) == 0) | 
|---|
| 317 | c1->phase[phase] = fcn; | 
|---|
| 318 | if (c1->oldscenario && | 
|---|
| 319 | strcmp(c1->oldscenario, scen->name) == 0) | 
|---|
| 320 | c1->oldphase[phase] = fcn; | 
|---|
| 321 | } | 
|---|
| 322 | } | 
|---|
| 323 | } | 
|---|
| 324 |  | 
|---|
| 325 | void | 
|---|
| 326 | showScenarioByName(stream *f, str nme) | 
|---|
| 327 | { | 
|---|
| 328 | Scenario scen = findScenario(nme); | 
|---|
| 329 |  | 
|---|
| 330 | if (scen) | 
|---|
| 331 | showScenario(f, scen); | 
|---|
| 332 | } | 
|---|
| 333 |  | 
|---|
| 334 | void | 
|---|
| 335 | showAllScenarios(stream *f) | 
|---|
| 336 | { | 
|---|
| 337 | int i; | 
|---|
| 338 | Scenario scen = scenarioRec; | 
|---|
| 339 |  | 
|---|
| 340 | for (i = 0; i < MAXSCEN && scen->name; i++, scen++) | 
|---|
| 341 | showScenario(f, scen); | 
|---|
| 342 | } | 
|---|
| 343 |  | 
|---|
| 344 | str getScenarioLanguage(Client c){ | 
|---|
| 345 | Scenario scen= findScenario(c->scenario); | 
|---|
| 346 | if( scen) return scen->language; | 
|---|
| 347 | return "mal"; | 
|---|
| 348 | } | 
|---|
| 349 | /* | 
|---|
| 350 | * Changing the scenario for a particular client invalidates the | 
|---|
| 351 | * state maintained for the previous scenario. The old scenario is | 
|---|
| 352 | * retained in the client record to facilitate propagation of | 
|---|
| 353 | * state information, or to simply switch back to the previous one. | 
|---|
| 354 | * Before we initialize a scenario the client scenario is reset to | 
|---|
| 355 | * the MAL scenario. This implies that all scenarios are initialized | 
|---|
| 356 | * using the same scenario. After the scenario initialization file | 
|---|
| 357 | * has been processed, the scenario phases are replaced with the | 
|---|
| 358 | * proper ones. | 
|---|
| 359 | * | 
|---|
| 360 | * All client records should be initialized with a default | 
|---|
| 361 | * scenario, i.e. the first described in the scenario table. | 
|---|
| 362 | */ | 
|---|
| 363 | static str | 
|---|
| 364 | fillScenario(Client c, Scenario scen) | 
|---|
| 365 | { | 
|---|
| 366 | c->scenario = scen->name; | 
|---|
| 367 |  | 
|---|
| 368 | c->phase[MAL_SCENARIO_READER] = scen->readerCmd; | 
|---|
| 369 | c->phase[MAL_SCENARIO_PARSER] = scen->parserCmd; | 
|---|
| 370 | c->phase[MAL_SCENARIO_OPTIMIZE] = scen->optimizerCmd; | 
|---|
| 371 | c->phase[MAL_SCENARIO_SCHEDULER] = scen->tacticsCmd; | 
|---|
| 372 | c->phase[MAL_SCENARIO_CALLBACK] = scen->callbackCmd; | 
|---|
| 373 | c->phase[MAL_SCENARIO_ENGINE] = scen->engineCmd; | 
|---|
| 374 | c->phase[MAL_SCENARIO_INITCLIENT] = scen->initClientCmd; | 
|---|
| 375 | c->phase[MAL_SCENARIO_EXITCLIENT] = scen->exitClientCmd; | 
|---|
| 376 | c->state[MAL_SCENARIO_READER] = 0; | 
|---|
| 377 | c->state[MAL_SCENARIO_PARSER] = 0; | 
|---|
| 378 | c->state[MAL_SCENARIO_OPTIMIZE] = 0; | 
|---|
| 379 | c->state[MAL_SCENARIO_SCHEDULER] = 0; | 
|---|
| 380 | c->state[MAL_SCENARIO_ENGINE] = 0; | 
|---|
| 381 | c->state[MAL_SCENARIO_INITCLIENT] = 0; | 
|---|
| 382 | c->state[MAL_SCENARIO_EXITCLIENT] = 0; | 
|---|
| 383 | return(MAL_SUCCEED); | 
|---|
| 384 | } | 
|---|
| 385 |  | 
|---|
| 386 | /* | 
|---|
| 387 | * Setting a new scenario calls for saving the previous state | 
|---|
| 388 | * and execution of the initClientScenario routine. | 
|---|
| 389 | */ | 
|---|
| 390 | str | 
|---|
| 391 | setScenario(Client c, str nme) | 
|---|
| 392 | { | 
|---|
| 393 | int i; | 
|---|
| 394 | str msg; | 
|---|
| 395 | Scenario scen; | 
|---|
| 396 |  | 
|---|
| 397 | scen = findScenario(nme); | 
|---|
| 398 | if (scen == NULL) | 
|---|
| 399 | throw(MAL, "setScenario", SCENARIO_NOT_FOUND " '%s'", nme); | 
|---|
| 400 |  | 
|---|
| 401 | if (c->scenario) { | 
|---|
| 402 | c->oldscenario = c->scenario; | 
|---|
| 403 | for (i = 0; i < SCENARIO_PROPERTIES; i++) { | 
|---|
| 404 | c->oldstate[i] = c->state[i]; | 
|---|
| 405 | c->oldphase[i] = c->phase[i]; | 
|---|
| 406 | } | 
|---|
| 407 | } | 
|---|
| 408 | for (i = 0; i < SCENARIO_PROPERTIES; i++) | 
|---|
| 409 | c->state[i] = 0; | 
|---|
| 410 |  | 
|---|
| 411 | msg = initScenario(c, scen); | 
|---|
| 412 | if (msg) { | 
|---|
| 413 | /* error occurred, reset the scenario , assume default always works */ | 
|---|
| 414 | c->scenario = c->oldscenario; | 
|---|
| 415 | for (i = 0; i < SCENARIO_PROPERTIES; i++) { | 
|---|
| 416 | c->state[i] = c->oldstate[i]; | 
|---|
| 417 | c->phase[i] = c->oldphase[i]; | 
|---|
| 418 | c->oldstate[i] = NULL; | 
|---|
| 419 | c->oldphase[i] = NULL; | 
|---|
| 420 | } | 
|---|
| 421 | c->oldscenario = NULL; | 
|---|
| 422 | return msg; | 
|---|
| 423 | } | 
|---|
| 424 | return MAL_SUCCEED; | 
|---|
| 425 | } | 
|---|
| 426 |  | 
|---|
| 427 | /* | 
|---|
| 428 | * After finishing a session in a scenario, we should reset the | 
|---|
| 429 | * state of the previous one. But also call the exitClient | 
|---|
| 430 | * to garbage collect any scenario specific structures. | 
|---|
| 431 | */ | 
|---|
| 432 | #if 0 | 
|---|
| 433 | str | 
|---|
| 434 | getCurrentScenario(Client c) | 
|---|
| 435 | { | 
|---|
| 436 | return c->scenario; | 
|---|
| 437 | } | 
|---|
| 438 | #endif | 
|---|
| 439 |  | 
|---|
| 440 | void | 
|---|
| 441 | resetScenario(Client c) | 
|---|
| 442 | { | 
|---|
| 443 | int i; | 
|---|
| 444 | Scenario scen = scenarioRec; | 
|---|
| 445 |  | 
|---|
| 446 | if (c->scenario == 0) | 
|---|
| 447 | return; | 
|---|
| 448 |  | 
|---|
| 449 | scen = findScenario(c->scenario); | 
|---|
| 450 | if (scen != NULL && scen->exitClientCmd) | 
|---|
| 451 | (*scen->exitClientCmd) (c); | 
|---|
| 452 |  | 
|---|
| 453 | c->scenario = c->oldscenario; | 
|---|
| 454 | for (i = 0; i < SCENARIO_PROPERTIES; i++) { | 
|---|
| 455 | c->state[i] = c->oldstate[i]; | 
|---|
| 456 | c->phase[i] = c->oldphase[i]; | 
|---|
| 457 | } | 
|---|
| 458 | c->oldscenario = 0; | 
|---|
| 459 | } | 
|---|
| 460 |  | 
|---|
| 461 | /* | 
|---|
| 462 | * The building blocks of scenarios are routines obeying a strict | 
|---|
| 463 | * name signature. They require exclusive access to the client | 
|---|
| 464 | * record. Any specific information should be accessible from | 
|---|
| 465 | * there, e.g., access to a scenario specific state descriptor. | 
|---|
| 466 | * The client scenario initialization and finalization brackets | 
|---|
| 467 | * are  @sc{xyzinitClient()} and @sc{xyzexitClient()}. | 
|---|
| 468 | * | 
|---|
| 469 | * The @sc{xyzparser(Client c)} contains the parser for language XYZ | 
|---|
| 470 | * and should fill the MAL program block associated with the client record. | 
|---|
| 471 | * The latter may have been initialized with variables. | 
|---|
| 472 | * Each language parser may require a catalog with information | 
|---|
| 473 | * on the translation of language specific datastructures into their BAT | 
|---|
| 474 | * equivalent. | 
|---|
| 475 | * | 
|---|
| 476 | * The @sc{xyzoptimizer(Client c)} contains language specific optimizations | 
|---|
| 477 | * using the MAL intermediate code as a starting point. | 
|---|
| 478 | * | 
|---|
| 479 | * The @sc{xyztactics(Client c)} synchronizes the program execution with the | 
|---|
| 480 | * state of the machine, e.g., claiming resources, the history of the client | 
|---|
| 481 | * or alignment of the request with concurrent actions (e.g., transaction | 
|---|
| 482 | * coordination). | 
|---|
| 483 | * | 
|---|
| 484 | * The @sc{xyzengine(Client c)} contains the applicable back-end engine. | 
|---|
| 485 | * The default is the MAL interpreter, which provides good balance | 
|---|
| 486 | * between speed and ability to analysis its behavior. | 
|---|
| 487 | * | 
|---|
| 488 | */ | 
|---|
| 489 | static const char *phases[] = { | 
|---|
| 490 | [MAL_SCENARIO_CALLBACK] = "scenario callback", | 
|---|
| 491 | [MAL_SCENARIO_ENGINE] = "scenario engine", | 
|---|
| 492 | [MAL_SCENARIO_EXITCLIENT] = "scenario exitclient", | 
|---|
| 493 | [MAL_SCENARIO_INITCLIENT] = "scenario initclient", | 
|---|
| 494 | [MAL_SCENARIO_OPTIMIZE] = "scenario optimize", | 
|---|
| 495 | [MAL_SCENARIO_PARSER] = "scenario parser", | 
|---|
| 496 | [MAL_SCENARIO_READER] = "scenario reader", | 
|---|
| 497 | [MAL_SCENARIO_SCHEDULER] = "scenario scheduler", | 
|---|
| 498 | }; | 
|---|
| 499 | static str | 
|---|
| 500 | runPhase(Client c, int phase) | 
|---|
| 501 | { | 
|---|
| 502 | str msg = MAL_SUCCEED; | 
|---|
| 503 | if (c->phase[phase]) { | 
|---|
| 504 | MT_thread_setworking(phases[phase]); | 
|---|
| 505 | return msg = (str) (*c->phase[phase])(c); | 
|---|
| 506 | } | 
|---|
| 507 | return msg; | 
|---|
| 508 | } | 
|---|
| 509 |  | 
|---|
| 510 | /* | 
|---|
| 511 | * Access control enforcement. Except for the server owner | 
|---|
| 512 | * running a scenario should be explicitly permitted. | 
|---|
| 513 | */ | 
|---|
| 514 | static str | 
|---|
| 515 | runScenarioBody(Client c, int once) | 
|---|
| 516 | { | 
|---|
| 517 | str msg = MAL_SUCCEED; | 
|---|
| 518 |  | 
|---|
| 519 | while (c->mode > FINISHCLIENT && !GDKexiting()) { | 
|---|
| 520 | // be aware that a MAL call  may initialize a different scenario | 
|---|
| 521 | if ( !c->state[0] && (msg = runPhase(c, MAL_SCENARIO_INITCLIENT)) ) | 
|---|
| 522 | goto wrapup; | 
|---|
| 523 | if ( c->mode <= FINISHCLIENT ||  (msg = runPhase(c, MAL_SCENARIO_READER)) ) | 
|---|
| 524 | goto wrapup; | 
|---|
| 525 | if ( c->mode <= FINISHCLIENT  || (msg = runPhase(c, MAL_SCENARIO_PARSER)) || c->blkmode) | 
|---|
| 526 | goto wrapup; | 
|---|
| 527 | if ( c->mode <= FINISHCLIENT ||  (msg = runPhase(c, MAL_SCENARIO_OPTIMIZE)) ) | 
|---|
| 528 | goto wrapup; | 
|---|
| 529 | if ( c->mode <= FINISHCLIENT || (msg = runPhase(c, MAL_SCENARIO_SCHEDULER))) | 
|---|
| 530 | goto wrapup; | 
|---|
| 531 | if ( c->mode <= FINISHCLIENT || (msg = runPhase(c, MAL_SCENARIO_ENGINE))) | 
|---|
| 532 | goto wrapup; | 
|---|
| 533 | wrapup: | 
|---|
| 534 | if (msg != MAL_SUCCEED){ | 
|---|
| 535 | if (c->phase[MAL_SCENARIO_CALLBACK]) { | 
|---|
| 536 | MT_thread_setworking(phases[MAL_SCENARIO_CALLBACK]); | 
|---|
| 537 | msg = (str) (*c->phase[MAL_SCENARIO_CALLBACK])(c, msg); | 
|---|
| 538 | } | 
|---|
| 539 | if (msg) { | 
|---|
| 540 | mnstr_printf(c->fdout, "!%s%s", msg, (msg[strlen(msg)-1] == '\n'? "": "\n")); | 
|---|
| 541 | freeException(msg); | 
|---|
| 542 | msg = MAL_SUCCEED; | 
|---|
| 543 | } | 
|---|
| 544 | } | 
|---|
| 545 | if( GDKerrbuf && GDKerrbuf[0]) | 
|---|
| 546 | mnstr_printf(c->fdout, "!GDKerror: %s\n",GDKerrbuf); | 
|---|
| 547 | assert(c->curprg->def->errors == NULL); | 
|---|
| 548 | c->actions++; | 
|---|
| 549 | if( once) break; | 
|---|
| 550 | } | 
|---|
| 551 | if (once == 0) | 
|---|
| 552 | msg = runPhase(c, MAL_SCENARIO_EXITCLIENT); | 
|---|
| 553 | return msg; | 
|---|
| 554 | } | 
|---|
| 555 |  | 
|---|
| 556 | str | 
|---|
| 557 | runScenario(Client c, int once) | 
|---|
| 558 | { | 
|---|
| 559 | str msg = MAL_SUCCEED; | 
|---|
| 560 |  | 
|---|
| 561 | if (c == 0 || c->phase[MAL_SCENARIO_READER] == 0) | 
|---|
| 562 | return msg; | 
|---|
| 563 | msg = runScenarioBody(c,once); | 
|---|
| 564 | if (msg != MAL_SUCCEED && | 
|---|
| 565 | strcmp(msg, "MALException:client.quit:Server stopped.")) | 
|---|
| 566 | mnstr_printf(c->fdout, "!%s\n",msg); | 
|---|
| 567 | return msg; | 
|---|
| 568 | } | 
|---|
| 569 |  | 
|---|
| 570 |  | 
|---|