Line data Source code
1 : /*
2 : * SPDX-License-Identifier: MPL-2.0
3 : *
4 : * This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 : *
8 : * Copyright 2024 MonetDB Foundation;
9 : * Copyright August 2008 - 2023 MonetDB B.V.;
10 : * Copyright 1997 - July 2008 CWI.
11 : */
12 :
13 : /*
14 : * (author) M. Kersten
15 : * @+ Session Scenarios
16 : * In MonetDB multiple languages and execution engines can
17 : * be combined at run time to satisfy a wide user-community.
18 : * Such an assemblage of components is called a @emph{scenario}
19 : * and consists of a @emph{engine}. These hooks allow
20 : * for both linked-in and external components.
21 : *
22 : * The languages supported are SQL, the Monet Assembly Language (MAL), and profiler.
23 : * The default scenario handles MAL instructions, which is used
24 : * to illustrate the behavior of the scenario steps.
25 : *
26 : * The MAL reader component handles interaction with
27 : * a front-end to obtain a string for subsequent compilation and
28 : * execution. The reader uses the common stream package to read
29 : * data in large chunks, if possible. In interactive mode the lines
30 : * are processed one at a time.
31 : *
32 : * The final stage is to choose an execution paradigm,
33 : * i.e. interpretative (default), compilation of an ad-hoc user
34 : * defined function, dataflow driven interpretation,
35 : * or vectorized pipe-line execution by a dedicated engine.
36 : *
37 : * A failure encountered in any of the steps terminates the scenario
38 : * cycle. It returns to the user for a new command.
39 : *
40 : * @+ Scenario management
41 : * Scenarios are captured in modules; they can be dynamically loaded
42 : * and remain active until the system is brought to a halt.
43 : * Thereafter its components are set to those required by
44 : * the scenario and the client initialization takes place.
45 : *
46 : * A scenario is interpreted in a strictly linear fashion,
47 : * i.e. performing a symbolic optimization before scheduling decisions
48 : * are taken.
49 : * The routines associated with each state in
50 : * the scenario may patch the code so as to assure that subsequent
51 : * execution can use a different scenario, e.g., to handle dynamic
52 : * code fragments.
53 : *
54 : * The state of execution is maintained in the scenario record for
55 : * each individual client. Sharing this information between clients
56 : * should be dealt with in the implementation of the scenario managers.
57 : * Upon need, the client can postpone a session scenario by
58 : * pushing a new one(language, optimize,
59 : * processor).
60 : *
61 : * @+ Scenario administration
62 : * Administration of scenarios follows the access rules
63 : * defined for code modules in general.
64 : *
65 : */
66 : #include "monetdb_config.h"
67 : #include "mal_scenario.h"
68 : #include "mal_client.h"
69 : #include "mal_authorize.h"
70 : #include "mal_exception.h"
71 : #include "mal_profiler.h"
72 : #include "mal_private.h"
73 : #include "mal_session.h"
74 :
75 : #ifdef HAVE_SYS_TIMES_H
76 : # include <sys/times.h>
77 : #endif
78 :
79 : static struct SCENARIO scenarioRec[MAXSCEN] = {
80 : {
81 : .name = "mal",
82 : .language = "mal",
83 : .initClient = "MALinitClient",
84 : .initClientCmd = (init_client) MALinitClient,
85 : .exitClient = "MALexitClient",
86 : .exitClientCmd = (exit_client) MALexitClient,
87 : .engine = "MALengine",
88 : .engineCmd = (engine_fptr) MALengine,
89 : },
90 : {
91 : .name = NULL,
92 : }
93 : };
94 :
95 : static str fillScenario(Client c, Scenario scen);
96 : static MT_Lock scenarioLock = MT_LOCK_INITIALIZER(scenarioLock);
97 :
98 :
99 : /*
100 : * Currently each user can define a new scenario, provided we have a free slot.
101 : * Scenarios not hardwired can always be dropped.
102 : */
103 : Scenario
104 630 : getFreeScenario(void)
105 : {
106 630 : int i;
107 630 : Scenario scen = NULL;
108 :
109 630 : MT_lock_set(&scenarioLock);
110 2205 : for (i = 0; i < MAXSCEN && scenarioRec[i].name; i++)
111 : ;
112 630 : if (i < MAXSCEN)
113 630 : scen = scenarioRec + i;
114 630 : MT_lock_unset(&scenarioLock);
115 :
116 630 : return scen;
117 : }
118 :
119 : str
120 315 : defaultScenario(Client c)
121 : {
122 315 : return fillScenario(c, scenarioRec);
123 : }
124 :
125 : /*
126 : * The Monet debugger provides an option to inspect the scenarios currently
127 : * defined.
128 : *
129 : */
130 : static void
131 0 : print_scenarioCommand(stream *f, str cmd)
132 : {
133 0 : mnstr_printf(f, " \"%s\",", cmd);
134 : }
135 :
136 : void
137 0 : showScenario(stream *f, Scenario scen)
138 : {
139 0 : mnstr_printf(f, "[ \"%s\",", scen->name);
140 0 : print_scenarioCommand(f, scen->initClient);
141 0 : print_scenarioCommand(f, scen->exitClient);
142 0 : print_scenarioCommand(f, scen->engine);
143 0 : mnstr_printf(f, "]\n");
144 0 : }
145 :
146 : Scenario
147 37976 : findScenario(const char *nme)
148 : {
149 37976 : int i;
150 37976 : Scenario scen = scenarioRec;
151 :
152 76124 : for (i = 0; i < MAXSCEN; i++, scen++)
153 76124 : if (scen->name && strcmp(scen->name, nme) == 0)
154 37976 : return scen;
155 : return NULL;
156 : }
157 :
158 : void
159 0 : showScenarioByName(stream *f, const char *nme)
160 : {
161 0 : Scenario scen = findScenario(nme);
162 :
163 0 : if (scen)
164 0 : showScenario(f, scen);
165 0 : }
166 :
167 : void
168 0 : showAllScenarios(stream *f)
169 : {
170 0 : int i;
171 0 : Scenario scen = scenarioRec;
172 :
173 0 : for (i = 0; i < MAXSCEN && scen->name; i++, scen++)
174 0 : showScenario(f, scen);
175 0 : }
176 :
177 : str
178 45 : getScenarioLanguage(Client c)
179 : {
180 45 : Scenario scen = findScenario(c->scenario);
181 45 : if (scen)
182 45 : return scen->language;
183 : return "mal";
184 : }
185 :
186 : /*
187 : * Changing the scenario for a particular client invalidates the
188 : * state maintained for the previous scenario.
189 : * Before we initialize a scenario the client scenario is reset to
190 : * the MAL scenario. This implies that all scenarios are initialized
191 : * using the same scenario. After the scenario initialization file
192 : * has been processed, the scenario functions are replaced with the
193 : * proper ones.
194 : *
195 : * All client records should be initialized with a default
196 : * scenario, i.e. the first described in the scenario table.
197 : */
198 : static str
199 37652 : fillScenario(Client c, Scenario scen)
200 : {
201 37652 : c->scenario = scen->name;
202 :
203 37652 : c->engine = scen->engineCmd;
204 37652 : c->initClient = scen->initClientCmd;
205 37652 : c->exitClient = scen->exitClientCmd;
206 37652 : return (MAL_SUCCEED);
207 : }
208 :
209 : /*
210 : * Setting a new scenario calls for saving the previous state
211 : * and execution of the initClientScenario routine.
212 : */
213 : str
214 37337 : setScenario(Client c, const char *nme)
215 : {
216 37337 : str msg;
217 37337 : Scenario scen;
218 :
219 37337 : scen = findScenario(nme);
220 37337 : if (scen == NULL)
221 0 : throw(MAL, "setScenario", SCENARIO_NOT_FOUND " '%s'", nme);
222 :
223 37337 : msg = fillScenario(c, scen);
224 37337 : if (msg) {
225 : /* error occurred, reset the scenario , assume default always works */
226 : c->scenario = NULL;
227 : c->initClient = NULL;
228 : c->exitClient = NULL;
229 : c->engine = NULL;
230 : return msg;
231 : }
232 37337 : return MAL_SUCCEED;
233 : }
234 :
235 : /*
236 : * After finishing a session in a scenario, we should reset the
237 : * state of the previous one. But also call the exitClient
238 : * to garbage collect any scenario specific structures.
239 : */
240 :
241 : void
242 0 : resetScenario(Client c)
243 : {
244 0 : Scenario scen = scenarioRec;
245 :
246 0 : if (c->scenario == 0)
247 : return;
248 :
249 0 : scen = findScenario(c->scenario);
250 0 : if (scen != NULL && scen->exitClientCmd) {
251 0 : str msg = (*scen->exitClientCmd) (c);
252 0 : freeException(msg);
253 : }
254 :
255 0 : c->scenario = NULL;
256 0 : c->initClient = NULL;
257 0 : c->exitClient = NULL;
258 0 : c->engine = NULL;
259 : }
260 :
261 : /*
262 : * The building blocks of scenarios are routines obeying a strict
263 : * name signature. They require exclusive access to the client
264 : * record. Any specific information should be accessible from
265 : * there, e.g., access to a scenario specific descriptor.
266 : * The client scenario initialization and finalization brackets
267 : * are @sc{xyzinitClient()} and @sc{xyzexitClient()}.
268 : *
269 : * The @sc{xyzengine(Client c)} contains the applicable back-end engine.
270 : * The default is the MAL interpreter, which provides good balance
271 : * between speed and ability to analysis its behavior.
272 : *
273 : */
274 :
275 : /*
276 : * Access control enforcement. Except for the server owner
277 : * running a scenario should be explicitly permitted.
278 : */
279 : static str
280 37319 : runScenarioBody(Client c)
281 : {
282 37319 : MT_thread_setworking("engine");
283 530541 : while (c->mode > FINISHCLIENT && !GDKexiting()) {
284 493129 : c->engine(c);
285 493222 : assert(c->curprg->def->errors == NULL);
286 : }
287 37321 : if (!GDKexiting() && GDKerrbuf && GDKerrbuf[0])
288 0 : mnstr_printf(c->fdout, "!GDKerror: %s\n", GDKerrbuf);
289 37321 : return c->exitClient(c);
290 : }
291 :
292 : str
293 37319 : runScenario(Client c)
294 : {
295 37319 : str msg = MAL_SUCCEED;
296 :
297 37319 : if (c == 0)
298 : return msg;
299 37319 : msg = runScenarioBody(c);
300 37324 : if (msg != MAL_SUCCEED &&
301 0 : strcmp(msg, "MALException:client.quit:Server stopped."))
302 0 : mnstr_printf(c->fdout, "!%s\n", msg);
303 : return msg;
304 : }
|