1/*
2 * The contents of this file are subject to the Initial
3 * Developer's Public License Version 1.0 (the "License");
4 * you may not use this file except in compliance with the
5 * License. You may obtain a copy of the License at
6 * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
7 *
8 * Software distributed under the License is distributed AS IS,
9 * WITHOUT WARRANTY OF ANY KIND, either express or implied.
10 * See the License for the specific language governing rights
11 * and limitations under the License.
12 *
13 * The Original Code was created by Adriano dos Santos Fernandes
14 * for the Firebird Open Source RDBMS project.
15 *
16 * Copyright (c) 2008 Adriano dos Santos Fernandes <adrianosf@uol.com.br>
17 * and all contributors signed below.
18 *
19 * All Rights Reserved.
20 * Contributor(s): ______________________________________.
21 */
22
23#include "firebird.h"
24#include "../jrd/ibase.h"
25#include "firebird/UdrEngine.h"
26#include "firebird/UdrCppEngine.h"
27#include "firebird/Plugin.h"
28#include "firebird/ExternalEngine.h"
29#include "../common/classes/alloc.h"
30#include "../common/classes/array.h"
31#include "../common/classes/init.h"
32#include "../common/classes/fb_string.h"
33#include "../common/classes/GenericMap.h"
34#include "../common/classes/objects_array.h"
35#include "../common/os/mod_loader.h"
36#include "../common/os/path_utils.h"
37#include "../common/classes/ImplementHelper.h"
38#include "../common/StatusHolder.h"
39
40
41namespace Firebird
42{
43 namespace Udr
44 {
45//------------------------------------------------------------------------------
46
47
48struct Node
49{
50 Node()
51 : name(*getDefaultMemoryPool()),
52 module(*getDefaultMemoryPool())
53 {
54 }
55
56 string name;
57 PathName module;
58};
59
60struct FunctionNode : public Node
61{
62 FunctionFactory* factory;
63 FunctionNode* next;
64};
65
66struct ProcedureNode : public Node
67{
68 ProcedureFactory* factory;
69 ProcedureNode* next;
70};
71
72struct TriggerNode : public Node
73{
74 TriggerFactory* factory;
75 TriggerNode* next;
76};
77
78
79static GlobalPtr<ObjectsArray<PathName> > paths;
80
81class Engine : public StdPlugin<ExternalEngine, FB_EXTERNAL_ENGINE_VERSION>
82{
83public:
84 explicit Engine(IPluginConfig* par)
85 : functions(getPool()),
86 procedures(getPool()),
87 triggers(getPool())
88 {
89 RefPtr<IConfig> defaultConfig(REF_NO_INCR, par->getDefaultConfig());
90
91 if (defaultConfig)
92 {
93 // this plugin is not ready to support different configurations
94 // therefore keep legacy approach
95
96 RefPtr<IConfigEntry> icp;
97
98 for (int n = 0; icp.assignRefNoIncr(defaultConfig->findPos("path", n)); ++n)
99 {
100 PathName newPath(icp->getValue());
101
102 bool found = false;
103
104 for (ObjectsArray<PathName>::iterator i = paths->begin(); i != paths->end(); ++i)
105 {
106 if (*i == newPath)
107 {
108 found = true;
109 break;
110 }
111 }
112
113 if (!found)
114 paths->add(newPath);
115 }
116 }
117 }
118
119 int FB_CARG release()
120 {
121 if (--refCounter == 0)
122 {
123 delete this;
124 return 0;
125 }
126
127 return 1;
128 }
129
130public:
131 void loadModule(const IRoutineMetadata* metadata, PathName* moduleName, string* entryPoint);
132 template <typename NodeType, typename ObjType, typename SharedObjType> ObjType* getChild(
133 IStatus* status, GenericMap<Pair<NonPooled<ExternalContext*, ObjType*> > >& children,
134 SharedObjType* sharedObj, ExternalContext* context, NodeType* nodes,
135 SortedArray<SharedObjType*>& sharedObjs, const PathName& moduleName);
136 template <typename ObjType> void deleteChildren(
137 GenericMap<Pair<NonPooled<ExternalContext*, ObjType*> > >& children);
138
139 template <typename T> T* findNode(T* nodes, const PathName& moduleName,
140 const string& entryPoint);
141
142private:
143 template <typename T, typename T2> T2* getNode(IStatus* status, T* nodes,
144 const PathName& moduleName, ExternalContext* context, const IRoutineMetadata* metadata,
145 const string& entryPoint);
146
147public:
148 virtual void FB_CARG open(IStatus* status, ExternalContext* context, Utf8* name, uint nameSize);
149 virtual void FB_CARG openAttachment(IStatus* status, ExternalContext* context);
150 virtual void FB_CARG closeAttachment(IStatus* status, ExternalContext* context);
151 virtual ExternalFunction* FB_CARG makeFunction(IStatus* status, ExternalContext* context,
152 const IRoutineMetadata* metadata, IMetadataBuilder* inBuilder, IMetadataBuilder* outBuilder);
153 virtual ExternalProcedure* FB_CARG makeProcedure(IStatus* status, ExternalContext* context,
154 const IRoutineMetadata* metadata, IMetadataBuilder* inBuilder, IMetadataBuilder* outBuilder);
155 virtual ExternalTrigger* FB_CARG makeTrigger(IStatus* status, ExternalContext* context,
156 const IRoutineMetadata* metadata, IMetadataBuilder* fieldsBuilder);
157
158public:
159 virtual void FB_CARG dispose();
160
161private:
162 Mutex childrenMutex;
163
164public:
165 SortedArray<class SharedFunction*> functions;
166 SortedArray<class SharedProcedure*> procedures;
167 SortedArray<class SharedTrigger*> triggers;
168};
169
170
171class ModulesMap : public GenericMap<Pair<Left<PathName, ModuleLoader::Module*> > >
172{
173public:
174 explicit ModulesMap(MemoryPool& p)
175 : GenericMap<Pair<Left<PathName, ModuleLoader::Module*> > >(p)
176 {
177 }
178
179 ~ModulesMap();
180};
181
182
183//--------------------------------------
184
185
186static AutoPtr<ModuleLoader::Module> libraryModule;
187
188static GlobalPtr<Mutex> modulesMutex;
189static GlobalPtr<ModulesMap> modules;
190
191static InitInstance<PathName> loadingModule;
192static FunctionNode* registeredFunctions = NULL;
193static ProcedureNode* registeredProcedures = NULL;
194static TriggerNode* registeredTriggers = NULL;
195
196
197//--------------------------------------
198
199
200class SharedFunction : public DisposeIface<ExternalFunction, FB_EXTERNAL_FUNCTION_VERSION>
201{
202public:
203 SharedFunction(IStatus* status, Engine* aEngine, ExternalContext* context,
204 const IRoutineMetadata* aMetadata,
205 IMetadataBuilder* inBuilder, IMetadataBuilder* outBuilder)
206 : engine(aEngine),
207 metadata(aMetadata),
208 moduleName(*getDefaultMemoryPool()),
209 entryPoint(*getDefaultMemoryPool()),
210 info(*getDefaultMemoryPool()),
211 children(*getDefaultMemoryPool())
212 {
213 engine->loadModule(metadata, &moduleName, &entryPoint);
214 FunctionNode* node = engine->findNode<FunctionNode>(
215 registeredFunctions, moduleName, entryPoint);
216 node->factory->setup(status, context, metadata, inBuilder, outBuilder);
217 }
218
219 ~SharedFunction()
220 {
221 engine->deleteChildren(children);
222 }
223
224public:
225 virtual void FB_CARG dispose()
226 {
227 delete this;
228 }
229
230public:
231 virtual void FB_CARG getCharSet(IStatus* status, ExternalContext* context,
232 Utf8* name, uint nameSize)
233 {
234 strncpy(name, context->getClientCharSet(), nameSize);
235
236 try
237 {
238 ExternalFunction* function = engine->getChild<FunctionNode, ExternalFunction>(status,
239 children, this, context, registeredFunctions, engine->functions, moduleName);
240 if (function)
241 function->getCharSet(status, context, name, nameSize);
242 }
243 catch (const StatusException& e)
244 {
245 e.stuff(status);
246 }
247 }
248
249 virtual void FB_CARG execute(IStatus* status, ExternalContext* context, void* inMsg, void* outMsg)
250 {
251 ExternalFunction* function = engine->getChild<FunctionNode, ExternalFunction>(status,
252 children, this, context, registeredFunctions, engine->functions, moduleName);
253 if (function)
254 function->execute(status, context, inMsg, outMsg);
255 }
256
257public:
258 Engine* engine;
259 const IRoutineMetadata* metadata;
260 PathName moduleName;
261 string entryPoint;
262 string info;
263 GenericMap<Pair<NonPooled<ExternalContext*, ExternalFunction*> > > children;
264};
265
266
267//--------------------------------------
268
269
270class SharedProcedure : public DisposeIface<ExternalProcedure, FB_EXTERNAL_PROCEDURE_VERSION>
271{
272public:
273 SharedProcedure(IStatus* status, Engine* aEngine, ExternalContext* context,
274 const IRoutineMetadata* aMetadata,
275 IMetadataBuilder* inBuilder, IMetadataBuilder* outBuilder)
276 : engine(aEngine),
277 metadata(aMetadata),
278 moduleName(*getDefaultMemoryPool()),
279 entryPoint(*getDefaultMemoryPool()),
280 info(*getDefaultMemoryPool()),
281 children(*getDefaultMemoryPool())
282 {
283 engine->loadModule(metadata, &moduleName, &entryPoint);
284 ProcedureNode* node = engine->findNode<ProcedureNode>(
285 registeredProcedures, moduleName, entryPoint);
286 node->factory->setup(status, context, metadata, inBuilder, outBuilder);
287 }
288
289 ~SharedProcedure()
290 {
291 engine->deleteChildren(children);
292 }
293
294public:
295 virtual void FB_CARG dispose()
296 {
297 delete this;
298 }
299
300public:
301 virtual void FB_CARG getCharSet(IStatus* status, ExternalContext* context,
302 Utf8* name, uint nameSize)
303 {
304 strncpy(name, context->getClientCharSet(), nameSize);
305
306 try
307 {
308 ExternalProcedure* procedure = engine->getChild<ProcedureNode, ExternalProcedure>(status,
309 children, this, context, registeredProcedures, engine->procedures, moduleName);
310 if (procedure)
311 procedure->getCharSet(status, context, name, nameSize);
312 }
313 catch (const StatusException& e)
314 {
315 e.stuff(status);
316 }
317 }
318
319 virtual ExternalResultSet* FB_CARG open(IStatus* status, ExternalContext* context,
320 void* inMsg, void* outMsg)
321 {
322 try
323 {
324 ExternalProcedure* procedure = engine->getChild<ProcedureNode, ExternalProcedure>(status,
325 children, this, context, registeredProcedures, engine->procedures, moduleName);
326 return procedure ? procedure->open(status, context, inMsg, outMsg) : NULL;
327 }
328 catch (const StatusException& e)
329 {
330 e.stuff(status);
331 return NULL;
332 }
333 }
334
335public:
336 Engine* engine;
337 const IRoutineMetadata* metadata;
338 PathName moduleName;
339 string entryPoint;
340 string info;
341 GenericMap<Pair<NonPooled<ExternalContext*, ExternalProcedure*> > > children;
342};
343
344
345//--------------------------------------
346
347
348class SharedTrigger : public DisposeIface<ExternalTrigger, FB_EXTERNAL_TRIGGER_VERSION>
349{
350public:
351 SharedTrigger(IStatus* status, Engine* aEngine, ExternalContext* context,
352 const IRoutineMetadata* aMetadata, IMetadataBuilder* fieldsBuilder)
353 : engine(aEngine),
354 metadata(aMetadata),
355 moduleName(*getDefaultMemoryPool()),
356 entryPoint(*getDefaultMemoryPool()),
357 info(*getDefaultMemoryPool()),
358 children(*getDefaultMemoryPool())
359 {
360 engine->loadModule(metadata, &moduleName, &entryPoint);
361 TriggerNode* node = engine->findNode<TriggerNode>(registeredTriggers, moduleName, entryPoint);
362 node->factory->setup(status, context, metadata, fieldsBuilder);
363 }
364
365 ~SharedTrigger()
366 {
367 engine->deleteChildren(children);
368 }
369
370public:
371 virtual void FB_CARG dispose()
372 {
373 delete this;
374 }
375
376public:
377 virtual void FB_CARG getCharSet(IStatus* status, ExternalContext* context,
378 Utf8* name, uint nameSize)
379 {
380 strncpy(name, context->getClientCharSet(), nameSize);
381
382 try
383 {
384 ExternalTrigger* trigger = engine->getChild<TriggerNode, ExternalTrigger>(status,
385 children, this, context, registeredTriggers, engine->triggers, moduleName);
386 if (trigger)
387 trigger->getCharSet(status, context, name, nameSize);
388 }
389 catch (const StatusException& e)
390 {
391 e.stuff(status);
392 }
393 }
394
395 virtual void FB_CARG execute(IStatus* status, ExternalContext* context,
396 ExternalTrigger::Action action, void* oldMsg, void* newMsg)
397 {
398 ExternalTrigger* trigger = engine->getChild<TriggerNode, ExternalTrigger>(status,
399 children, this, context, registeredTriggers, engine->triggers, moduleName);
400 if (trigger)
401 trigger->execute(status, context, action, oldMsg, newMsg);
402 }
403
404public:
405 Engine* engine;
406 const IRoutineMetadata* metadata;
407 PathName moduleName;
408 string entryPoint;
409 string info;
410 GenericMap<Pair<NonPooled<ExternalContext*, ExternalTrigger*> > > children;
411};
412
413
414//--------------------------------------
415
416
417extern "C" void fbUdrRegFunction(const char* name, FunctionFactory* factory)
418{
419 FunctionNode* node = new FunctionNode();
420 node->name = name;
421 node->module = loadingModule();
422 node->factory = factory;
423 node->next = registeredFunctions;
424 registeredFunctions = node;
425}
426
427
428extern "C" void fbUdrRegProcedure(const char* name, ProcedureFactory* factory)
429{
430 ProcedureNode* node = new ProcedureNode();
431 node->name = name;
432 node->module = loadingModule();
433 node->factory = factory;
434 node->next = registeredProcedures;
435 registeredProcedures = node;
436}
437
438
439extern "C" void fbUdrRegTrigger(const char* name, TriggerFactory* factory)
440{
441 TriggerNode* node = new TriggerNode();
442 node->name = name;
443 node->module = loadingModule();
444 node->factory = factory;
445 node->next = registeredTriggers;
446 registeredTriggers = node;
447}
448
449
450ModulesMap::~ModulesMap()
451{
452 while (registeredFunctions)
453 {
454 FunctionNode* del = registeredFunctions;
455 registeredFunctions = registeredFunctions->next;
456 delete del;
457 }
458
459 while (registeredProcedures)
460 {
461 ProcedureNode* del = registeredProcedures;
462 registeredProcedures = registeredProcedures->next;
463 delete del;
464 }
465
466 while (registeredTriggers)
467 {
468 TriggerNode* del = registeredTriggers;
469 registeredTriggers = registeredTriggers->next;
470 delete del;
471 }
472
473 Accessor accessor(this);
474 for (bool cont = accessor.getFirst(); cont; cont = accessor.getNext())
475 delete accessor.current()->second;
476}
477
478
479//--------------------------------------
480
481
482void Engine::loadModule(const IRoutineMetadata* metadata, PathName* moduleName, string* entryPoint)
483{
484 LocalStatus status;
485 const string str(metadata->getEntryPoint(&status));
486 StatusException::check(status.get());
487
488 const size_t pos = str.find('!');
489 if (pos == string::npos)
490 {
491 static const ISC_STATUS statusVector[] = {
492 isc_arg_gds,
493 isc_random,
494 isc_arg_string, (ISC_STATUS) "Invalid entry point",
495 isc_arg_end
496 };
497
498 StatusException::check(statusVector);
499 }
500
501 *moduleName = PathName(str.substr(0, pos).c_str());
502 // Do not allow module names with directory separators as a security measure.
503 if (moduleName->find_first_of("/\\") != string::npos)
504 {
505 static const ISC_STATUS statusVector[] = {
506 isc_arg_gds,
507 isc_random,
508 isc_arg_string, (ISC_STATUS) "Invalid module name",
509 isc_arg_end
510 };
511
512 StatusException::check(statusVector);
513 }
514
515 *entryPoint = str.substr(pos + 1);
516
517 size_t n = entryPoint->find('!');
518 *entryPoint = (n == string::npos ? *entryPoint : entryPoint->substr(0, n));
519
520 MutexLockGuard guard(modulesMutex, FB_FUNCTION);
521
522 if (modules->exist(*moduleName))
523 return;
524
525 loadingModule() = *moduleName;
526
527 for (ObjectsArray<PathName>::iterator i = paths->begin(); i != paths->end(); ++i)
528 {
529 PathName path;
530 PathUtils::concatPath(path, *i, *moduleName);
531
532 ModuleLoader::Module* module = ModuleLoader::fixAndLoadModule(path);
533
534 if (module)
535 {
536 modules->put(*moduleName, module);
537 break;
538 }
539 else
540 {
541 static const ISC_STATUS statusVector[] = {
542 isc_arg_gds,
543 isc_random,
544 isc_arg_string, (ISC_STATUS) "Module not found",
545 isc_arg_end
546 };
547
548 StatusException::check(statusVector);
549 }
550 }
551}
552
553
554template <typename NodeType, typename ObjType, typename SharedObjType> ObjType* Engine::getChild(
555 IStatus* status, GenericMap<Pair<NonPooled<ExternalContext*, ObjType*> > >& children,
556 SharedObjType* sharedObj, ExternalContext* context, NodeType* nodes,
557 SortedArray<SharedObjType*>& sharedObjs, const PathName& moduleName)
558{
559 MutexLockGuard guard(childrenMutex, FB_FUNCTION);
560
561 if (!sharedObjs.exist(sharedObj))
562 sharedObjs.add(sharedObj);
563
564 ObjType* obj;
565 if (!children.get(context, obj))
566 {
567 obj = getNode<NodeType, ObjType>(status, nodes, moduleName, context, sharedObj->metadata,
568 sharedObj->entryPoint);
569
570 if (obj)
571 children.put(context, obj);
572 }
573
574 return obj;
575}
576
577
578template <typename ObjType> void Engine::deleteChildren(
579 GenericMap<Pair<NonPooled<ExternalContext*, ObjType*> > >& children)
580{
581 // No need to lock childrenMutex as if there are more threads simultaneously accessing
582 // these children in this moment there will be a memory corruption anyway.
583
584 typedef typename GenericMap<Pair<NonPooled<ExternalContext*, ObjType*> > >::Accessor ChildrenAccessor;
585 ChildrenAccessor accessor(&children);
586 for (bool found = accessor.getFirst(); found; found = accessor.getNext())
587 delete accessor.current()->second;
588}
589
590
591template <typename T> T* Engine::findNode(T* nodes, const PathName& moduleName,
592 const string& entryPoint)
593{
594 for (T* node = nodes; node; node = node->next)
595 {
596 if (node->module == moduleName && entryPoint == node->name)
597 return node;
598 }
599
600 static const ISC_STATUS statusVector[] = {
601 isc_arg_gds,
602 isc_random,
603 isc_arg_string, (ISC_STATUS) "Entry point not found",
604 isc_arg_end
605 };
606
607 StatusException::check(statusVector);
608
609 return NULL;
610}
611
612
613template <typename T, typename T2> T2* Engine::getNode(IStatus* status, T* nodes,
614 const PathName& moduleName, ExternalContext* context, const IRoutineMetadata* metadata,
615 const string& entryPoint)
616{
617 T* node = findNode<T>(nodes, moduleName, entryPoint);
618 return node->factory->newItem(status, context, metadata);
619}
620
621
622void FB_CARG Engine::open(IStatus* /*status*/, ExternalContext* /*context*/, Utf8* name, uint nameSize)
623{
624 strncpy(name, "UTF-8", nameSize);
625}
626
627
628void FB_CARG Engine::openAttachment(IStatus* /*status*/, ExternalContext* /*context*/)
629{
630}
631
632
633void FB_CARG Engine::closeAttachment(IStatus* status, ExternalContext* context)
634{
635 MutexLockGuard guard(childrenMutex, FB_FUNCTION);
636
637 for (SortedArray<SharedFunction*>::iterator i = functions.begin(); i != functions.end(); ++i)
638 {
639 ExternalFunction* function;
640 if ((*i)->children.get(context, function))
641 {
642 function->dispose();
643 (*i)->children.remove(context);
644 }
645 }
646
647 for (SortedArray<SharedProcedure*>::iterator i = procedures.begin(); i != procedures.end(); ++i)
648 {
649 ExternalProcedure* procedure;
650 if ((*i)->children.get(context, procedure))
651 {
652 procedure->dispose();
653 (*i)->children.remove(context);
654 }
655 }
656
657 for (SortedArray<SharedTrigger*>::iterator i = triggers.begin(); i != triggers.end(); ++i)
658 {
659 ExternalTrigger* trigger;
660 if ((*i)->children.get(context, trigger))
661 {
662 trigger->dispose();
663 (*i)->children.remove(context);
664 }
665 }
666}
667
668
669ExternalFunction* FB_CARG Engine::makeFunction(IStatus* status, ExternalContext* context,
670 const IRoutineMetadata* metadata, IMetadataBuilder* inBuilder, IMetadataBuilder* outBuilder)
671{
672 try
673 {
674 return new SharedFunction(status, this, context, metadata, inBuilder, outBuilder);
675 }
676 catch (const StatusException& e)
677 {
678 e.stuff(status);
679 return NULL;
680 }
681}
682
683
684ExternalProcedure* FB_CARG Engine::makeProcedure(IStatus* status, ExternalContext* context,
685 const IRoutineMetadata* metadata, IMetadataBuilder* inBuilder, IMetadataBuilder* outBuilder)
686{
687 try
688 {
689 return new SharedProcedure(status, this, context, metadata, inBuilder, outBuilder);
690 }
691 catch (const StatusException& e)
692 {
693 e.stuff(status);
694 return NULL;
695 }
696}
697
698
699ExternalTrigger* FB_CARG Engine::makeTrigger(IStatus* status, ExternalContext* context,
700 const IRoutineMetadata* metadata, IMetadataBuilder* fieldsBuilder)
701{
702 try
703 {
704 return new SharedTrigger(status, this, context, metadata, fieldsBuilder);
705 }
706 catch (const StatusException& e)
707 {
708 e.stuff(status);
709 return NULL;
710 }
711}
712
713
714void FB_CARG Engine::dispose()
715{
716 delete this;
717}
718
719
720//--------------------------------------
721
722
723class ExternalEngineFactoryImpl : public SimpleFactory<Engine>
724{
725} factory;
726
727extern "C" void FB_PLUGIN_ENTRY_POINT(IMaster* master)
728{
729 CachedMasterInterface::set(master);
730
731 PluginManagerInterfacePtr pi;
732 pi->registerPluginFactory(PluginType::ExternalEngine, "UDR", &factory);
733 myModule->registerMe();
734
735 PathName libraryName("fbclient");
736 ModuleLoader::doctorModuleExtension(libraryName);
737
738 libraryModule.reset(ModuleLoader::loadModule(libraryName));
739}
740
741
742//------------------------------------------------------------------------------
743 } // namespace Udr
744} // namespace Firebird
745