8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-25 00:03:03 +01:00
firebird-mirror/src/jrd/ExtEngineManager.cpp

1035 lines
25 KiB
C++
Raw Normal View History

/*
* The contents of this file are subject to the Initial
* Developer's Public License Version 1.0 (the "License");
* you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
* http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
*
* Software distributed under the License is distributed AS IS,
* WITHOUT WARRANTY OF ANY KIND, either express or implied.
* See the License for the specific language governing rights
* and limitations under the License.
*
* The Original Code was created by Adriano dos Santos Fernandes
* for the Firebird Open Source RDBMS project.
*
* Copyright (c) 2008 Adriano dos Santos Fernandes <adrianosf@uol.com.br>
* and all contributors signed below.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*/
#include "firebird.h"
#include "consts_pub.h"
#include "iberror.h"
#include "inf_pub.h"
#include "../jrd/ExtEngineManager.h"
#include "../jrd/ErrorImpl.h"
#include "../jrd/ValueImpl.h"
#include "../jrd/ValuesImpl.h"
#include "../dsql/sqlda_pub.h"
#include "../jrd/dsc.h"
#include "../jrd/jrd.h"
#include "../jrd/exe.h"
#include "../jrd/req.h"
#include "../jrd/status.h"
#include "../jrd/tra.h"
#include "../jrd/PluginManager.h"
#include "../jrd/ibase.h"
#include "../jrd/os/path_utils.h"
#include "../jrd/cvt_proto.h"
#include "../jrd/evl_proto.h"
#include "../jrd/intl_proto.h"
#include "../jrd/met_proto.h"
#include "../jrd/mov_proto.h"
#include "../jrd/thread_proto.h"
#include "../common/classes/auto.h"
#include "../common/classes/fb_string.h"
#include "../common/classes/init.h"
#include "../common/classes/objects_array.h"
#include "../common/config/config.h"
#include "../config/ConfigFile.h"
#include "../config/ConfObj.h"
#include "../config/ConfObject.h"
#include "../config/Element.h"
#include "../config/ScanDir.h"
#include "../config/AdminException.h"
using namespace Firebird;
namespace Jrd {
class ExtEngineManager::AttachmentImpl : public Firebird::Attachment
{
public:
AttachmentImpl(ExternalContextImpl* aContext, Handle aHandle, Jrd::Attachment* aAttachment);
virtual ~AttachmentImpl();
public:
virtual void FB_CALL dispose(Error* error);
virtual Handle FB_CALL getHandle(Error* error) const;
virtual const char* FB_CALL getUserName() const;
virtual const char* FB_CALL getDatabaseName() const;
private:
ExternalContextImpl* context;
FB_API_HANDLE handle;
Jrd::Attachment* attachment;
};
class ExtEngineManager::TransactionImpl : public Firebird::Transaction
{
public:
TransactionImpl(Handle aHandle);
virtual ~TransactionImpl();
public:
virtual Handle FB_CALL getHandle(Error* error) const;
private:
FB_API_HANDLE handle;
};
template <typename T> class ExtEngineManager::ContextManager
{
public:
ContextManager(thread_db* tdbb, EngineAttachmentInfo* aAttInfo, T* obj,
CallerName aCallerName = CallerName())
: attInfo(aAttInfo),
attachment(tdbb->getAttachment()),
transaction(tdbb->getTransaction()),
charSet(attachment->att_charset),
attInUse(attachment->att_in_use),
traInUse(transaction ? transaction->tra_in_use : false)
{
attachment->att_in_use = true;
if (transaction)
{
callerName = transaction->tra_caller_name;
transaction->tra_caller_name = aCallerName;
++transaction->tra_callback_count;
transaction->tra_in_use = true;
}
attInfo->context->setTransaction(tdbb);
setCharSet(tdbb, attInfo, obj);
}
ContextManager(thread_db* tdbb, EngineAttachmentInfo* aAttInfo, USHORT aCharSet,
CallerName aCallerName = CallerName())
: attInfo(aAttInfo),
attachment(tdbb->getAttachment()),
transaction(tdbb->getTransaction()),
charSet(attachment->att_charset),
attInUse(attachment->att_in_use),
traInUse(transaction ? transaction->tra_in_use : false)
{
attachment->att_charset = aCharSet;
attachment->att_in_use = true;
if (transaction)
{
callerName = transaction->tra_caller_name;
transaction->tra_caller_name = aCallerName;
++transaction->tra_callback_count;
transaction->tra_in_use = true;
}
attInfo->context->setTransaction(tdbb);
}
~ContextManager()
{
if (transaction)
{
--transaction->tra_callback_count;
transaction->tra_in_use = traInUse;
transaction->tra_caller_name = callerName;
}
attachment->att_in_use = attInUse;
attachment->att_charset = charSet;
}
private:
void setCharSet(thread_db* tdbb, EngineAttachmentInfo* attInfo, T* obj)
{
attachment->att_charset = attInfo->adminCharSet;
if (!obj)
return;
Utf8 charSetName[MAX_SQL_IDENTIFIER_SIZE];
{ // scope
Database::Checkout dcoHolder(tdbb->getDatabase());
2009-10-30 11:50:59 +01:00
obj->getCharSet(RaiseError(), attInfo->context, charSetName, MAX_SQL_IDENTIFIER_LEN);
charSetName[MAX_SQL_IDENTIFIER_LEN] = '\0';
}
USHORT charSetId;
if (!MET_get_char_coll_subtype(tdbb, &charSetId,
reinterpret_cast<const UCHAR*>(charSetName), strlen(charSetName)))
{
status_exception::raise(Arg::Gds(isc_charset_not_found) << Arg::Str(charSetName));
}
attachment->att_charset = charSetId;
}
private:
EngineAttachmentInfo* attInfo;
Jrd::Attachment* attachment;
jrd_tra* transaction;
2009-10-30 11:50:59 +01:00
// These data members are to restore the original information.
const USHORT charSet;
const bool attInUse;
const bool traInUse;
CallerName callerName;
};
//---------------------
ExtEngineManager::AttachmentImpl::AttachmentImpl(ExternalContextImpl* aContext, Handle aHandle,
Jrd::Attachment* aAttachment)
: context(aContext),
handle(aHandle),
attachment(aAttachment)
{
}
ExtEngineManager::AttachmentImpl::~AttachmentImpl()
{
context->attachment.release();
handle = 0;
dispose(LogError());
}
void FB_CALL ExtEngineManager::AttachmentImpl::dispose(Error* error)
{
ISC_STATUS_ARRAY statusVector;
if (handle)
{
if (isc_detach_database(statusVector, &handle) != 0)
{
ErrorImpl::statusVectorToError(statusVector, error);
return;
}
}
context->attachment = NULL;
}
2009-11-05 09:29:33 +01:00
Handle FB_CALL ExtEngineManager::AttachmentImpl::getHandle(Error* /*error*/) const
{
return handle;
}
const char* FB_CALL ExtEngineManager::AttachmentImpl::getUserName() const
{
return attachment->att_user->usr_user_name.c_str();
}
const char* FB_CALL ExtEngineManager::AttachmentImpl::getDatabaseName() const
{
return attachment->att_database->dbb_database_name.c_str();
}
//---------------------
ExtEngineManager::TransactionImpl::TransactionImpl(Handle aHandle)
: handle(aHandle)
{
}
ExtEngineManager::TransactionImpl::~TransactionImpl()
{
}
2009-11-05 09:29:33 +01:00
Handle FB_CALL ExtEngineManager::TransactionImpl::getHandle(Error* /*error*/) const
{
return handle;
}
//---------------------
ExtEngineManager::ExternalContextImpl::ExternalContextImpl(thread_db* tdbb,
ExternalEngine* aEngine)
: engine(aEngine),
internalAttachment(tdbb->getAttachment()),
miscInfo(*internalAttachment->att_pool),
traHandle(0)
{
//// TODO: admin rights
attHandle = internalAttachment->att_public_handle;
clientCharSet = INTL_charset_lookup(tdbb, internalAttachment->att_client_charset)->getName();
setTransaction(tdbb);
}
ExtEngineManager::ExternalContextImpl::~ExternalContextImpl()
{
releaseTransaction();
}
void ExtEngineManager::ExternalContextImpl::releaseTransaction()
{
if (traHandle)
{
traHandle = 0;
transaction = NULL;
}
}
void ExtEngineManager::ExternalContextImpl::setTransaction(thread_db* tdbb)
{
releaseTransaction();
jrd_tra* tra = tdbb->getTransaction();
traHandle = tra ? tra->tra_public_handle : 0;
transaction = FB_NEW(*internalAttachment->att_pool) TransactionImpl(traHandle);
}
2009-11-05 09:29:33 +01:00
ExternalEngine* ExtEngineManager::ExternalContextImpl::getEngine(Firebird::Error* /*error*/)
{
return engine;
}
2009-11-05 09:29:33 +01:00
Firebird::Attachment* FB_CALL ExtEngineManager::ExternalContextImpl::getAttachment(Error* /*error*/)
{
if (!this->attachment)
{
thread_db* tdbb = JRD_get_thread_data();
attachment = FB_NEW(*internalAttachment->att_pool) AttachmentImpl(this, attHandle,
tdbb->getAttachment());
}
return attachment;
}
2009-11-05 09:29:33 +01:00
Firebird::Transaction* FB_CALL ExtEngineManager::ExternalContextImpl::getTransaction(Error* /*error*/)
{
return transaction;
}
const Firebird::Utf8* FB_CALL ExtEngineManager::ExternalContextImpl::getClientCharSet()
{
return clientCharSet.c_str();
}
int FB_CALL ExtEngineManager::ExternalContextImpl::obtainInfoCode()
{
static AtomicCounter counter;
return ++counter;
}
void* FB_CALL ExtEngineManager::ExternalContextImpl::getInfo(int code)
{
void* value = NULL;
miscInfo.get(code, value);
return value;
}
void* FB_CALL ExtEngineManager::ExternalContextImpl::setInfo(int code, void* value)
{
void* oldValue = getInfo(code);
miscInfo.put(code, value);
return oldValue;
}
//---------------------
static InitInstance<GenericMap<Pair<Full<MetaName, string> > > > enginesModules;
//---------------------
ExtEngineManager::Function::Function(thread_db* tdbb, ExtEngineManager* aExtManager,
ExternalEngine* aEngine, Firebird::ExternalFunction* aFunction,
const UserFunction* aUdf)
: extManager(aExtManager),
engine(aEngine),
function(aFunction),
udf(aUdf),
database(tdbb->getDatabase())
{
}
ExtEngineManager::Function::~Function()
{
Database::Checkout dcoHolder(database);
function->dispose(LogError());
}
void ExtEngineManager::Function::execute(thread_db* tdbb, jrd_nod* args, impure_value* impure)
{
EngineAttachmentInfo* attInfo = extManager->getEngineAttachment(tdbb, engine);
ContextManager<ExternalFunction> ctxManager(tdbb, attInfo,
function); // CallerName(obj_udf, function->fun_name)
impure->vlu_desc.dsc_flags = DSC_null;
2009-10-22 01:48:07 +02:00
MemoryPool& pool = *tdbb->getDefaultPool();
ValueImpl result(pool, &impure->vlu_desc, "", true);
Firebird::HalfStaticArray<impure_value, 32> impureArgs;
impure_value* impureArgsPtr = impureArgs.getBuffer(args->nod_count);
try
{
2009-10-22 01:48:07 +02:00
ValuesImpl params(pool, args->nod_count);
for (int i = 0; i < args->nod_count; ++i)
{
impureArgsPtr->vlu_desc = udf->fun_rpt[i + 1].fun_desc;
if (impureArgsPtr->vlu_desc.isText())
{
2009-10-22 01:48:07 +02:00
impureArgsPtr->vlu_string =
FB_NEW_RPT(pool, impureArgsPtr->vlu_desc.getStringLength()) VaryingString();
impureArgsPtr->vlu_desc.dsc_address = (UCHAR*) impureArgsPtr->vlu_string;
}
else
{
impureArgsPtr->vlu_string = NULL;
impureArgsPtr->vlu_desc.dsc_address = (UCHAR*) &impureArgsPtr->vlu_misc;
}
dsc* arg = EVL_expr(tdbb, args->nod_arg[i]);
if (tdbb->getRequest()->req_flags & req_null)
impureArgsPtr->vlu_desc.dsc_flags = DSC_null;
else
{
MOV_move(tdbb, arg, &impureArgsPtr->vlu_desc);
INTL_adjust_text_descriptor(tdbb, &impureArgsPtr->vlu_desc);
}
params.getValue(i + 1)->make(&impureArgsPtr->vlu_desc, "", true);
++impureArgsPtr;
}
{ // scope
Database::Checkout dcoHolder(tdbb->getDatabase());
function->execute(RaiseError(), attInfo->context, &params, &result);
}
}
catch (...)
{
for (int i = 0; i < args->nod_count; ++i)
delete impureArgs[i].vlu_string;
throw;
}
for (int i = 0; i < args->nod_count; ++i)
delete impureArgs[i].vlu_string;
if (result.isNull())
tdbb->getRequest()->req_flags |= req_null;
else
tdbb->getRequest()->req_flags &= ~req_null;
}
//---------------------
ExtEngineManager::Procedure::Procedure(thread_db* tdbb, ExtEngineManager* aExtManager,
ExternalEngine* aEngine, Firebird::ExternalProcedure* aProcedure,
const jrd_prc* aPrc)
: extManager(aExtManager),
engine(aEngine),
procedure(aProcedure),
prc(aPrc),
database(tdbb->getDatabase())
{
}
ExtEngineManager::Procedure::~Procedure()
{
Database::Checkout dcoHolder(database);
procedure->dispose(LogError());
}
ExtEngineManager::ResultSet* ExtEngineManager::Procedure::open(thread_db* tdbb,
ValuesImpl* inputParams, ValuesImpl* outputParams)
{
return FB_NEW(*tdbb->getDefaultPool()) ResultSet(tdbb, inputParams, outputParams, this);
}
//---------------------
ExtEngineManager::ResultSet::ResultSet(thread_db* tdbb, ValuesImpl* inputParams,
ValuesImpl* outputParams, ExtEngineManager::Procedure* aProcedure)
: procedure(aProcedure),
database(tdbb->getDatabase()),
firstFetch(true)
{
attInfo = procedure->extManager->getEngineAttachment(tdbb, procedure->engine);
ContextManager<ExternalProcedure> ctxManager(tdbb, attInfo, procedure->procedure,
(procedure->prc->prc_name.qualifier.isEmpty() ?
CallerName(obj_procedure, procedure->prc->prc_name.identifier) :
CallerName(obj_package_header, procedure->prc->prc_name.qualifier)));
Attachment* attachment = tdbb->getAttachment();
charSet = attachment->att_charset;
Database::Checkout dcoHolder(tdbb->getDatabase());
resultSet = procedure->procedure->open(RaiseError(), attInfo->context, inputParams,
outputParams);
}
ExtEngineManager::ResultSet::~ResultSet()
{
if (resultSet)
{
Database::Checkout dcoHolder(database);
resultSet->dispose(LogError());
}
}
bool ExtEngineManager::ResultSet::fetch(thread_db* tdbb)
{
bool wasFirstFetch = firstFetch;
firstFetch = false;
if (!resultSet)
return wasFirstFetch;
ContextManager<ExternalProcedure> ctxManager(tdbb, attInfo, charSet,
(procedure->prc->prc_name.qualifier.isEmpty() ?
CallerName(obj_procedure, procedure->prc->prc_name.identifier) :
CallerName(obj_package_header, procedure->prc->prc_name.qualifier)));
Database::Checkout dcoHolder(tdbb->getDatabase());
return resultSet->fetch(RaiseError());
}
//---------------------
ExtEngineManager::Trigger::Trigger(thread_db* tdbb, ExtEngineManager* aExtManager,
ExternalEngine* aEngine, Firebird::ExternalTrigger* aTrigger,
const Jrd::Trigger* aTrg)
: extManager(aExtManager),
engine(aEngine),
trigger(aTrigger),
trg(aTrg),
database(tdbb->getDatabase())
{
}
ExtEngineManager::Trigger::~Trigger()
{
}
void ExtEngineManager::Trigger::execute(thread_db* tdbb, Firebird::ExternalTrigger::Action action,
record_param* oldRpb, record_param* newRpb)
{
EngineAttachmentInfo* attInfo = extManager->getEngineAttachment(tdbb, engine);
ContextManager<ExternalTrigger> ctxManager(tdbb, attInfo, trigger,
CallerName(obj_trigger, trg->name));
Array<dsc*> descs;
try
{
2009-10-22 01:48:07 +02:00
MemoryPool& pool = *tdbb->getDefaultPool();
AutoPtr<ValuesImpl> oldValues, newValues;
int valueOldCount = 0;
int valueNewCount = 0;
if (oldRpb)
2009-10-31 02:46:06 +01:00
valueOldCount = setValues(tdbb, pool, oldValues, descs, oldRpb);
if (newRpb)
2009-10-31 02:46:06 +01:00
valueNewCount = setValues(tdbb, pool, newValues, descs, newRpb);
{ // scope
Database::Checkout dcoHolder(tdbb->getDatabase());
trigger->execute(RaiseError(), attInfo->context, action, oldValues, newValues);
for (int i = 1; i <= valueNewCount; ++i)
{
ValueImpl* val = newValues->getValue(i);
if (val->isNull())
SET_NULL(newRpb->rpb_record, val->getFieldNumber());
else
CLEAR_NULL(newRpb->rpb_record, val->getFieldNumber());
}
}
}
catch (...)
{
for (size_t i = 0; i < descs.getCount(); ++i)
delete descs[i];
throw;
}
for (size_t i = 0; i < descs.getCount(); ++i)
delete descs[i];
}
2009-10-22 01:48:07 +02:00
int ExtEngineManager::Trigger::setValues(thread_db* tdbb, MemoryPool& pool,
2009-10-31 02:46:06 +01:00
AutoPtr<ValuesImpl>& values, Array<dsc*>& descs,
2009-10-22 01:48:07 +02:00
record_param* rpb)
{
if (!rpb || !rpb->rpb_record)
return 0;
Record* record = rpb->rpb_record;
const Format* format = record->rec_format;
2009-10-22 01:48:07 +02:00
values = FB_NEW(pool) ValuesImpl(pool, format->fmt_count);
int start = descs.getCount();
descs.resize(start + format->fmt_count);
int j = 0;
for (int i = 0; i < format->fmt_count; ++i)
{
2009-10-22 01:48:07 +02:00
descs[start + i] = FB_NEW(pool) dsc;
if (format->fmt_desc[i].dsc_dtype != dtype_unknown)
{
EVL_field(rpb->rpb_relation, record, i, descs[start + i]);
jrd_fld* field = (*rpb->rpb_relation->rel_fields)[i];
fb_assert(field);
values->getValue(j + 1)->make(descs[start + i], field->fld_name, true, i);
++j;
}
}
return j;
}
//---------------------
ExtEngineManager::~ExtEngineManager()
{
fb_assert(enginesAttachments.count() == 0);
EnginesMap::Accessor accessor(&engines);
for (bool found = accessor.getFirst(); found; found = accessor.getNext())
{
ExternalEngine* engine = accessor.current()->second;
engine->dispose(LogError());
}
}
//---------------------
void ExtEngineManager::initialize()
{
Firebird::PathName pluginsPath = PluginManager::getPluginsDirectory();
ScanDir dir(pluginsPath.c_str(), "*.conf");
try
{
SortedObjectsArray<MetaName> conflicts(*getDefaultMemoryPool());
while (dir.next())
{
Vulcan::ConfigFile configFile(dir.getFilePath(), Vulcan::ConfigFile::LEX_none);
for (Element* el = configFile.getObjects()->children; el; el = el->sibling)
{
if (el->name == "external_engine")
{
MetaName name = el->getAttributeName(0);
if (enginesModules().exist(name) || conflicts.exist(name))
{
string s;
s.printf("External engine %s defined more than once.", name.c_str());
gds__log(s.c_str());
conflicts.add(name);
continue;
}
Element* plugin = el->findChild("plugin_module");
if (plugin)
enginesModules().put(name, plugin->getAttributeName(0));
else
conflicts.add(name);
}
}
}
}
catch (AdminException& ex)
{
string s;
s.printf("Error in plugin config file '%s': %s'", dir.getFilePath(), ex.getText());
gds__log(s.c_str());
}
}
2009-10-30 11:50:59 +01:00
void ExtEngineManager::closeAttachment(thread_db* tdbb, Attachment* /*attachment*/)
{
Array<ExternalEngine*> enginesCopy;
{ // scope
ReadLockGuard readGuard(enginesLock);
EnginesMap::Accessor accessor(&engines);
for (bool found = accessor.getFirst(); found; found = accessor.getNext())
enginesCopy.add(accessor.current()->second);
}
Database::Checkout dcoHolder(tdbb->getDatabase());
for (Array<ExternalEngine*>::iterator i = enginesCopy.begin(); i != enginesCopy.end(); ++i)
{
ExternalEngine* engine = *i;
EngineAttachmentInfo* attInfo = getEngineAttachment(tdbb, engine, true);
if (attInfo)
{
ContextManager<ExternalFunction> ctxManager(tdbb, attInfo, attInfo->adminCharSet);
engine->closeAttachment(LogError(), attInfo->context);
delete attInfo;
}
}
}
ExtEngineManager::Function* ExtEngineManager::makeFunction(thread_db* tdbb, const UserFunction* udf,
const Firebird::MetaName& engine, const Firebird::string& entryPoint,
const Firebird::string& body)
{
string entryPointTrimmed = entryPoint;
entryPointTrimmed.trim();
EngineAttachmentInfo* attInfo = getEngineAttachment(tdbb, engine);
ContextManager<ExternalFunction> ctxManager(tdbb, attInfo,
attInfo->adminCharSet); // CallerName(obj_udf, udf->fun_name)
ExternalFunction* externalFunction;
{ // scope
Database::Checkout dcoHolder(tdbb->getDatabase());
externalFunction = attInfo->engine->makeFunction(RaiseError(),
attInfo->context, udf->fun_name.qualifier.nullStr(), udf->fun_name.identifier.c_str(),
entryPointTrimmed.nullStr(), body.nullStr());
if (!externalFunction)
{
//// TODO: localize
status_exception::raise(
Arg::Gds(isc_random) <<
Arg::Str("External function not returned by the external engine plugin"));
}
}
try
{
return FB_NEW(getPool()) Function(tdbb, this, attInfo->engine, externalFunction, udf);
}
catch (...)
{
Database::Checkout dcoHolder(tdbb->getDatabase());
externalFunction->dispose(LogError());
throw;
}
}
ExtEngineManager::Procedure* ExtEngineManager::makeProcedure(thread_db* tdbb, const jrd_prc* prc,
const Firebird::MetaName& engine, const Firebird::string& entryPoint,
const Firebird::string& body)
{
string entryPointTrimmed = entryPoint;
entryPointTrimmed.trim();
EngineAttachmentInfo* attInfo = getEngineAttachment(tdbb, engine);
ContextManager<ExternalProcedure> ctxManager(tdbb, attInfo, attInfo->adminCharSet,
(prc->prc_name.qualifier.isEmpty() ?
CallerName(obj_procedure, prc->prc_name.identifier) :
CallerName(obj_package_header, prc->prc_name.qualifier)));
ExternalProcedure* externalProcedure;
{ // scope
Database::Checkout dcoHolder(tdbb->getDatabase());
externalProcedure = attInfo->engine->makeProcedure(RaiseError(),
attInfo->context, prc->prc_name.qualifier.nullStr(), prc->prc_name.identifier.c_str(),
entryPointTrimmed.nullStr(), body.nullStr());
if (!externalProcedure)
{
//// TODO: localize
status_exception::raise(
Arg::Gds(isc_random) <<
Arg::Str("External procedure not returned by the external engine plugin"));
}
}
try
{
return FB_NEW(getPool()) Procedure(tdbb, this, attInfo->engine, externalProcedure, prc);
}
catch (...)
{
Database::Checkout dcoHolder(tdbb->getDatabase());
externalProcedure->dispose(LogError());
throw;
}
}
ExtEngineManager::Trigger* ExtEngineManager::makeTrigger(thread_db* tdbb, const Jrd::Trigger* trg,
const Firebird::MetaName& engine, const Firebird::string& entryPoint,
const Firebird::string& body, Firebird::ExternalTrigger::Type type)
{
string entryPointTrimmed = entryPoint;
entryPointTrimmed.trim();
MetaName relationNameTrimmed;
if (trg->relation)
relationNameTrimmed = trg->relation->rel_name;
EngineAttachmentInfo* attInfo = getEngineAttachment(tdbb, engine);
ContextManager<ExternalTrigger> ctxManager(tdbb, attInfo, attInfo->adminCharSet,
CallerName(obj_trigger, trg->name));
ExternalTrigger* externalTrigger;
{ // scope
Database::Checkout dcoHolder(tdbb->getDatabase());
externalTrigger = attInfo->engine->makeTrigger(RaiseError(), attInfo->context,
trg->name.c_str(), entryPointTrimmed.nullStr(), body.nullStr(),
relationNameTrimmed.c_str(), type);
if (!externalTrigger)
{
//// TODO: localize
status_exception::raise(
Arg::Gds(isc_random) <<
Arg::Str("External trigger not returned by the external engine plugin"));
}
}
try
{
return FB_NEW(getPool()) Trigger(tdbb, this, attInfo->engine, externalTrigger, trg);
}
catch (...)
{
Database::Checkout dcoHolder(tdbb->getDatabase());
externalTrigger->dispose(LogError());
throw;
}
}
ExternalEngine* ExtEngineManager::getEngine(thread_db* tdbb, const Firebird::MetaName& name)
{
ReadLockGuard readGuard(enginesLock);
ExternalEngine* engine = NULL;
if (!engines.get(name, engine))
{
readGuard.release();
WriteLockGuard writeGuard(enginesLock);
if (!engines.get(name, engine))
{
string pluginName;
if (enginesModules().get(name, pluginName))
{
PluginImpl* plugin = PluginManager::getPlugin(pluginName);
EngineAttachment key(NULL, NULL);
AutoPtr<EngineAttachmentInfo> attInfo;
try
{
Database::Checkout dcoHolder(tdbb->getDatabase());
engine = plugin->getExternalEngineFactory()->createEngine(RaiseError(),
EXTERNAL_VERSION_1, name.c_str());
if (engine)
{
if (engine->getVersion(RaiseError()) != EXTERNAL_VERSION_1)
{
//// TODO: localize
status_exception::raise(
Arg::Gds(isc_random) <<
Arg::Str(string("Incompatible plugin version for external engine \"") +
name.c_str() + "\""));
}
Database::SyncGuard dsGuard(tdbb->getDatabase());
key = EngineAttachment(engine, tdbb->getAttachment());
attInfo = FB_NEW(getPool()) EngineAttachmentInfo();
attInfo->engine = engine;
attInfo->context = FB_NEW(getPool()) ExternalContextImpl(tdbb, engine);
setupAdminCharSet(tdbb, engine, attInfo);
ContextManager<ExternalFunction> ctxManager(tdbb, attInfo, attInfo->adminCharSet);
engine->openAttachment(LogError(), attInfo->context);
}
}
catch (...)
{
if (engine)
engine->dispose(LogError());
throw;
}
if (engine)
{
engines.put(name, engine);
enginesAttachments.put(key, attInfo);
attInfo.release();
}
}
}
}
if (!engine)
{
//// TODO: localize
status_exception::raise(
Arg::Gds(isc_random) <<
Arg::Str(string("External engine \"") + name.c_str() + "\" not found"));
}
return engine;
}
ExtEngineManager::EngineAttachmentInfo* ExtEngineManager::getEngineAttachment(
thread_db* tdbb, const Firebird::MetaName& name)
{
ExternalEngine* engine = getEngine(tdbb, name);
return getEngineAttachment(tdbb, engine);
}
ExtEngineManager::EngineAttachmentInfo* ExtEngineManager::getEngineAttachment(
thread_db* tdbb, ExternalEngine* engine, bool closing)
{
EngineAttachment key(engine, tdbb->getAttachment());
EngineAttachmentInfo* attInfo = NULL;
ReadLockGuard readGuard(&enginesLock);
if (!enginesAttachments.get(key, attInfo) && !closing)
{
readGuard.release();
WriteLockGuard writeGuard(enginesLock);
if (!enginesAttachments.get(key, attInfo))
{
attInfo = FB_NEW(getPool()) EngineAttachmentInfo();
attInfo->engine = engine;
attInfo->context = FB_NEW(getPool()) ExternalContextImpl(tdbb, engine);
setupAdminCharSet(tdbb, engine, attInfo);
enginesAttachments.put(key, attInfo);
ContextManager<ExternalFunction> ctxManager(tdbb, attInfo, attInfo->adminCharSet);
Database::Checkout dcoHolder(tdbb->getDatabase());
engine->openAttachment(LogError(), attInfo->context);
}
return attInfo;
}
if (closing && attInfo)
{
readGuard.release();
WriteLockGuard writeGuard(enginesLock);
enginesAttachments.remove(key);
}
return attInfo;
}
void ExtEngineManager::setupAdminCharSet(thread_db* tdbb, Firebird::ExternalEngine* engine,
EngineAttachmentInfo* attInfo)
{
ContextManager<ExternalFunction> ctxManager(tdbb, attInfo, CS_UTF8);
Utf8 charSetName[MAX_SQL_IDENTIFIER_SIZE] = "NONE";
engine->open(RaiseError(), attInfo->context, charSetName,
MAX_SQL_IDENTIFIER_LEN);
charSetName[MAX_SQL_IDENTIFIER_LEN] = '\0';
if (!MET_get_char_coll_subtype(tdbb, &attInfo->adminCharSet,
reinterpret_cast<const UCHAR*>(charSetName),
strlen(charSetName)))
{
status_exception::raise(
Arg::Gds(isc_charset_not_found) <<
Arg::Str(charSetName));
}
}
} // namespace Jrd