8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-31 06:43:02 +01:00
firebird-mirror/src/jrd/ExtEngineManager.cpp

1117 lines
29 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 "../dsql/sqlda_pub.h"
2010-10-12 10:02:57 +02:00
#include "../common/dsc.h"
#include "../jrd/align.h"
#include "../jrd/jrd.h"
#include "../jrd/exe.h"
#include "../jrd/req.h"
#include "../jrd/status.h"
#include "../jrd/tra.h"
#include "../jrd/ibase.h"
2010-10-12 10:02:57 +02:00
#include "../common/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/par_proto.h"
#include "../jrd/thread_proto.h"
#include "../jrd/Function.h"
2010-10-12 10:02:57 +02:00
#include "../common/isc_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"
2010-10-12 10:02:57 +02:00
#include "../common/ScanDir.h"
#include "../common/utils_proto.h"
#include "../common/classes/GetPlugins.h"
using namespace Firebird;
namespace Jrd {
static MakeUpgradeInfo<> upInfo;
// Create a Format based on a IMessageMetadata.
static Format* createFormat(MemoryPool& pool, IMessageMetadata* params)
{
LocalStatus status;
unsigned count = params->getCount(&status);
status.check();
Format* format = Format::newFormat(pool, count * 2);
unsigned runOffset = 0;
dsc* desc = format->fmt_desc.begin();
for (unsigned i = 0; i < count; ++i)
{
unsigned descOffset, nullOffset, descDtype, descLength;
runOffset = fb_utils::sqlTypeToDsc(runOffset, params->getType(&status, i),
params->getLength(&status, i), &descDtype, &descLength,
&descOffset, &nullOffset);
status.check();
desc->clear();
desc->dsc_dtype = descDtype;
desc->dsc_length = descLength;
desc->dsc_scale = params->getScale(&status, i);
desc->dsc_sub_type = params->getSubType(&status, i);
desc->setTextType(params->getCharset(&status, i));
desc->dsc_address = (UCHAR*)(IPTR) descOffset;
desc->dsc_flags = (params->isNullable(&status, i) ? DSC_nullable : 0);
++desc;
desc->makeShort(0, (SSHORT*)(IPTR) nullOffset);
status.check();
++desc;
}
format->fmt_length = runOffset;
return format;
}
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)
{
// !!!!! needs async lock to be safe
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;
// !!!!! needs async lock to be safe
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;
}
// !!!!! needs async lock to be safe
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
Attachment::Checkout attCout(attachment, FB_FUNCTION);
LocalStatus status;
obj->getCharSet(&status, attInfo->context, charSetName, MAX_SQL_IDENTIFIER_LEN);
status.check();
2009-10-30 11:50:59 +01:00
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::ExternalContextImpl::ExternalContextImpl(thread_db* tdbb,
ExternalEngine* aEngine)
: engine(aEngine),
internalAttachment(tdbb->getAttachment()),
internalTransaction(NULL),
externalAttachment(NULL),
externalTransaction(NULL),
miscInfo(*internalAttachment->att_pool)
{
//// TODO: admin rights
clientCharSet = INTL_charset_lookup(tdbb, internalAttachment->att_client_charset)->getName();
internalAttachment->att_interface->addRef();
externalAttachment = MasterInterfacePtr()->registerAttachment(JProvider::getInstance(),
internalAttachment->att_interface);
}
ExtEngineManager::ExternalContextImpl::~ExternalContextImpl()
{
releaseTransaction();
if (externalAttachment)
{
externalAttachment->release();
externalAttachment = NULL;
}
}
void ExtEngineManager::ExternalContextImpl::releaseTransaction()
{
if (externalTransaction)
{
externalTransaction->release();
externalTransaction = NULL;
}
internalTransaction = NULL;
}
void ExtEngineManager::ExternalContextImpl::setTransaction(thread_db* tdbb)
{
jrd_tra* newTransaction = tdbb->getTransaction();
if (newTransaction == internalTransaction)
return;
releaseTransaction();
fb_assert(!externalTransaction);
if ((internalTransaction = newTransaction))
{
internalTransaction->getInterface()->addRef();
externalTransaction = MasterInterfacePtr()->registerTransaction(externalAttachment,
internalTransaction->getInterface());
}
}
IMaster* ExtEngineManager::ExternalContextImpl::getMaster()
{
MasterInterfacePtr master;
return master;
}
ExternalEngine* ExtEngineManager::ExternalContextImpl::getEngine(IStatus* /*status*/)
{
return engine;
}
Firebird::IAttachment* FB_CALL ExtEngineManager::ExternalContextImpl::getAttachment(IStatus* /*status*/)
{
return externalAttachment;
}
Firebird::ITransaction* FB_CALL ExtEngineManager::ExternalContextImpl::getTransaction(IStatus* /*status*/)
{
return externalTransaction;
}
const char* FB_CALL ExtEngineManager::ExternalContextImpl::getUserName()
{
return internalAttachment->att_user->usr_user_name.c_str();
}
const char* FB_CALL ExtEngineManager::ExternalContextImpl::getDatabaseName()
{
return internalAttachment->att_database->dbb_database_name.c_str();
}
const 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;
}
//---------------------
ExtEngineManager::Function::Function(thread_db* tdbb, ExtEngineManager* aExtManager,
ExternalEngine* aEngine, RoutineMetadata* aMetadata, ExternalFunction* aFunction,
const Jrd::Function* aUdf)
: extManager(aExtManager),
engine(aEngine),
metadata(aMetadata),
function(aFunction),
udf(aUdf),
database(tdbb->getDatabase())
{
}
ExtEngineManager::Function::~Function()
{
2011-05-10 03:12:14 +02:00
//Database::Checkout dcoHolder(database);
function->dispose();
}
void ExtEngineManager::Function::execute(thread_db* tdbb, UCHAR* inMsg, UCHAR* outMsg) const
{
EngineAttachmentInfo* attInfo = extManager->getEngineAttachment(tdbb, engine);
ContextManager<ExternalFunction> ctxManager(tdbb, attInfo, function,
(udf->getName().package.isEmpty() ?
CallerName(obj_udf, udf->getName().identifier) :
CallerName(obj_package_header, udf->getName().package)));
Attachment::Checkout attCout(tdbb->getAttachment(), FB_FUNCTION);
LocalStatus status;
function->execute(&status, attInfo->context, inMsg, outMsg);
status.check();
}
//---------------------
ExtEngineManager::Procedure::Procedure(thread_db* tdbb, ExtEngineManager* aExtManager,
ExternalEngine* aEngine, RoutineMetadata* aMetadata, ExternalProcedure* aProcedure,
const jrd_prc* aPrc)
: extManager(aExtManager),
engine(aEngine),
metadata(aMetadata),
procedure(aProcedure),
prc(aPrc),
database(tdbb->getDatabase())
{
}
ExtEngineManager::Procedure::~Procedure()
{
2011-05-09 12:15:19 +02:00
//Database::Checkout dcoHolder(database);
procedure->dispose();
}
ExtEngineManager::ResultSet* ExtEngineManager::Procedure::open(thread_db* tdbb,
UCHAR* inMsg, UCHAR* outMsg) const
{
return FB_NEW(*tdbb->getDefaultPool()) ResultSet(tdbb, inMsg, outMsg, this);
}
//---------------------
ExtEngineManager::ResultSet::ResultSet(thread_db* tdbb, UCHAR* inMsg, UCHAR* outMsg,
const ExtEngineManager::Procedure* aProcedure)
: procedure(aProcedure),
2011-05-09 12:15:19 +02:00
attachment(tdbb->getAttachment()),
firstFetch(true)
{
attInfo = procedure->extManager->getEngineAttachment(tdbb, procedure->engine);
ContextManager<ExternalProcedure> ctxManager(tdbb, attInfo, procedure->procedure,
(procedure->prc->getName().package.isEmpty() ?
CallerName(obj_procedure, procedure->prc->getName().identifier) :
CallerName(obj_package_header, procedure->prc->getName().package)));
charSet = attachment->att_charset;
Attachment::Checkout attCout(attachment, FB_FUNCTION);
LocalStatus status;
resultSet = procedure->procedure->open(&status, attInfo->context, inMsg, outMsg);
status.check();
}
ExtEngineManager::ResultSet::~ResultSet()
{
if (resultSet)
{
2011-05-09 12:15:19 +02:00
fb_assert(attachment == JRD_get_thread_data()->getAttachment());
Attachment::Checkout attCout(attachment, FB_FUNCTION);
resultSet->dispose();
}
}
bool ExtEngineManager::ResultSet::fetch(thread_db* tdbb)
{
bool wasFirstFetch = firstFetch;
firstFetch = false;
if (!resultSet)
return wasFirstFetch;
ContextManager<ExternalProcedure> ctxManager(tdbb, attInfo, charSet,
(procedure->prc->getName().package.isEmpty() ?
CallerName(obj_procedure, procedure->prc->getName().identifier) :
CallerName(obj_package_header, procedure->prc->getName().package)));
2011-05-09 12:15:19 +02:00
fb_assert(attachment == tdbb->getAttachment());
Attachment::Checkout attCout(attachment, FB_FUNCTION);
LocalStatus status;
bool ret = resultSet->fetch(&status);
status.check();
return ret;
}
//---------------------
ExtEngineManager::Trigger::Trigger(thread_db* tdbb, MemoryPool& pool, ExtEngineManager* aExtManager,
ExternalEngine* aEngine, RoutineMetadata* aMetadata,
ExternalTrigger* aTrigger, const Jrd::Trigger* aTrg)
: extManager(aExtManager),
engine(aEngine),
metadata(aMetadata),
trigger(aTrigger),
trg(aTrg),
fieldsPos(pool),
database(tdbb->getDatabase())
{
dsc shortDesc;
shortDesc.makeShort(0);
jrd_rel* relation = trg->relation;
if (relation)
{
format = createFormat(pool, metadata->triggerFields);
for (unsigned i = 0; i < format->fmt_count / 2; ++i)
fieldsPos.add(i);
}
}
ExtEngineManager::Trigger::~Trigger()
{
2011-05-09 12:15:19 +02:00
// hvlad: shouldn't we call trigger->dispose() here ?
}
void ExtEngineManager::Trigger::execute(thread_db* tdbb, ExternalTrigger::Action action,
record_param* oldRpb, record_param* newRpb) const
{
EngineAttachmentInfo* attInfo = extManager->getEngineAttachment(tdbb, engine);
ContextManager<ExternalTrigger> ctxManager(tdbb, attInfo, trigger,
CallerName(obj_trigger, trg->name));
// ASF: Using Array instead of HalfStaticArray to not need to do alignment hacks here.
Array<UCHAR> oldMsg;
Array<UCHAR> newMsg;
if (oldRpb)
setValues(tdbb, oldMsg, oldRpb);
if (newRpb)
setValues(tdbb, newMsg, newRpb);
{ // scope
Attachment::Checkout attCout(tdbb->getAttachment(), FB_FUNCTION);
LocalStatus status;
trigger->execute(&status, attInfo->context, action,
(oldRpb ? oldMsg.begin() : NULL), (newRpb ? newMsg.begin() : NULL));
status.check();
}
if (newRpb)
{
// Move data back from the message to the record.
Record* record = newRpb->rpb_record;
UCHAR* p = newMsg.begin();
for (unsigned i = 0; i < format->fmt_count / 2; ++i)
{
USHORT fieldPos = fieldsPos[i];
dsc target;
bool readonly = !EVL_field(newRpb->rpb_relation, record, fieldPos, &target) &&
target.dsc_address && !(target.dsc_flags & DSC_null);
if (!readonly)
{
SSHORT* nullSource = (SSHORT*) (p + (IPTR) format->fmt_desc[i * 2 + 1].dsc_address);
if (*nullSource == 0)
{
dsc source = format->fmt_desc[i * 2];
source.dsc_address += (IPTR) p;
MOV_move(tdbb, &source, &target);
2013-03-17 18:35:53 +01:00
record->clearNull(fieldPos);
}
else
2013-03-17 18:35:53 +01:00
record->setNull(fieldPos);
}
}
}
}
void ExtEngineManager::Trigger::setValues(thread_db* tdbb, Array<UCHAR>& msgBuffer,
record_param* rpb) const
{
if (!rpb || !rpb->rpb_record)
return;
UCHAR* p = msgBuffer.getBuffer(format->fmt_length);
///memset(p, 0, format->fmt_length);
for (unsigned i = 0; i < format->fmt_count / 2; ++i)
{
USHORT fieldPos = fieldsPos[i];
dsc source;
EVL_field(rpb->rpb_relation, rpb->rpb_record, fieldPos, &source);
2011-06-24 08:34:16 +02:00
// CVC: I'm not sure why it's not important to check EVL_field's result.
2011-06-26 20:48:00 +02:00
SSHORT* nullTarget = (SSHORT*) (p + (IPTR) format->fmt_desc[i * 2 + 1].dsc_address);
*nullTarget = (source.dsc_flags & DSC_null) != 0 ? -1 : 0;
if (*nullTarget == 0)
{
dsc target = format->fmt_desc[i * 2];
target.dsc_address += (IPTR) p;
MOV_move(tdbb, &source, &target);
}
}
}
//---------------------
ExtEngineManager::~ExtEngineManager()
{
fb_assert(enginesAttachments.count() == 0);
/*
AP: Commented out this code due to later AV.
When engine is released, it does dlclose() plugin module (libudr_engine.so),
but that module is not actually unloaded - because UDR module (libudrcpp_example.so) is using
symbols from plugin module, therefore raising plugin module's reference count.
UDR module can be unloaded only from plugin module's global variable (ModuleMap modules) dtor,
2011-01-16 03:16:15 +01:00
which is not called as long as plugin module is not unloaded. As the result all this will be
unloaded only on program exit, causing at that moment AV if this code is active: it happens that
~ModuleMap dlcloses itself.
PluginManagerInterfacePtr pi;
EnginesMap::Accessor accessor(&engines);
for (bool found = accessor.getFirst(); found; found = accessor.getNext())
{
ExternalEngine* engine = accessor.current()->second;
pi->releasePlugin(engine);
}
*/
}
//---------------------
void ExtEngineManager::initialize()
{
}
2011-05-09 12:15:19 +02:00
void ExtEngineManager::closeAttachment(thread_db* tdbb, Attachment* attachment)
{
Array<ExternalEngine*> enginesCopy;
{ // scope
ReadLockGuard readGuard(enginesLock, FB_FUNCTION);
EnginesMap::Accessor accessor(&engines);
for (bool found = accessor.getFirst(); found; found = accessor.getNext())
enginesCopy.add(accessor.current()->second);
}
RefDeb(DEB_RLS_JATT, "ExtEngineManager::closeAttachment");
Attachment::Checkout attCout(attachment, FB_FUNCTION, true);
for (Array<ExternalEngine*>::iterator i = enginesCopy.begin(); i != enginesCopy.end(); ++i)
{
ExternalEngine* engine = *i;
EngineAttachmentInfo* attInfo = getEngineAttachment(tdbb, engine, true);
if (attInfo)
{
{ // scope
ContextManager<ExternalFunction> ctxManager(tdbb, attInfo, attInfo->adminCharSet);
LocalStatus status;
engine->closeAttachment(&status, attInfo->context); //// FIXME: log status
}
delete attInfo;
}
}
}
void ExtEngineManager::makeFunction(thread_db* tdbb, Jrd::Function* udf,
const MetaName& engine, const string& entryPoint, const string& body)
{
string entryPointTrimmed = entryPoint;
entryPointTrimmed.trim();
EngineAttachmentInfo* attInfo = getEngineAttachment(tdbb, engine);
ContextManager<ExternalFunction> ctxManager(tdbb, attInfo, attInfo->adminCharSet,
(udf->getName().package.isEmpty() ?
CallerName(obj_udf, udf->getName().identifier) :
CallerName(obj_package_header, udf->getName().package)));
///MemoryPool& pool = *tdbb->getDefaultPool();
MemoryPool& pool = *getDefaultMemoryPool();
AutoPtr<RoutineMetadata> metadata(FB_NEW(pool) RoutineMetadata(pool));
metadata->package = udf->getName().package;
metadata->name = udf->getName().identifier;
metadata->entryPoint = entryPointTrimmed;
metadata->body = body;
MsgMetadata* inMsg = new MsgMetadata;
metadata->inputParameters = inMsg;
MsgMetadata* outMsg = new MsgMetadata;
metadata->outputParameters = outMsg;
for (Array<NestConst<Parameter> >::const_iterator i = udf->getInputFields().begin();
i != udf->getInputFields().end();
++i)
{
SLONG sqlLen, sqlSubType, sqlScale, sqlType;
(*i)->prm_desc.getSqlInfo(&sqlLen, &sqlSubType, &sqlScale, &sqlType);
StatementMetadata::Parameters::Item& item = inMsg->items.add();
item.field = (*i)->prm_name.c_str();
item.type = sqlType;
item.subType = sqlSubType;
item.length = sqlLen;
item.scale = sqlScale;
item.nullable = (*i)->prm_nullable;
item.finished = true;
}
{ // scope
const Parameter* i = udf->getOutputFields()[0];
SLONG sqlLen, sqlSubType, sqlScale, sqlType;
i->prm_desc.getSqlInfo(&sqlLen, &sqlSubType, &sqlScale, &sqlType);
StatementMetadata::Parameters::Item& item = outMsg->items.add();
item.field = i->prm_name.c_str();
item.type = sqlType;
item.subType = sqlSubType;
item.length = sqlLen;
item.scale = sqlScale;
item.nullable = i->prm_nullable;
item.finished = true;
}
LocalStatus status;
RefPtr<IMetadataBuilder> inBuilder(metadata->inputParameters->getBuilder(&status));
status.check();
inBuilder->release();
RefPtr<IMetadataBuilder> outBuilder(metadata->outputParameters->getBuilder(&status));
status.check();
outBuilder->release();
ExternalFunction* externalFunction;
{ // scope
Attachment::Checkout attCout(tdbb->getAttachment(), FB_FUNCTION);
externalFunction = attInfo->engine->makeFunction(&status, attInfo->context, metadata,
inBuilder, outBuilder);
status.check();
if (!externalFunction)
{
2010-01-04 01:43:45 +01:00
status_exception::raise(
Arg::Gds(isc_eem_func_not_returned) << udf->getName().toString() << engine);
}
metadata->inputParameters = inBuilder->getMetadata(&status);
status.check();
metadata->outputParameters = outBuilder->getMetadata(&status);
status.check();
}
try
{
udf->setInputFormat(createFormat(pool, metadata->inputParameters));
udf->setOutputFormat(createFormat(pool, metadata->outputParameters));
udf->fun_external = FB_NEW(getPool()) Function(tdbb, this, attInfo->engine,
metadata.release(), externalFunction, udf);
}
catch (...)
{
Attachment::Checkout attCout(tdbb->getAttachment(), FB_FUNCTION);
externalFunction->dispose();
throw;
}
}
void ExtEngineManager::makeProcedure(thread_db* tdbb, jrd_prc* prc,
const MetaName& engine, const string& entryPoint, const string& body)
{
string entryPointTrimmed = entryPoint;
entryPointTrimmed.trim();
EngineAttachmentInfo* attInfo = getEngineAttachment(tdbb, engine);
ContextManager<ExternalProcedure> ctxManager(tdbb, attInfo, attInfo->adminCharSet,
(prc->getName().package.isEmpty() ?
CallerName(obj_procedure, prc->getName().identifier) :
CallerName(obj_package_header, prc->getName().package)));
///MemoryPool& pool = *tdbb->getDefaultPool();
MemoryPool& pool = *getDefaultMemoryPool();
AutoPtr<RoutineMetadata> metadata(FB_NEW(pool) RoutineMetadata(pool));
metadata->package = prc->getName().package;
metadata->name = prc->getName().identifier;
metadata->entryPoint = entryPointTrimmed;
metadata->body = body;
MsgMetadata* inMsg = new MsgMetadata;
metadata->inputParameters = inMsg;
MsgMetadata* outMsg = new MsgMetadata;
metadata->outputParameters = outMsg;
const Array<NestConst<Parameter> >* parameters[] = {
&prc->getInputFields(), &prc->getOutputFields()};
for (unsigned i = 0; i < 2; ++i)
{
for (Array<NestConst<Parameter> >::const_iterator j = parameters[i]->begin();
j != parameters[i]->end();
++j)
{
SLONG sqlLen, sqlSubType, sqlScale, sqlType;
(*j)->prm_desc.getSqlInfo(&sqlLen, &sqlSubType, &sqlScale, &sqlType);
StatementMetadata::Parameters::Item& item = i == 1 ?
outMsg->items.add() : inMsg->items.add();
item.field = (*j)->prm_name.c_str();
item.type = sqlType;
item.subType = sqlSubType;
item.length = sqlLen;
item.scale = sqlScale;
item.nullable = (*j)->prm_nullable;
item.finished = true;
}
}
LocalStatus status;
RefPtr<IMetadataBuilder> inBuilder(metadata->inputParameters->getBuilder(&status));
status.check();
inBuilder->release();
RefPtr<IMetadataBuilder> outBuilder(metadata->outputParameters->getBuilder(&status));
status.check();
outBuilder->release();
ExternalProcedure* externalProcedure;
{ // scope
Attachment::Checkout attCout(tdbb->getAttachment(), FB_FUNCTION);
externalProcedure = attInfo->engine->makeProcedure(&status, attInfo->context, metadata,
inBuilder, outBuilder);
status.check();
if (!externalProcedure)
{
2010-01-04 01:43:45 +01:00
status_exception::raise(
Arg::Gds(isc_eem_proc_not_returned) <<
prc->getName().toString() << engine);
}
metadata->inputParameters = inBuilder->getMetadata(&status);
status.check();
metadata->outputParameters = outBuilder->getMetadata(&status);
status.check();
}
try
{
prc->setInputFormat(createFormat(pool, metadata->inputParameters));
prc->setOutputFormat(createFormat(pool, metadata->outputParameters));
prc->setExternal(FB_NEW(getPool()) Procedure(tdbb, this, attInfo->engine,
metadata.release(), externalProcedure, prc));
}
catch (...)
{
Attachment::Checkout attCout(tdbb->getAttachment(), FB_FUNCTION);
externalProcedure->dispose();
throw;
}
}
void ExtEngineManager::makeTrigger(thread_db* tdbb, Jrd::Trigger* trg,
const MetaName& engine, const string& entryPoint, const string& body,
ExternalTrigger::Type type)
{
string entryPointTrimmed = entryPoint;
entryPointTrimmed.trim();
EngineAttachmentInfo* attInfo = getEngineAttachment(tdbb, engine);
ContextManager<ExternalTrigger> ctxManager(tdbb, attInfo, attInfo->adminCharSet,
CallerName(obj_trigger, trg->name));
///MemoryPool& pool = *tdbb->getDefaultPool();
MemoryPool& pool = *getDefaultMemoryPool();
AutoPtr<RoutineMetadata> metadata(FB_NEW(pool) RoutineMetadata(pool));
metadata->name = trg->name;
metadata->entryPoint = entryPointTrimmed;
metadata->body = body;
metadata->triggerType = type;
jrd_rel* relation = trg->relation;
2011-06-04 04:18:06 +02:00
if (relation)
{
2011-06-04 04:18:06 +02:00
metadata->triggerTable = relation->rel_name;
MsgMetadata* fieldsMsg = new MsgMetadata;
metadata->triggerFields = fieldsMsg;
2011-06-04 04:18:06 +02:00
Format* relFormat = relation->rel_current_format;
2011-06-04 04:18:06 +02:00
for (size_t i = 0; i < relation->rel_fields->count(); ++i)
{
jrd_fld* field = (*relation->rel_fields)[i];
if (!field)
continue;
SLONG sqlLen, sqlSubType, sqlScale, sqlType;
relFormat->fmt_desc[i].getSqlInfo(&sqlLen, &sqlSubType, &sqlScale, &sqlType);
StatementMetadata::Parameters::Item& item = fieldsMsg->items.add();
2011-06-04 04:18:06 +02:00
item.field = field->fld_name.c_str();
item.type = sqlType;
item.subType = sqlSubType;
item.length = sqlLen;
item.scale = sqlScale;
item.nullable = !field->fld_not_null;
item.finished = true;
2011-06-04 04:18:06 +02:00
}
}
LocalStatus status;
RefPtr<IMetadataBuilder> fieldsBuilder(relation ?
metadata->triggerFields->getBuilder(&status) : NULL);
if (relation)
{
status.check();
fieldsBuilder->release();
}
ExternalTrigger* externalTrigger;
{ // scope
Attachment::Checkout attCout(tdbb->getAttachment(), FB_FUNCTION);
LocalStatus status;
externalTrigger = attInfo->engine->makeTrigger(&status, attInfo->context, metadata,
fieldsBuilder);
status.check();
if (!externalTrigger)
{
2010-01-04 01:43:45 +01:00
status_exception::raise(
2010-01-04 01:48:40 +01:00
Arg::Gds(isc_eem_trig_not_returned) << trg->name << engine);
}
if (relation)
{
metadata->triggerFields = fieldsBuilder->getMetadata(&status);
status.check();
}
}
try
{
trg->extTrigger = FB_NEW(getPool()) Trigger(tdbb, pool, this, attInfo->engine,
metadata.release(), externalTrigger, trg);
}
catch (...)
{
Attachment::Checkout attCout(tdbb->getAttachment(), FB_FUNCTION);
externalTrigger->dispose();
throw;
}
}
ExternalEngine* ExtEngineManager::getEngine(thread_db* tdbb, const MetaName& name)
{
ReadLockGuard readGuard(enginesLock, FB_FUNCTION);
ExternalEngine* engine = NULL;
if (!engines.get(name, engine))
{
readGuard.release();
WriteLockGuard writeGuard(enginesLock, FB_FUNCTION);
if (!engines.get(name, engine))
{
2011-03-06 02:06:36 +01:00
GetPlugins<ExternalEngine> engineControl(PluginType::ExternalEngine,
FB_EXTERNAL_ENGINE_VERSION, upInfo, name.c_str());
if (engineControl.hasData())
{
EngineAttachment key(NULL, NULL);
AutoPtr<EngineAttachmentInfo> attInfo;
try
{
Attachment::Checkout attCout(tdbb->getAttachment(), FB_FUNCTION);
engine = engineControl.plugin();
if (engine)
{
Attachment::SyncGuard attGuard(tdbb->getAttachment(), FB_FUNCTION);
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);
LocalStatus status;
engine->openAttachment(&status, attInfo->context); //// FIXME: log status
}
}
catch (...)
{
if (engine)
{
PluginManagerInterfacePtr()->releasePlugin(engine);
}
throw;
}
if (engine)
{
engine->addRef();
engines.put(name, engine);
enginesAttachments.put(key, attInfo);
attInfo.release();
}
}
}
}
if (!engine)
{
2010-01-04 01:43:45 +01:00
status_exception::raise(Arg::Gds(isc_eem_engine_notfound) << name);
}
return engine;
}
ExtEngineManager::EngineAttachmentInfo* ExtEngineManager::getEngineAttachment(
thread_db* tdbb, const 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, FB_FUNCTION);
if (!enginesAttachments.get(key, attInfo) && !closing)
{
readGuard.release();
WriteLockGuard writeGuard(enginesLock, FB_FUNCTION);
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);
Attachment::Checkout attCout(tdbb->getAttachment(), FB_FUNCTION);
LocalStatus status;
engine->openAttachment(&status, attInfo->context); //// FIXME: log status
}
return attInfo;
}
if (closing && attInfo)
{
readGuard.release();
WriteLockGuard writeGuard(enginesLock, FB_FUNCTION);
enginesAttachments.remove(key);
}
return attInfo;
}
void ExtEngineManager::setupAdminCharSet(thread_db* tdbb, ExternalEngine* engine,
EngineAttachmentInfo* attInfo)
{
ContextManager<ExternalFunction> ctxManager(tdbb, attInfo, CS_UTF8);
Utf8 charSetName[MAX_SQL_IDENTIFIER_SIZE] = "NONE";
LocalStatus status;
engine->open(&status, attInfo->context, charSetName, MAX_SQL_IDENTIFIER_LEN);
status.check();
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