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
23typedef 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
39static PlantRecord plants[MAXPLANTS];
40static int lastPlant= 0;
41static int plantId = 1;
42
43mal_export Plant newPlant(MalBlkPtr mb);
44
45
46static int
47findPlant(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
55str
56runFactory(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 */
158str
159callFactory(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 */
232Plant
233newPlant(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 */
264int
265yieldResult(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
291str
292yieldFactory(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
318str
319shutdownFactory(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
351str
352shutdownFactoryByName(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
380void 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