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 | * For documentation see website |
12 | */ |
13 | #include "monetdb_config.h" |
14 | #include "mal_factory.h" |
15 | #include "mal_instruction.h" |
16 | #include "mal_interpreter.h" |
17 | #include "mal_function.h" |
18 | #include "mal_exception.h" |
19 | #include "mal_session.h" |
20 | #include "mal_namespace.h" |
21 | #include "mal_private.h" |
22 | |
23 | typedef struct { |
24 | int id; /* unique plant number */ |
25 | MalBlkPtr factory; |
26 | MalStkPtr stk; /* private state */ |
27 | int pc; /* where we are */ |
28 | int inuse; /* able to handle it */ |
29 | int next; /* next plant of same factory */ |
30 | int policy; /* flags to control behavior */ |
31 | |
32 | Client client; /* who called it */ |
33 | MalBlkPtr caller; /* from routine */ |
34 | MalStkPtr env; /* with the stack */ |
35 | InstrPtr pci; /* with the instruction */ |
36 | } PlantRecord, *Plant; |
37 | |
38 | #define MAXPLANTS 256 |
39 | static PlantRecord plants[MAXPLANTS]; |
40 | static int lastPlant= 0; |
41 | static int plantId = 1; |
42 | |
43 | mal_export Plant newPlant(MalBlkPtr mb); |
44 | |
45 | |
46 | static int |
47 | findPlant(MalBlkPtr mb){ |
48 | int i; |
49 | for(i=0; i<lastPlant; i++) |
50 | if( plants[i].factory == mb) |
51 | return i; |
52 | return -1; |
53 | } |
54 | |
55 | str |
56 | runFactory(Client cntxt, MalBlkPtr mb, MalBlkPtr mbcaller, MalStkPtr stk, InstrPtr pci) |
57 | { |
58 | Plant pl=0; |
59 | int firstcall= TRUE, i, k; |
60 | InstrPtr psig = getInstrPtr(mb, 0); |
61 | ValPtr lhs, rhs; |
62 | char cmd; |
63 | str msg; |
64 | |
65 | #ifdef DEBUG_MAL_FACTORY |
66 | fprintf(stderr, "#factoryMgr called\n" ); |
67 | #endif |
68 | /* the lookup can be largely avoided by handing out the index |
69 | upon factory definition. todo |
70 | Alternative is to move them to the front |
71 | */ |
72 | for(i=0; i< lastPlant; i++) |
73 | if( plants[i].factory == mb){ |
74 | if(i > 0 && i< lastPlant ){ |
75 | PlantRecord prec= plants[i-1]; |
76 | plants[i-1] = plants[i]; |
77 | plants[i]= prec; |
78 | i--; |
79 | } |
80 | pl= plants+i; |
81 | firstcall= FALSE; |
82 | break; |
83 | } |
84 | if (pl == 0) { |
85 | /* compress the plant table*/ |
86 | for(k=i=0;i<=lastPlant; i++) |
87 | if( plants[i].inuse) |
88 | plants[k++]= plants[i]; |
89 | lastPlant = k; |
90 | /* initialize a new plant using the owner policy */ |
91 | pl = newPlant(mb); |
92 | if (pl == NULL) |
93 | throw(MAL, "factory.new" , SQLSTATE(HY001) MAL_MALLOC_FAIL); |
94 | } |
95 | /* |
96 | * We have found a factory to process the request. |
97 | * Let's call it as a synchronous action, without concern on parallelism. |
98 | */ |
99 | /* remember context */ |
100 | pl->client = cntxt; |
101 | pl->caller = mbcaller; |
102 | pl->env = stk; |
103 | pl->pci = pci; |
104 | pl->inuse = 1; |
105 | /* inherit debugging */ |
106 | cmd = stk->cmd; |
107 | if ( pl->stk == NULL) |
108 | throw(MAL, "factory.new" , "internal error, stack frame missing" ); |
109 | |
110 | /* copy the calling arguments onto the stack |
111 | of the factory */ |
112 | i = psig->retc; |
113 | for (k = pci->retc; i < pci->argc; i++, k++) { |
114 | lhs = &pl->stk->stk[psig->argv[k]]; |
115 | /* variable arguments ? */ |
116 | if (k == psig->argc - 1) |
117 | k--; |
118 | |
119 | rhs = &pl->env->stk[getArg(pci, i)]; |
120 | if (VALcopy(lhs, rhs) == NULL) |
121 | throw(MAL, "factory.call" , SQLSTATE(HY001) MAL_MALLOC_FAIL); |
122 | if( lhs->vtype == TYPE_bat ) |
123 | BBPretain(lhs->val.bval); |
124 | } |
125 | if (mb->errors) |
126 | throw(MAL, "factory.call" , PROGRAM_GENERAL); |
127 | if (firstcall ){ |
128 | /* initialize the stack */ |
129 | for(i= psig->argc; i< mb->vtop; i++) { |
130 | lhs = &pl->stk->stk[i]; |
131 | if( isVarConstant(mb,i) > 0 ){ |
132 | if( !isVarDisabled(mb,i)){ |
133 | rhs = &getVarConstant(mb,i); |
134 | if (VALcopy(lhs,rhs) == NULL) |
135 | throw(MAL, "factory.call" , SQLSTATE(HY001) MAL_MALLOC_FAIL); |
136 | } |
137 | } else{ |
138 | lhs->vtype = getVarGDKType(mb,i); |
139 | lhs->val.pval = 0; |
140 | lhs->len = 0; |
141 | } |
142 | } |
143 | pl->stk->stkbot= mb->vtop; /* stack already initialized */ |
144 | msg = runMAL(cntxt, mb, 0, pl->stk); |
145 | } else { |
146 | msg = reenterMAL(cntxt, mb, pl->pc, -1, pl->stk); |
147 | } |
148 | /* propagate change in debugging status */ |
149 | if (cmd && pl->stk && pl->stk->cmd != cmd && cmd != 'x') |
150 | for (; stk; stk = stk->up) |
151 | stk->cmd = pl->stk->cmd; |
152 | return msg; |
153 | } |
154 | /* |
155 | * The shortcut operator for factory calls assumes that the user is |
156 | * not interested in the results produced. |
157 | */ |
158 | str |
159 | callFactory(Client cntxt, MalBlkPtr mb, ValPtr argv[], char flag){ |
160 | Plant pl; |
161 | InstrPtr psig = getInstrPtr(mb, 0); |
162 | int i; |
163 | ValPtr lhs,rhs; |
164 | MalStkPtr stk; |
165 | str ret; |
166 | |
167 | i= findPlant(mb); |
168 | if( i< 0) { |
169 | /* first call? prepare the factory */ |
170 | pl = newPlant(mb); |
171 | if (pl == NULL) |
172 | throw(MAL, "factory.call" , SQLSTATE(HY001) MAL_MALLOC_FAIL); |
173 | /* remember context, which does not exist. */ |
174 | pl->client = cntxt; |
175 | pl->caller = 0; |
176 | pl->env = 0; |
177 | pl->pci = 0; |
178 | pl->inuse = 1; |
179 | stk = pl->stk; |
180 | /* initialize the stack */ |
181 | stk->stktop= mb->vtop; |
182 | stk->stksize= mb->vsize; |
183 | stk->blk= mb; |
184 | stk->up = 0; |
185 | stk->cmd= flag; |
186 | /* initialize the stack */ |
187 | for(i= psig->argc; i< mb->vtop; i++) |
188 | if( isVarConstant(mb,i) > 0 ){ |
189 | lhs = &stk->stk[i]; |
190 | rhs = &getVarConstant(mb,i); |
191 | if (VALcopy(lhs,rhs) == NULL) |
192 | throw(MAL, "factory.call" , SQLSTATE(HY001) MAL_MALLOC_FAIL); |
193 | } else { |
194 | lhs = &stk->stk[i]; |
195 | lhs->vtype = getVarGDKType(mb,i); |
196 | } |
197 | pl->stk= stk; |
198 | } else { |
199 | pl= plants+i; |
200 | /* |
201 | * When you re-enter the factory the old arguments should be |
202 | * released to make room for the new ones. |
203 | */ |
204 | for (i = psig->retc; i < psig->argc; i++) { |
205 | lhs = &pl->stk->stk[psig->argv[i]]; |
206 | if( lhs->vtype == TYPE_bat ) |
207 | BBPrelease(lhs->val.bval); |
208 | } |
209 | } |
210 | /* copy the calling arguments onto the stack of the factory */ |
211 | i = psig->retc; |
212 | for (i = psig->retc; i < psig->argc; i++) { |
213 | lhs = &pl->stk->stk[psig->argv[i]]; |
214 | if (VALcopy(lhs, argv[i]) == NULL) |
215 | throw(MAL, "factory.call" , SQLSTATE(HY001) MAL_MALLOC_FAIL); |
216 | if( lhs->vtype == TYPE_bat ) |
217 | BBPretain(lhs->val.bval); |
218 | } |
219 | ret= reenterMAL(cntxt, mb, pl->pc, -1, pl->stk); |
220 | /* garbage collect the string arguments, these positions |
221 | will simply be overwritten the next time. |
222 | for (i = psig->retc; i < psig->argc; i++) |
223 | garbageElement(lhs = &pl->stk->stk[psig->argv[i]]); |
224 | */ |
225 | return ret; |
226 | } |
227 | /* |
228 | * A new plant is constructed. The properties of the factory |
229 | * should be known upon compile time. They are retrieved from |
230 | * the signature of the factory definition. |
231 | */ |
232 | Plant |
233 | newPlant(MalBlkPtr mb) |
234 | { |
235 | Plant p, plim; |
236 | MalStkPtr stk; |
237 | |
238 | plim = plants + lastPlant; |
239 | for (p = plants; p < plim && p->factory; p++) |
240 | ; |
241 | stk = newGlobalStack(mb->vsize); |
242 | if (lastPlant == MAXPLANTS || stk == NULL){ |
243 | if( stk) GDKfree(stk); |
244 | return 0; |
245 | } |
246 | if (p == plim) |
247 | lastPlant++; |
248 | p->factory = mb; |
249 | p->id = plantId++; |
250 | |
251 | p->pc = 1; /* where we start */ |
252 | p->stk = stk; |
253 | p->stk->blk = mb; |
254 | p->stk->keepAlive = TRUE; |
255 | return p; |
256 | } |
257 | |
258 | /* |
259 | * Upon reaching the yield operator, the factory is |
260 | * suspended until the next request arrives. |
261 | * The information in the target list should be delivered |
262 | * to the caller stack frame. |
263 | */ |
264 | int |
265 | yieldResult(MalBlkPtr mb, InstrPtr p, int pc) |
266 | { |
267 | Plant pl, plim = plants + lastPlant; |
268 | ValPtr lhs, rhs; |
269 | int i; |
270 | |
271 | (void) p; |
272 | (void) pc; |
273 | for (pl = plants; pl < plim; pl++) |
274 | if (pl->factory == mb ) { |
275 | if( pl->env == NULL) |
276 | return(int) (pl-plants); |
277 | for (i = 0; i < p->retc; i++) { |
278 | #ifdef DEBUG_MAL_FACTORY |
279 | fprintf(stderr,"#lhs %d rhs %d\n" , getArg(pl->pci, i), getArg(p, i)); |
280 | #endif |
281 | rhs = &pl->stk->stk[getArg(p, i)]; |
282 | lhs = &pl->env->stk[getArg(pl->pci, i)]; |
283 | if (VALcopy(lhs, rhs) == NULL) |
284 | return -1; |
285 | } |
286 | return (int) (pl-plants); |
287 | } |
288 | return -1; |
289 | } |
290 | |
291 | str |
292 | yieldFactory(MalBlkPtr mb, InstrPtr p, int pc) |
293 | { |
294 | Plant pl; |
295 | int i; |
296 | |
297 | i = yieldResult(mb, p, pc); |
298 | |
299 | if (i>=0) { |
300 | pl = plants+i; |
301 | pl->pc = pc + 1; |
302 | pl->client = NULL; |
303 | pl->caller = NULL; |
304 | pl->pci = NULL; |
305 | pl->env = NULL; |
306 | return MAL_SUCCEED; |
307 | } |
308 | throw(MAL, "factory.yield" , SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); |
309 | } |
310 | |
311 | /* |
312 | * A return from a factory body implies removal of |
313 | * all state information. |
314 | * This code should also prepare for handling factories |
315 | * that are still running threads in parallel. |
316 | */ |
317 | |
318 | str |
319 | shutdownFactory(Client cntxt, MalBlkPtr mb) |
320 | { |
321 | Plant pl, plim; |
322 | |
323 | plim = plants + lastPlant; |
324 | for (pl = plants; pl < plim; pl++) |
325 | if (pl->factory == mb) { |
326 | /* MSresetVariables(mb, pl->stk, 0);*/ |
327 | /* freeStack(pl->stk); there may be a reference?*/ |
328 | /* we are inside the body of the factory and about to return */ |
329 | pl->factory = 0; |
330 | if (pl->stk) |
331 | pl->stk->keepAlive = FALSE; |
332 | if ( pl->stk) { |
333 | garbageCollector(cntxt, mb, pl->stk,TRUE); |
334 | GDKfree(pl->stk); |
335 | } |
336 | pl->stk=0; |
337 | pl->pc = 0; |
338 | pl->inuse = 0; |
339 | pl->client = NULL; |
340 | pl->caller = NULL; |
341 | pl->pci = NULL; |
342 | pl->env = NULL; |
343 | pl->client = NULL; |
344 | pl->caller = NULL; |
345 | pl->env= NULL; |
346 | pl->pci = NULL; |
347 | } |
348 | return MAL_SUCCEED; |
349 | } |
350 | |
351 | str |
352 | shutdownFactoryByName(Client cntxt, Module m, str nme){ |
353 | Plant pl, plim; |
354 | InstrPtr p; |
355 | Symbol s; |
356 | |
357 | plim = plants + lastPlant; |
358 | for (pl = plants; pl < plim; pl++) |
359 | if (pl->factory ) { |
360 | MalStkPtr stk; |
361 | |
362 | p= getInstrPtr(pl->factory,0); |
363 | if( strcmp(nme, getFunctionId(p)) != 0) continue; |
364 | s = findSymbolInModule(m, nme ); |
365 | if (s == NULL){ |
366 | throw(MAL, "factory.remove" , |
367 | OPERATION_FAILED " SQL entry '%s' not found" , |
368 | putName(nme)); |
369 | } |
370 | stk = pl->stk; |
371 | MSresetVariables(cntxt, pl->factory, stk, 0); |
372 | shutdownFactory(cntxt, pl->factory); |
373 | freeStack(stk); |
374 | deleteSymbol(m,s); |
375 | return MAL_SUCCEED; |
376 | } |
377 | return MAL_SUCCEED; |
378 | } |
379 | |
380 | void mal_factory_reset(void) |
381 | { |
382 | Plant pl, plim; |
383 | |
384 | plim = plants + lastPlant; |
385 | for (pl = plants; pl < plim; pl++){ |
386 | /* MSresetVariables(mb, pl->stk, 0);*/ |
387 | /* freeStack(pl->stk); there may be a reference?*/ |
388 | /* we are inside the body of the factory and about to return */ |
389 | if (pl->stk) { |
390 | pl->stk->keepAlive = FALSE; |
391 | garbageCollector(NULL, pl->factory, pl->stk, TRUE); |
392 | GDKfree(pl->stk); |
393 | } |
394 | pl->factory = 0; |
395 | pl->stk=0; |
396 | pl->pc = 0; |
397 | pl->inuse = 0; |
398 | pl->client = NULL; |
399 | pl->caller = NULL; |
400 | pl->pci = NULL; |
401 | pl->env = NULL; |
402 | pl->client = NULL; |
403 | pl->caller = NULL; |
404 | pl->env= NULL; |
405 | pl->pci = NULL; |
406 | } |
407 | plantId = 1; |
408 | lastPlant = 0; |
409 | } |
410 | |