mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-22 21:23:04 +01:00
Improve performance of external (UDR) functions (#7989)
This commit is contained in:
parent
a50d8ac4c4
commit
547cb8388b
@ -128,8 +128,7 @@ FB_UDR_BEGIN_FUNCTION(sum_args)
|
|||||||
// Get a reference to the return value.
|
// Get a reference to the return value.
|
||||||
ISC_LONG& ret = *(ISC_LONG*) (out + outOffset);
|
ISC_LONG& ret = *(ISC_LONG*) (out + outOffset);
|
||||||
|
|
||||||
// The return value is automatically initialized to 0.
|
ret = 0;
|
||||||
///ret = 0;
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < inCount; ++i)
|
for (unsigned i = 0; i < inCount; ++i)
|
||||||
{
|
{
|
||||||
|
@ -13059,7 +13059,7 @@ DmlNode* UdfCallNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch*
|
|||||||
if (argCount > node->function->fun_inputs)
|
if (argCount > node->function->fun_inputs)
|
||||||
mismatchStatus << Arg::Gds(isc_wronumarg);
|
mismatchStatus << Arg::Gds(isc_wronumarg);
|
||||||
|
|
||||||
for (auto pos = 0; pos < positionalArgCount; ++pos)
|
for (auto pos = 0u; pos < positionalArgCount; ++pos)
|
||||||
{
|
{
|
||||||
if (pos < node->function->fun_inputs)
|
if (pos < node->function->fun_inputs)
|
||||||
{
|
{
|
||||||
@ -13513,12 +13513,20 @@ dsc* UdfCallNode::execute(thread_db* tdbb, Request* request) const
|
|||||||
|
|
||||||
funcRequest->setGmtTimeStamp(request->getGmtTimeStamp());
|
funcRequest->setGmtTimeStamp(request->getGmtTimeStamp());
|
||||||
|
|
||||||
|
if (function->fun_external)
|
||||||
|
{
|
||||||
|
function->fun_external->execute(
|
||||||
|
tdbb, funcRequest, transaction, inMsgLength, inMsg, outMsgLength, outMsg);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
EXE_start(tdbb, funcRequest, transaction);
|
EXE_start(tdbb, funcRequest, transaction);
|
||||||
|
|
||||||
if (inMsgLength != 0)
|
if (inMsgLength != 0)
|
||||||
EXE_send(tdbb, funcRequest, 0, inMsgLength, inMsg);
|
EXE_send(tdbb, funcRequest, 0, inMsgLength, inMsg);
|
||||||
|
|
||||||
EXE_receive(tdbb, funcRequest, 1, outMsgLength, outMsg);
|
EXE_receive(tdbb, funcRequest, 1, outMsgLength, outMsg);
|
||||||
|
}
|
||||||
|
|
||||||
// Clean up all savepoints started during execution of the function
|
// Clean up all savepoints started during execution of the function
|
||||||
|
|
||||||
|
@ -1464,7 +1464,7 @@ public:
|
|||||||
TYPE_TRUNCATE_LOCAL_TABLE,
|
TYPE_TRUNCATE_LOCAL_TABLE,
|
||||||
TYPE_UPDATE_OR_INSERT,
|
TYPE_UPDATE_OR_INSERT,
|
||||||
|
|
||||||
TYPE_EXT_INIT_PARAMETER,
|
TYPE_EXT_INIT_PARAMETERS,
|
||||||
TYPE_EXT_TRIGGER
|
TYPE_EXT_TRIGGER
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
#include "../jrd/cmp_proto.h"
|
#include "../jrd/cmp_proto.h"
|
||||||
#include "../jrd/cvt_proto.h"
|
#include "../jrd/cvt_proto.h"
|
||||||
#include "../jrd/evl_proto.h"
|
#include "../jrd/evl_proto.h"
|
||||||
|
#include "../jrd/exe_proto.h"
|
||||||
#include "../jrd/intl_proto.h"
|
#include "../jrd/intl_proto.h"
|
||||||
#include "../jrd/met_proto.h"
|
#include "../jrd/met_proto.h"
|
||||||
#include "../jrd/mov_proto.h"
|
#include "../jrd/mov_proto.h"
|
||||||
@ -48,6 +49,7 @@
|
|||||||
#include "../jrd/SystemPackages.h"
|
#include "../jrd/SystemPackages.h"
|
||||||
#include "../common/isc_proto.h"
|
#include "../common/isc_proto.h"
|
||||||
#include "../common/classes/auto.h"
|
#include "../common/classes/auto.h"
|
||||||
|
#include "../common/classes/fb_pair.h"
|
||||||
#include "../common/classes/fb_string.h"
|
#include "../common/classes/fb_string.h"
|
||||||
#include "../common/classes/init.h"
|
#include "../common/classes/init.h"
|
||||||
#include "../common/classes/objects_array.h"
|
#include "../common/classes/objects_array.h"
|
||||||
@ -65,6 +67,57 @@ static EngineCheckout::Type checkoutType(IExternalEngine* engine);
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
// Compare two formats for equivalence, excluding fmt_defaults field.
|
||||||
|
bool sameFormats(const Format* fmt1, const Format* fmt2)
|
||||||
|
{
|
||||||
|
return fmt1->fmt_length == fmt2->fmt_length &&
|
||||||
|
fmt1->fmt_count == fmt2->fmt_count &&
|
||||||
|
fmt1->fmt_version == fmt2->fmt_version &&
|
||||||
|
fmt1->fmt_desc == fmt2->fmt_desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy message between different formats.
|
||||||
|
void copyMessage(thread_db* tdbb,
|
||||||
|
const Format* srcFormat, UCHAR* srcMsg,
|
||||||
|
const Format* dstFormat, UCHAR* dstMsg)
|
||||||
|
{
|
||||||
|
fb_assert(srcFormat->fmt_desc.getCount() == dstFormat->fmt_desc.getCount());
|
||||||
|
|
||||||
|
const auto srcDescEnd = srcFormat->fmt_desc.begin() + (srcFormat->fmt_desc.getCount() / 2 * 2);
|
||||||
|
auto srcDescIt = srcFormat->fmt_desc.begin();
|
||||||
|
auto dstDescIt = dstFormat->fmt_desc.begin();
|
||||||
|
|
||||||
|
while (srcDescIt < srcDescEnd)
|
||||||
|
{
|
||||||
|
fb_assert(srcDescIt[1].dsc_dtype == dtype_short);
|
||||||
|
fb_assert(dstDescIt[1].dsc_dtype == dtype_short);
|
||||||
|
|
||||||
|
const auto srcArgOffset = (IPTR) srcDescIt[0].dsc_address;
|
||||||
|
const auto srcNullOffset = (IPTR) srcDescIt[1].dsc_address;
|
||||||
|
const auto srcNullPtr = reinterpret_cast<const SSHORT*>(srcMsg + srcNullOffset);
|
||||||
|
|
||||||
|
const auto dstArgOffset = (IPTR) dstDescIt[0].dsc_address;
|
||||||
|
const auto dstNullOffset = (IPTR) dstDescIt[1].dsc_address;
|
||||||
|
const auto dstNullPtr = reinterpret_cast<SSHORT*>(dstMsg + dstNullOffset);
|
||||||
|
|
||||||
|
if (!*srcNullPtr)
|
||||||
|
{
|
||||||
|
dsc srcDesc = srcDescIt[0];
|
||||||
|
srcDesc.dsc_address = srcMsg + srcArgOffset;
|
||||||
|
|
||||||
|
dsc dstDesc = dstDescIt[0];
|
||||||
|
dstDesc.dsc_address = dstMsg + dstArgOffset;
|
||||||
|
|
||||||
|
MOV_move(tdbb, &srcDesc, &dstDesc);
|
||||||
|
}
|
||||||
|
|
||||||
|
*dstNullPtr = *srcNullPtr;
|
||||||
|
|
||||||
|
srcDescIt += 2;
|
||||||
|
dstDescIt += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Internal message node.
|
// Internal message node.
|
||||||
class IntMessageNode : public MessageNode
|
class IntMessageNode : public MessageNode
|
||||||
{
|
{
|
||||||
@ -78,8 +131,8 @@ namespace
|
|||||||
setup(tdbb, csb, message, format->fmt_count);
|
setup(tdbb, csb, message, format->fmt_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual USHORT setupDesc(thread_db* tdbb, CompilerScratch* csb, USHORT index,
|
USHORT setupDesc(thread_db* tdbb, CompilerScratch* csb, USHORT index,
|
||||||
dsc* desc, ItemInfo* itemInfo)
|
dsc* desc, ItemInfo* itemInfo) override
|
||||||
{
|
{
|
||||||
*desc = format->fmt_desc[index];
|
*desc = format->fmt_desc[index];
|
||||||
|
|
||||||
@ -116,7 +169,7 @@ namespace
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
Array<NestConst<Parameter>>& parameters;
|
Array<NestConst<Parameter>>& parameters;
|
||||||
const Format* format;
|
const Format* const format;
|
||||||
};
|
};
|
||||||
|
|
||||||
// External message node.
|
// External message node.
|
||||||
@ -130,14 +183,14 @@ namespace
|
|||||||
setup(tdbb, csb, message, format->fmt_count);
|
setup(tdbb, csb, message, format->fmt_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual USHORT setupDesc(thread_db* tdbb, CompilerScratch* csb, USHORT index,
|
USHORT setupDesc(thread_db* tdbb, CompilerScratch* csb, USHORT index,
|
||||||
dsc* desc, ItemInfo* itemInfo)
|
dsc* desc, ItemInfo* itemInfo) override
|
||||||
{
|
{
|
||||||
*desc = format->fmt_desc[index];
|
*desc = format->fmt_desc[index];
|
||||||
return type_alignments[desc->dsc_dtype];
|
return type_alignments[desc->dsc_dtype];
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const
|
const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const override
|
||||||
{
|
{
|
||||||
if (request->req_operation == Request::req_evaluate)
|
if (request->req_operation == Request::req_evaluate)
|
||||||
{
|
{
|
||||||
@ -150,22 +203,27 @@ namespace
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const Format* format;
|
const Format* const format;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initialize output parameters with their domains default value or NULL.
|
// Initialize output parameters with their domains default value or NULL.
|
||||||
// Kind of blr_init_variable, but for parameters.
|
// Kind of blr_init_variable, but for parameters.
|
||||||
class InitParameterNode final : public TypedNode<StmtNode, StmtNode::TYPE_EXT_INIT_PARAMETER>
|
class InitParametersNode final : public TypedNode<StmtNode, StmtNode::TYPE_EXT_INIT_PARAMETERS>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
InitParameterNode(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb,
|
InitParametersNode(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb,
|
||||||
Array<NestConst<Parameter> >& parameters, MessageNode* aMessage, USHORT aArgNumber)
|
Array<NestConst<Parameter>>& parameters, MessageNode* aMessage)
|
||||||
: TypedNode<StmtNode, StmtNode::TYPE_EXT_INIT_PARAMETER>(pool),
|
: TypedNode<StmtNode, StmtNode::TYPE_EXT_INIT_PARAMETERS>(pool),
|
||||||
message(aMessage),
|
message(aMessage)
|
||||||
argNumber(aArgNumber)
|
|
||||||
{
|
{
|
||||||
Parameter* parameter = parameters[argNumber / 2];
|
// Iterate over the format items, except the EOF item.
|
||||||
defaultValueNode = NULL;
|
const unsigned paramCount = message->format->fmt_count / 2;
|
||||||
|
|
||||||
|
defaultValuesNode = FB_NEW_POOL(pool) ValueListNode(pool, paramCount);
|
||||||
|
|
||||||
|
for (unsigned paramIndex = 0; paramIndex < paramCount; ++paramIndex)
|
||||||
|
{
|
||||||
|
const auto parameter = parameters[paramIndex];
|
||||||
|
|
||||||
if (parameter->prm_mechanism != prm_mech_type_of &&
|
if (parameter->prm_mechanism != prm_mech_type_of &&
|
||||||
!fb_utils::implicit_domain(parameter->prm_field_source.c_str()))
|
!fb_utils::implicit_domain(parameter->prm_field_source.c_str()))
|
||||||
@ -176,73 +234,67 @@ namespace
|
|||||||
bool exist = csb->csb_map_field_info.get(namePair, fieldInfo);
|
bool exist = csb->csb_map_field_info.get(namePair, fieldInfo);
|
||||||
|
|
||||||
if (exist && fieldInfo.defaultValue)
|
if (exist && fieldInfo.defaultValue)
|
||||||
defaultValueNode = CMP_clone_node(tdbb, csb, fieldInfo.defaultValue);
|
defaultValuesNode->items[paramIndex] = CMP_clone_node(tdbb, csb, fieldInfo.defaultValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string internalPrint(NodePrinter& printer) const
|
string internalPrint(NodePrinter& printer) const override
|
||||||
{
|
{
|
||||||
StmtNode::internalPrint(printer);
|
StmtNode::internalPrint(printer);
|
||||||
|
|
||||||
NODE_PRINT(printer, message);
|
NODE_PRINT(printer, message);
|
||||||
NODE_PRINT(printer, argNumber);
|
NODE_PRINT(printer, defaultValuesNode);
|
||||||
NODE_PRINT(printer, defaultValueNode);
|
|
||||||
|
|
||||||
return "InitParameterNode";
|
return "InitParametersNode";
|
||||||
}
|
}
|
||||||
|
|
||||||
void genBlr(DsqlCompilerScratch* /*dsqlScratch*/)
|
void genBlr(DsqlCompilerScratch* /*dsqlScratch*/) override
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
InitParameterNode* pass1(thread_db* tdbb, CompilerScratch* csb)
|
InitParametersNode* pass1(thread_db* tdbb, CompilerScratch* csb) override
|
||||||
{
|
{
|
||||||
doPass1(tdbb, csb, &defaultValueNode);
|
doPass1(tdbb, csb, &defaultValuesNode);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
InitParameterNode* pass2(thread_db* tdbb, CompilerScratch* csb)
|
InitParametersNode* pass2(thread_db* tdbb, CompilerScratch* csb) override
|
||||||
{
|
{
|
||||||
ExprNode::doPass2(tdbb, csb, &defaultValueNode);
|
ExprNode::doPass2(tdbb, csb, &defaultValuesNode);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const
|
const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const override
|
||||||
{
|
{
|
||||||
if (request->req_operation == Request::req_evaluate)
|
if (request->req_operation == Request::req_evaluate)
|
||||||
{
|
{
|
||||||
dsc* defaultDesc = NULL;
|
const auto msg = request->getImpure<UCHAR>(message->impureOffset);
|
||||||
|
const auto paramCount = defaultValuesNode->items.getCount();
|
||||||
|
|
||||||
|
for (unsigned paramIndex = 0; paramIndex < paramCount; ++paramIndex)
|
||||||
|
{
|
||||||
|
const auto defaultValueNode = defaultValuesNode->items[paramIndex];
|
||||||
|
dsc* defaultDesc = nullptr;
|
||||||
|
|
||||||
if (defaultValueNode)
|
if (defaultValueNode)
|
||||||
{
|
|
||||||
defaultDesc = EVL_expr(tdbb, request, defaultValueNode);
|
defaultDesc = EVL_expr(tdbb, request, defaultValueNode);
|
||||||
|
|
||||||
if (request->req_flags & req_null)
|
const auto formatIndex = paramIndex * 2;
|
||||||
defaultDesc = NULL;
|
const auto& nullDesc = message->format->fmt_desc[formatIndex + 1];
|
||||||
}
|
fb_assert(nullDesc.dsc_dtype == dtype_short);
|
||||||
|
|
||||||
if (defaultDesc)
|
if (defaultDesc)
|
||||||
{
|
{
|
||||||
// Initialize the value. The null flag is already initialized to not-null
|
// Initialize the value. The null flag is already initialized to not-null.
|
||||||
// by the ExtMessageNode.
|
fb_assert(!*(SSHORT*) (msg + (IPTR) nullDesc.dsc_address));
|
||||||
|
|
||||||
dsc desc = message->format->fmt_desc[argNumber];
|
|
||||||
desc.dsc_address = request->getImpure<UCHAR>(
|
|
||||||
message->impureOffset + (IPTR) desc.dsc_address);
|
|
||||||
|
|
||||||
|
dsc desc = message->format->fmt_desc[formatIndex];
|
||||||
|
desc.dsc_address = msg + (IPTR) desc.dsc_address;
|
||||||
MOV_move(tdbb, defaultDesc, &desc);
|
MOV_move(tdbb, defaultDesc, &desc);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
*(SSHORT*) (msg + (IPTR) nullDesc.dsc_address) = FB_TRUE;
|
||||||
SSHORT tempValue = -1;
|
|
||||||
dsc temp;
|
|
||||||
temp.makeShort(0, &tempValue);
|
|
||||||
|
|
||||||
dsc desc = message->format->fmt_desc[argNumber + 1];
|
|
||||||
desc.dsc_address = request->getImpure<UCHAR>(
|
|
||||||
message->impureOffset + (IPTR) desc.dsc_address);
|
|
||||||
|
|
||||||
MOV_move(tdbb, &temp, &desc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
request->req_operation = Request::req_return;
|
request->req_operation = Request::req_return;
|
||||||
@ -252,27 +304,8 @@ namespace
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MessageNode* message;
|
MessageNode* const message;
|
||||||
USHORT argNumber;
|
ValueListNode* defaultValuesNode;
|
||||||
ValueExprNode* defaultValueNode;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Output parameters initialization.
|
|
||||||
class InitOutputNode : public CompoundStmtNode
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
InitOutputNode(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb,
|
|
||||||
Array<NestConst<Parameter> >& parameters, MessageNode* message)
|
|
||||||
: CompoundStmtNode(pool)
|
|
||||||
{
|
|
||||||
// Iterate over the format items, except the EOF item.
|
|
||||||
for (USHORT i = 0; i < (message->format->fmt_count / 2) * 2; i += 2)
|
|
||||||
{
|
|
||||||
InitParameterNode* init = FB_NEW_POOL(pool) InitParameterNode(
|
|
||||||
tdbb, pool, csb, parameters, message, i);
|
|
||||||
statements.add(init);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Move parameters from a message to another, validating theirs values.
|
// Move parameters from a message to another, validating theirs values.
|
||||||
@ -285,14 +318,14 @@ namespace
|
|||||||
checkMessageEof(aCheckMessageEof)
|
checkMessageEof(aCheckMessageEof)
|
||||||
{
|
{
|
||||||
// Iterate over the format items, except the EOF item.
|
// Iterate over the format items, except the EOF item.
|
||||||
for (USHORT i = 0; i < (fromMessage->format->fmt_count / 2) * 2; i += 2)
|
for (unsigned i = 0; i < (fromMessage->format->fmt_count / 2) * 2; i += 2)
|
||||||
{
|
{
|
||||||
ParameterNode* flag = FB_NEW_POOL(pool) ParameterNode(pool);
|
auto flag = FB_NEW_POOL(pool) ParameterNode(pool);
|
||||||
flag->messageNumber = fromMessage->messageNumber;
|
flag->messageNumber = fromMessage->messageNumber;
|
||||||
flag->message = fromMessage;
|
flag->message = fromMessage;
|
||||||
flag->argNumber = i + 1;
|
flag->argNumber = i + 1;
|
||||||
|
|
||||||
ParameterNode* param = FB_NEW_POOL(pool) ParameterNode(pool);
|
auto param = FB_NEW_POOL(pool) ParameterNode(pool);
|
||||||
param->messageNumber = fromMessage->messageNumber;
|
param->messageNumber = fromMessage->messageNumber;
|
||||||
param->message = fromMessage;
|
param->message = fromMessage;
|
||||||
param->argNumber = i;
|
param->argNumber = i;
|
||||||
@ -337,38 +370,6 @@ namespace
|
|||||||
MessageNode* checkMessageEof;
|
MessageNode* checkMessageEof;
|
||||||
};
|
};
|
||||||
|
|
||||||
// External function node.
|
|
||||||
class ExtFunctionNode : public SuspendNode
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ExtFunctionNode(MemoryPool& pool, const MessageNode* aExtInMessageNode, const MessageNode* aExtOutMessageNode,
|
|
||||||
const ExtEngineManager::Function* aFunction)
|
|
||||||
: SuspendNode(pool),
|
|
||||||
extInMessageNode(aExtInMessageNode),
|
|
||||||
extOutMessageNode(aExtOutMessageNode),
|
|
||||||
function(aFunction)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const
|
|
||||||
{
|
|
||||||
if (request->req_operation == Request::req_evaluate)
|
|
||||||
{
|
|
||||||
UCHAR* inMsg = extInMessageNode ? request->getImpure<UCHAR>(extInMessageNode->impureOffset) : NULL;
|
|
||||||
UCHAR* outMsg = request->getImpure<UCHAR>(extOutMessageNode->impureOffset);
|
|
||||||
|
|
||||||
function->execute(tdbb, inMsg, outMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
return SuspendNode::execute(tdbb, request, exeState);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
const MessageNode* extInMessageNode;
|
|
||||||
const MessageNode* extOutMessageNode;
|
|
||||||
const ExtEngineManager::Function* function;
|
|
||||||
};
|
|
||||||
|
|
||||||
// External procedure node.
|
// External procedure node.
|
||||||
class ExtProcedureNode : public CompoundStmtNode
|
class ExtProcedureNode : public CompoundStmtNode
|
||||||
{
|
{
|
||||||
@ -390,7 +391,7 @@ namespace
|
|||||||
statements.add(FB_NEW_POOL(pool) StallNode(pool));
|
statements.add(FB_NEW_POOL(pool) StallNode(pool));
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const
|
const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const override
|
||||||
{
|
{
|
||||||
impure_state* const impure = request->getImpure<impure_state>(impureOffset);
|
impure_state* const impure = request->getImpure<impure_state>(impureOffset);
|
||||||
ExtEngineManager::ResultSet*& resultSet = request->req_ext_resultset;
|
ExtEngineManager::ResultSet*& resultSet = request->req_ext_resultset;
|
||||||
@ -462,27 +463,27 @@ namespace
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
string internalPrint(NodePrinter& printer) const
|
string internalPrint(NodePrinter& printer) const override
|
||||||
{
|
{
|
||||||
StmtNode::internalPrint(printer);
|
StmtNode::internalPrint(printer);
|
||||||
return "ExtTriggerNode";
|
return "ExtTriggerNode";
|
||||||
}
|
}
|
||||||
|
|
||||||
void genBlr(DsqlCompilerScratch* /*dsqlScratch*/)
|
void genBlr(DsqlCompilerScratch* /*dsqlScratch*/) override
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
ExtTriggerNode* pass1(thread_db* /*tdbb*/, CompilerScratch* /*csb*/)
|
ExtTriggerNode* pass1(thread_db* /*tdbb*/, CompilerScratch* /*csb*/) override
|
||||||
{
|
{
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExtTriggerNode* pass2(thread_db* /*tdbb*/, CompilerScratch* /*csb*/)
|
ExtTriggerNode* pass2(thread_db* /*tdbb*/, CompilerScratch* /*csb*/) override
|
||||||
{
|
{
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const
|
const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const override
|
||||||
{
|
{
|
||||||
if (request->req_operation == Request::req_evaluate)
|
if (request->req_operation == Request::req_evaluate)
|
||||||
{
|
{
|
||||||
@ -502,7 +503,6 @@ namespace
|
|||||||
&request->req_rpb[n] : NULL;
|
&request->req_rpb[n] : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const ExtEngineManager::Trigger* trigger;
|
const ExtEngineManager::Trigger* trigger;
|
||||||
};
|
};
|
||||||
@ -757,13 +757,133 @@ void ExtEngineManager::ExtRoutine::PluginDeleter::operator()(IPluginBase* ptr)
|
|||||||
//---------------------
|
//---------------------
|
||||||
|
|
||||||
|
|
||||||
ExtEngineManager::Function::Function(thread_db* tdbb, ExtEngineManager* aExtManager,
|
struct ExtEngineManager::Function::Impl final
|
||||||
IExternalEngine* aEngine, RoutineMetadata* aMetadata, IExternalFunction* aFunction,
|
{
|
||||||
|
Impl(MemoryPool& pool)
|
||||||
|
: inValidations(pool),
|
||||||
|
outValidations(pool),
|
||||||
|
outDefaults(pool)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<NonPooledPair<Item, ItemInfo*>> inValidations;
|
||||||
|
Array<NonPooledPair<Item, ItemInfo*>> outValidations;
|
||||||
|
Array<unsigned> outDefaults;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
ExtEngineManager::Function::Function(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb,
|
||||||
|
ExtEngineManager* aExtManager, IExternalEngine* aEngine,
|
||||||
|
RoutineMetadata* aMetadata, IExternalFunction* aFunction,
|
||||||
|
RefPtr<IMessageMetadata> extInputParameters, RefPtr<IMessageMetadata> extOutputParameters,
|
||||||
const Jrd::Function* aUdf)
|
const Jrd::Function* aUdf)
|
||||||
: ExtRoutine(tdbb, aExtManager, aEngine, aMetadata),
|
: ExtRoutine(tdbb, aExtManager, aEngine, aMetadata),
|
||||||
function(aFunction),
|
function(aFunction),
|
||||||
udf(aUdf)
|
udf(aUdf),
|
||||||
|
impl(FB_NEW_POOL(pool) Impl(pool))
|
||||||
{
|
{
|
||||||
|
extInputFormat.reset(Routine::createFormat(pool, extInputParameters, false));
|
||||||
|
extOutputFormat.reset(Routine::createFormat(pool, extOutputParameters, true));
|
||||||
|
|
||||||
|
const bool useExtInMessage = udf->getInputFields().hasData() &&
|
||||||
|
!sameFormats(extInputFormat, udf->getInputFormat());
|
||||||
|
|
||||||
|
if (useExtInMessage)
|
||||||
|
extInputImpureOffset = csb->allocImpure(FB_ALIGNMENT, extInputFormat->fmt_length);
|
||||||
|
else
|
||||||
|
extInputFormat.reset();
|
||||||
|
|
||||||
|
const bool useExtOutMessage = udf->getOutputFields().hasData() &&
|
||||||
|
!sameFormats(extOutputFormat, udf->getOutputFormat());
|
||||||
|
|
||||||
|
if (useExtOutMessage)
|
||||||
|
extOutputImpureOffset = csb->allocImpure(FB_ALIGNMENT, extOutputFormat->fmt_length);
|
||||||
|
else
|
||||||
|
extOutputFormat.reset();
|
||||||
|
|
||||||
|
// Get input parameters that have validation expressions.
|
||||||
|
for (const auto param : udf->getInputFields())
|
||||||
|
{
|
||||||
|
FieldInfo fieldInfo;
|
||||||
|
ItemInfo itemInfo;
|
||||||
|
|
||||||
|
if (param->prm_mechanism != prm_mech_type_of &&
|
||||||
|
!fb_utils::implicit_domain(param->prm_field_source.c_str()))
|
||||||
|
{
|
||||||
|
const MetaNamePair namePair(param->prm_field_source, "");
|
||||||
|
const bool exist = csb->csb_map_field_info.get(namePair, fieldInfo);
|
||||||
|
|
||||||
|
if (!exist)
|
||||||
|
{
|
||||||
|
dsc dummyDesc;
|
||||||
|
MET_get_domain(tdbb, csb->csb_pool, param->prm_field_source, &dummyDesc, &fieldInfo);
|
||||||
|
csb->csb_map_field_info.put(namePair, fieldInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
itemInfo.field = namePair;
|
||||||
|
itemInfo.nullable = fieldInfo.nullable;
|
||||||
|
itemInfo.fullDomain = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
itemInfo.name = param->prm_name;
|
||||||
|
|
||||||
|
if (!param->prm_nullable)
|
||||||
|
itemInfo.nullable = false;
|
||||||
|
|
||||||
|
if (itemInfo.isSpecial())
|
||||||
|
{
|
||||||
|
Item item(Item::TYPE_PARAMETER, 0, (param->prm_number - 1) * 2);
|
||||||
|
csb->csb_map_item_info.put(item, itemInfo);
|
||||||
|
|
||||||
|
impl->inValidations.ensureCapacity(udf->getInputFields().getCount() - (param->prm_number - 1));
|
||||||
|
impl->inValidations.push({item, CMP_pass2_validation(tdbb, csb, item)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get output parameters that have default or validation expressions.
|
||||||
|
for (const auto param : udf->getOutputFields())
|
||||||
|
{
|
||||||
|
FieldInfo fieldInfo;
|
||||||
|
ItemInfo itemInfo;
|
||||||
|
|
||||||
|
if (param->prm_mechanism != prm_mech_type_of &&
|
||||||
|
!fb_utils::implicit_domain(param->prm_field_source.c_str()))
|
||||||
|
{
|
||||||
|
const MetaNamePair namePair(param->prm_field_source, "");
|
||||||
|
const bool exist = csb->csb_map_field_info.get(namePair, fieldInfo);
|
||||||
|
|
||||||
|
if (!exist)
|
||||||
|
{
|
||||||
|
dsc dummyDesc;
|
||||||
|
MET_get_domain(tdbb, csb->csb_pool, param->prm_field_source, &dummyDesc, &fieldInfo);
|
||||||
|
csb->csb_map_field_info.put(namePair, fieldInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fieldInfo.defaultValue)
|
||||||
|
{
|
||||||
|
impl->outDefaults.ensureCapacity(udf->getOutputFields().getCount() - param->prm_number);
|
||||||
|
impl->outDefaults.push(param->prm_number);
|
||||||
|
}
|
||||||
|
|
||||||
|
itemInfo.field = namePair;
|
||||||
|
itemInfo.nullable = fieldInfo.nullable;
|
||||||
|
itemInfo.fullDomain = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
itemInfo.name = param->prm_name;
|
||||||
|
|
||||||
|
if (!param->prm_nullable)
|
||||||
|
itemInfo.nullable = false;
|
||||||
|
|
||||||
|
if (itemInfo.isSpecial())
|
||||||
|
{
|
||||||
|
Item item(Item::TYPE_PARAMETER, 1, param->prm_number * 2);
|
||||||
|
csb->csb_map_item_info.put(item, itemInfo);
|
||||||
|
|
||||||
|
impl->outValidations.ensureCapacity(udf->getOutputFields().getCount());
|
||||||
|
impl->outValidations.push({item, CMP_pass2_validation(tdbb, csb, item)});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -774,8 +894,75 @@ ExtEngineManager::Function::~Function()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ExtEngineManager::Function::execute(thread_db* tdbb, UCHAR* inMsg, UCHAR* outMsg) const
|
// Execute an external function starting with an inactive request and ending with an active one.
|
||||||
|
void ExtEngineManager::Function::execute(thread_db* tdbb, Request* request, jrd_tra* transaction,
|
||||||
|
unsigned inMsgLength, UCHAR* inMsg, unsigned outMsgLength, UCHAR* outMsg) const
|
||||||
{
|
{
|
||||||
|
AutoSetRestore2<Request*, thread_db> autoSetRequest(
|
||||||
|
tdbb, &thread_db::getRequest, &thread_db::setRequest, request);
|
||||||
|
|
||||||
|
EXE_activate(tdbb, request, transaction);
|
||||||
|
|
||||||
|
fb_assert(inMsgLength == udf->getInputFormat()->fmt_length);
|
||||||
|
fb_assert(outMsgLength == udf->getOutputFormat()->fmt_length);
|
||||||
|
|
||||||
|
// Validate input parameters (internal message).
|
||||||
|
validateParameters(tdbb, inMsg, true);
|
||||||
|
|
||||||
|
// If there is a need for an external input message, copy the internal message to it and switch the message.
|
||||||
|
if (extInputImpureOffset.has_value())
|
||||||
|
{
|
||||||
|
const auto extInMsg = request->getImpure<UCHAR>(extInputImpureOffset.value());
|
||||||
|
copyMessage(tdbb, udf->getInputFormat(), inMsg, extInputFormat, extInMsg);
|
||||||
|
inMsg = extInMsg;
|
||||||
|
///inMsgLength = extInputFormat->fmt_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize outputs in the internal message.
|
||||||
|
{
|
||||||
|
fb_assert(udf->getOutputFormat()->fmt_desc.getCount() / 2 == udf->getOutputFields().getCount());
|
||||||
|
|
||||||
|
// Initialize everything to NULL (FB_TRUE).
|
||||||
|
memset(outMsg, FB_TRUE, udf->getOutputFormat()->fmt_length);
|
||||||
|
|
||||||
|
for (const auto paramNumber : impl->outDefaults)
|
||||||
|
{
|
||||||
|
const auto param = udf->getOutputFields()[paramNumber];
|
||||||
|
const MetaNamePair namePair(param->prm_field_source, "");
|
||||||
|
FieldInfo fieldInfo;
|
||||||
|
|
||||||
|
dsc* defaultValue = nullptr;
|
||||||
|
|
||||||
|
if (request->getStatement()->mapFieldInfo.get(namePair, fieldInfo) && fieldInfo.defaultValue)
|
||||||
|
defaultValue = EVL_expr(tdbb, request, fieldInfo.defaultValue);
|
||||||
|
|
||||||
|
const auto& paramDesc = udf->getOutputFormat()->fmt_desc[paramNumber * 2];
|
||||||
|
const auto& nullDesc = udf->getOutputFormat()->fmt_desc[paramNumber * 2 + 1];
|
||||||
|
|
||||||
|
fb_assert(nullDesc.dsc_dtype == dtype_short);
|
||||||
|
|
||||||
|
if (defaultValue)
|
||||||
|
{
|
||||||
|
dsc desc = paramDesc;
|
||||||
|
desc.dsc_address = outMsg + (IPTR) desc.dsc_address;
|
||||||
|
MOV_move(tdbb, defaultValue, &desc);
|
||||||
|
|
||||||
|
*(SSHORT*) (outMsg + (IPTR) nullDesc.dsc_address) = FB_FALSE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
*(SSHORT*) (outMsg + (IPTR) nullDesc.dsc_address) = FB_TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto extOutMsg = extOutputImpureOffset.has_value() ?
|
||||||
|
request->getImpure<UCHAR>(extOutputImpureOffset.value()) : nullptr;
|
||||||
|
|
||||||
|
// If there is a need for an external output message, copy the internal message to it.
|
||||||
|
if (extOutMsg)
|
||||||
|
copyMessage(tdbb, udf->getOutputFormat(), outMsg, extOutputFormat, extOutMsg);
|
||||||
|
|
||||||
|
// Call external.
|
||||||
|
{ // scope
|
||||||
EngineAttachmentInfo* attInfo = extManager->getEngineAttachment(tdbb, engine.get());
|
EngineAttachmentInfo* attInfo = extManager->getEngineAttachment(tdbb, engine.get());
|
||||||
const MetaString& userName = udf->invoker ? udf->invoker->getUserName() : "";
|
const MetaString& userName = udf->invoker ? udf->invoker->getUserName() : "";
|
||||||
ContextManager<IExternalFunction> ctxManager(tdbb, attInfo, function,
|
ContextManager<IExternalFunction> ctxManager(tdbb, attInfo, function,
|
||||||
@ -786,10 +973,41 @@ void ExtEngineManager::Function::execute(thread_db* tdbb, UCHAR* inMsg, UCHAR* o
|
|||||||
EngineCheckout cout(tdbb, FB_FUNCTION, checkoutType(attInfo->engine));
|
EngineCheckout cout(tdbb, FB_FUNCTION, checkoutType(attInfo->engine));
|
||||||
|
|
||||||
FbLocalStatus status;
|
FbLocalStatus status;
|
||||||
function->execute(&status, attInfo->context, inMsg, outMsg);
|
function->execute(&status, attInfo->context, inMsg, (extOutMsg ? extOutMsg : outMsg));
|
||||||
status.check();
|
status.check();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If there is an external output message, copy it to the internal message.
|
||||||
|
if (extOutMsg)
|
||||||
|
copyMessage(tdbb, extOutputFormat, extOutMsg, udf->getOutputFormat(), outMsg);
|
||||||
|
|
||||||
|
// Validate output parameters (internal message).
|
||||||
|
validateParameters(tdbb, outMsg, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExtEngineManager::Function::validateParameters(thread_db* tdbb, UCHAR* msg, bool input) const
|
||||||
|
{
|
||||||
|
const auto format = input ? udf->getInputFormat() : udf->getOutputFormat();
|
||||||
|
const auto& validations = input ? impl->inValidations : impl->outValidations;
|
||||||
|
const UCHAR messageNumber = input ? 0 : 1;
|
||||||
|
|
||||||
|
for (const auto& [item, itemInfo] : validations)
|
||||||
|
{
|
||||||
|
const unsigned paramNumber = item.index / 2;
|
||||||
|
const auto& paramDesc = format->fmt_desc[paramNumber * 2];
|
||||||
|
const auto& nullDesc = format->fmt_desc[paramNumber * 2 + 1];
|
||||||
|
|
||||||
|
fb_assert(nullDesc.dsc_dtype == dtype_short);
|
||||||
|
|
||||||
|
dsc value = paramDesc;
|
||||||
|
value.dsc_address = msg + (IPTR) value.dsc_address;
|
||||||
|
|
||||||
|
const bool isNull = *(SSHORT*) (msg + (IPTR) nullDesc.dsc_address);
|
||||||
|
|
||||||
|
EVL_validate(tdbb, Item(Item::TYPE_PARAMETER, messageNumber, paramNumber), itemInfo, &value, isNull);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//---------------------
|
//---------------------
|
||||||
|
|
||||||
@ -1108,9 +1326,9 @@ void ExtEngineManager::Trigger::setValues(thread_db* tdbb, Request* request, Arr
|
|||||||
const DeclareVariableNode* varDecl = varDecls[computedVarId++];
|
const DeclareVariableNode* varDecl = varDecls[computedVarId++];
|
||||||
impure_value* varImpure = request->getImpure<impure_value>(varDecl->impureOffset);
|
impure_value* varImpure = request->getImpure<impure_value>(varDecl->impureOffset);
|
||||||
|
|
||||||
*nullTarget = (varImpure->vlu_desc.dsc_flags & DSC_null) != 0 ? -1 : 0;
|
*nullTarget = (varImpure->vlu_desc.dsc_flags & DSC_null) ? FB_TRUE : FB_FALSE;
|
||||||
|
|
||||||
if (*nullTarget == 0)
|
if (!*nullTarget)
|
||||||
MOV_move(tdbb, &varImpure->vlu_desc, &target);
|
MOV_move(tdbb, &varImpure->vlu_desc, &target);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1118,9 +1336,9 @@ void ExtEngineManager::Trigger::setValues(thread_db* tdbb, Request* request, Arr
|
|||||||
if (!EVL_field(rpb->rpb_relation, rpb->rpb_record, fieldPos, &source))
|
if (!EVL_field(rpb->rpb_relation, rpb->rpb_record, fieldPos, &source))
|
||||||
source.dsc_flags |= DSC_null;
|
source.dsc_flags |= DSC_null;
|
||||||
|
|
||||||
*nullTarget = (source.dsc_flags & DSC_null) != 0 ? -1 : 0;
|
*nullTarget = (source.dsc_flags & DSC_null) ? FB_TRUE : FB_FALSE;
|
||||||
|
|
||||||
if (*nullTarget == 0)
|
if (!*nullTarget)
|
||||||
MOV_move(tdbb, &source, &target);
|
MOV_move(tdbb, &source, &target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1391,63 +1609,16 @@ void ExtEngineManager::makeFunction(thread_db* tdbb, CompilerScratch* csb, Jrd::
|
|||||||
status.check();
|
status.check();
|
||||||
}
|
}
|
||||||
|
|
||||||
const Format* extInputFormat = Routine::createFormat(pool, extInputParameters, false);
|
|
||||||
const Format* extOutputFormat = Routine::createFormat(pool, extOutputParameters, true);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
udf->fun_external = FB_NEW_POOL(pool) Function(tdbb, this, attInfo->engine,
|
udf->fun_external = FB_NEW_POOL(pool) Function(tdbb, pool, csb, this, attInfo->engine,
|
||||||
metadata.release(), externalFunction, udf);
|
metadata.release(), externalFunction, extInputParameters, extOutputParameters, udf);
|
||||||
|
|
||||||
MemoryPool& csbPool = csb->csb_pool;
|
// This is necessary for compilation, but will never be executed.
|
||||||
|
const auto dummyNode = FB_NEW_POOL(csb->csb_pool) CompoundStmtNode(csb->csb_pool);
|
||||||
|
|
||||||
CompoundStmtNode* mainNode = FB_NEW_POOL(csbPool) CompoundStmtNode(csbPool);
|
auto statement = udf->getStatement();
|
||||||
|
PAR_preparsed_node(tdbb, nullptr, dummyNode, nullptr, &csb, &statement, false, 0);
|
||||||
IntMessageNode* intInMessageNode = udf->getInputFields().hasData() ?
|
|
||||||
FB_NEW_POOL(csbPool) IntMessageNode(tdbb, csbPool, csb, 0,
|
|
||||||
udf->getInputFields(), udf->getInputFormat()) :
|
|
||||||
NULL;
|
|
||||||
ExtMessageNode* extInMessageNode = NULL;
|
|
||||||
|
|
||||||
if (intInMessageNode)
|
|
||||||
{
|
|
||||||
mainNode->statements.add(intInMessageNode);
|
|
||||||
|
|
||||||
extInMessageNode = FB_NEW_POOL(csbPool) ExtMessageNode(tdbb, csbPool, csb, 2, extInputFormat);
|
|
||||||
mainNode->statements.add(extInMessageNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
IntMessageNode* intOutMessageNode = FB_NEW_POOL(csbPool) IntMessageNode(tdbb, csbPool, csb, 1,
|
|
||||||
udf->getOutputFields(), udf->getOutputFormat());
|
|
||||||
mainNode->statements.add(intOutMessageNode);
|
|
||||||
|
|
||||||
ExtMessageNode* extOutMessageNode = FB_NEW_POOL(csbPool) ExtMessageNode(tdbb, csbPool, csb, 3,
|
|
||||||
extOutputFormat);
|
|
||||||
mainNode->statements.add(extOutMessageNode);
|
|
||||||
|
|
||||||
// Initialize the output fields into the external message.
|
|
||||||
InitOutputNode* initOutputNode = FB_NEW_POOL(csbPool) InitOutputNode(
|
|
||||||
tdbb, csbPool, csb, udf->getOutputFields(), extOutMessageNode);
|
|
||||||
mainNode->statements.add(initOutputNode);
|
|
||||||
|
|
||||||
if (intInMessageNode)
|
|
||||||
{
|
|
||||||
ReceiveNode* receiveNode = intInMessageNode ? FB_NEW_POOL(csbPool) ReceiveNode(csbPool) : NULL;
|
|
||||||
receiveNode->message = intInMessageNode;
|
|
||||||
receiveNode->statement = FB_NEW_POOL(csbPool) MessageMoverNode(
|
|
||||||
csbPool, intInMessageNode, extInMessageNode);
|
|
||||||
mainNode->statements.add(receiveNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
ExtFunctionNode* extFunctionNode = FB_NEW_POOL(csbPool) ExtFunctionNode(csbPool,
|
|
||||||
extInMessageNode, extOutMessageNode, udf->fun_external);
|
|
||||||
mainNode->statements.add(extFunctionNode);
|
|
||||||
extFunctionNode->message = intOutMessageNode;
|
|
||||||
extFunctionNode->statement = FB_NEW_POOL(csbPool) MessageMoverNode(
|
|
||||||
csbPool, extOutMessageNode, intOutMessageNode);
|
|
||||||
|
|
||||||
Statement* statement = udf->getStatement();
|
|
||||||
PAR_preparsed_node(tdbb, NULL, mainNode, NULL, &csb, &statement, false, 0);
|
|
||||||
udf->setStatement(statement);
|
udf->setStatement(statement);
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
@ -1563,9 +1734,9 @@ void ExtEngineManager::makeProcedure(thread_db* tdbb, CompilerScratch* csb, jrd_
|
|||||||
mainNode->statements.add(extOutMessageNode);
|
mainNode->statements.add(extOutMessageNode);
|
||||||
|
|
||||||
// Initialize the output fields into the external message.
|
// Initialize the output fields into the external message.
|
||||||
InitOutputNode* initOutputNode = FB_NEW_POOL(csbPool) InitOutputNode(
|
InitParametersNode* initParametersNode = FB_NEW_POOL(csbPool) InitParametersNode(
|
||||||
tdbb, csbPool, csb, prc->getOutputFields(), extOutMessageNode);
|
tdbb, csbPool, csb, prc->getOutputFields(), extOutMessageNode);
|
||||||
mainNode->statements.add(initOutputNode);
|
mainNode->statements.add(initParametersNode);
|
||||||
|
|
||||||
ReceiveNode* receiveNode = intInMessageNode ?
|
ReceiveNode* receiveNode = intInMessageNode ?
|
||||||
FB_NEW_POOL(csbPool) ReceiveNode(csbPool) : NULL;
|
FB_NEW_POOL(csbPool) ReceiveNode(csbPool) : NULL;
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include "firebird/Interface.h"
|
#include "firebird/Interface.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include "../common/classes/array.h"
|
#include "../common/classes/array.h"
|
||||||
#include "../common/classes/fb_string.h"
|
#include "../common/classes/fb_string.h"
|
||||||
@ -218,6 +219,7 @@ public:
|
|||||||
public:
|
public:
|
||||||
ExtRoutine(thread_db* tdbb, ExtEngineManager* aExtManager,
|
ExtRoutine(thread_db* tdbb, ExtEngineManager* aExtManager,
|
||||||
Firebird::IExternalEngine* aEngine, RoutineMetadata* aMetadata);
|
Firebird::IExternalEngine* aEngine, RoutineMetadata* aMetadata);
|
||||||
|
virtual ~ExtRoutine() = default;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class PluginDeleter
|
class PluginDeleter
|
||||||
@ -233,42 +235,56 @@ public:
|
|||||||
Database* database;
|
Database* database;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Function : public ExtRoutine
|
class Function final : public ExtRoutine
|
||||||
{
|
{
|
||||||
|
private:
|
||||||
|
struct Impl; // hack to avoid circular inclusion of headers
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Function(thread_db* tdbb, ExtEngineManager* aExtManager,
|
Function(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, ExtEngineManager* aExtManager,
|
||||||
Firebird::IExternalEngine* aEngine,
|
Firebird::IExternalEngine* aEngine,
|
||||||
RoutineMetadata* aMetadata,
|
RoutineMetadata* aMetadata,
|
||||||
Firebird::IExternalFunction* aFunction,
|
Firebird::IExternalFunction* aFunction,
|
||||||
|
Firebird::RefPtr<Firebird::IMessageMetadata> extInputParameters,
|
||||||
|
Firebird::RefPtr<Firebird::IMessageMetadata> extOutputParameters,
|
||||||
const Jrd::Function* aUdf);
|
const Jrd::Function* aUdf);
|
||||||
~Function();
|
~Function() override;
|
||||||
|
|
||||||
void execute(thread_db* tdbb, UCHAR* inMsg, UCHAR* outMsg) const;
|
void execute(thread_db* tdbb, Request* request, jrd_tra* transaction,
|
||||||
|
unsigned inMsgLength, UCHAR* inMsg, unsigned outMsgLength, UCHAR* outMsg) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void validateParameters(thread_db* tdbb, UCHAR* msg, bool input) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Firebird::IExternalFunction* function;
|
Firebird::IExternalFunction* function;
|
||||||
const Jrd::Function* udf;
|
const Jrd::Function* udf;
|
||||||
|
Firebird::AutoPtr<Format> extInputFormat;
|
||||||
|
Firebird::AutoPtr<Format> extOutputFormat;
|
||||||
|
Firebird::AutoPtr<Impl> impl;
|
||||||
|
std::optional<ULONG> extInputImpureOffset;
|
||||||
|
std::optional<ULONG> extOutputImpureOffset;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ResultSet;
|
class ResultSet;
|
||||||
|
|
||||||
class Procedure : public ExtRoutine
|
class Procedure final : public ExtRoutine
|
||||||
{
|
{
|
||||||
|
friend class ResultSet;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Procedure(thread_db* tdbb, ExtEngineManager* aExtManager,
|
Procedure(thread_db* tdbb, ExtEngineManager* aExtManager,
|
||||||
Firebird::IExternalEngine* aEngine,
|
Firebird::IExternalEngine* aEngine,
|
||||||
RoutineMetadata* aMetadata,
|
RoutineMetadata* aMetadata,
|
||||||
Firebird::IExternalProcedure* aProcedure,
|
Firebird::IExternalProcedure* aProcedure,
|
||||||
const jrd_prc* aPrc);
|
const jrd_prc* aPrc);
|
||||||
~Procedure();
|
~Procedure() override;
|
||||||
|
|
||||||
ResultSet* open(thread_db* tdbb, UCHAR* inMsg, UCHAR* outMsg) const;
|
ResultSet* open(thread_db* tdbb, UCHAR* inMsg, UCHAR* outMsg) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Firebird::IExternalProcedure* procedure;
|
Firebird::IExternalProcedure* procedure;
|
||||||
const jrd_prc* prc;
|
const jrd_prc* prc;
|
||||||
|
|
||||||
friend class ResultSet;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class ResultSet
|
class ResultSet
|
||||||
@ -288,13 +304,13 @@ public:
|
|||||||
USHORT charSet;
|
USHORT charSet;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Trigger : public ExtRoutine
|
class Trigger final : public ExtRoutine
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Trigger(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, ExtEngineManager* aExtManager,
|
Trigger(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, ExtEngineManager* aExtManager,
|
||||||
Firebird::IExternalEngine* aEngine, RoutineMetadata* aMetadata,
|
Firebird::IExternalEngine* aEngine, RoutineMetadata* aMetadata,
|
||||||
Firebird::IExternalTrigger* aTrigger, const Jrd::Trigger* aTrg);
|
Firebird::IExternalTrigger* aTrigger, const Jrd::Trigger* aTrg);
|
||||||
~Trigger();
|
~Trigger() override;
|
||||||
|
|
||||||
void execute(thread_db* tdbb, Request* request, unsigned action,
|
void execute(thread_db* tdbb, Request* request, unsigned action,
|
||||||
record_param* oldRpb, record_param* newRpb) const;
|
record_param* oldRpb, record_param* newRpb) const;
|
||||||
|
@ -32,7 +32,7 @@ namespace Jrd
|
|||||||
class ValueListNode;
|
class ValueListNode;
|
||||||
class QualifiedName;
|
class QualifiedName;
|
||||||
|
|
||||||
class Function : public Routine
|
class Function final : public Routine
|
||||||
{
|
{
|
||||||
static const char* const EXCEPTION_MESSAGE;
|
static const char* const EXCEPTION_MESSAGE;
|
||||||
|
|
||||||
@ -58,29 +58,30 @@ namespace Jrd
|
|||||||
static int blockingAst(void*);
|
static int blockingAst(void*);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual int getObjectType() const
|
int getObjectType() const override
|
||||||
{
|
{
|
||||||
return obj_udf;
|
return obj_udf;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual SLONG getSclType() const
|
SLONG getSclType() const override
|
||||||
{
|
{
|
||||||
return obj_functions;
|
return obj_functions;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool checkCache(thread_db* tdbb) const;
|
bool checkCache(thread_db* tdbb) const override;
|
||||||
virtual void clearCache(thread_db* tdbb);
|
void clearCache(thread_db* tdbb) override;
|
||||||
|
|
||||||
virtual ~Function()
|
~Function() override
|
||||||
{
|
{
|
||||||
delete fun_external;
|
delete fun_external;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void releaseExternal()
|
void releaseExternal() override
|
||||||
{
|
{
|
||||||
delete fun_external;
|
delete fun_external;
|
||||||
fun_external = NULL;
|
fun_external = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int (*fun_entrypoint)(); // function entrypoint
|
int (*fun_entrypoint)(); // function entrypoint
|
||||||
USHORT fun_inputs; // input arguments
|
USHORT fun_inputs; // input arguments
|
||||||
@ -93,7 +94,7 @@ namespace Jrd
|
|||||||
const ExtEngineManager::Function* fun_external;
|
const ExtEngineManager::Function* fun_external;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool reload(thread_db* tdbb);
|
bool reload(thread_db* tdbb) override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -848,18 +848,9 @@ void EXE_send(thread_db* tdbb, Request* request, USHORT msg, ULONG length, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void EXE_start(thread_db* tdbb, Request* request, jrd_tra* transaction)
|
// Mark a request as active.
|
||||||
|
void EXE_activate(thread_db* tdbb, Request* request, jrd_tra* transaction)
|
||||||
{
|
{
|
||||||
/**************************************
|
|
||||||
*
|
|
||||||
* E X E _ s t a r t
|
|
||||||
*
|
|
||||||
**************************************
|
|
||||||
*
|
|
||||||
* Functional description
|
|
||||||
* Start an execution running.
|
|
||||||
*
|
|
||||||
**************************************/
|
|
||||||
SET_TDBB(tdbb);
|
SET_TDBB(tdbb);
|
||||||
|
|
||||||
BLKCHK(request, type_req);
|
BLKCHK(request, type_req);
|
||||||
@ -923,10 +914,15 @@ void EXE_start(thread_db* tdbb, Request* request, jrd_tra* transaction)
|
|||||||
request->req_src_column = 0;
|
request->req_src_column = 0;
|
||||||
|
|
||||||
TRA_setup_request_snapshot(tdbb, request);
|
TRA_setup_request_snapshot(tdbb, request);
|
||||||
|
}
|
||||||
|
|
||||||
execute_looper(tdbb, request, transaction,
|
|
||||||
request->getStatement()->topNode,
|
// Start and execute a request.
|
||||||
Request::req_evaluate);
|
void EXE_start(thread_db* tdbb, Request* request, jrd_tra* transaction)
|
||||||
|
{
|
||||||
|
EXE_activate(tdbb, request, transaction);
|
||||||
|
|
||||||
|
execute_looper(tdbb, request, transaction, request->getStatement()->topNode, Request::req_evaluate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -428,9 +428,8 @@ public:
|
|||||||
bool fullDomain;
|
bool fullDomain;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef Firebird::GenericMap<Firebird::Pair<Firebird::Left<MetaNamePair, FieldInfo> > >
|
typedef Firebird::LeftPooledMap<MetaNamePair, FieldInfo> MapFieldInfo;
|
||||||
MapFieldInfo;
|
typedef Firebird::RightPooledMap<Item, ItemInfo> MapItemInfo;
|
||||||
typedef Firebird::GenericMap<Firebird::Pair<Firebird::Right<Item, ItemInfo> > > MapItemInfo;
|
|
||||||
|
|
||||||
// Compile scratch block
|
// Compile scratch block
|
||||||
|
|
||||||
|
@ -51,6 +51,7 @@ void EXE_execute_triggers(Jrd::thread_db*, Jrd::TrigVector**, Jrd::record_param*
|
|||||||
void EXE_receive(Jrd::thread_db*, Jrd::Request*, USHORT, ULONG, void*, bool = false);
|
void EXE_receive(Jrd::thread_db*, Jrd::Request*, USHORT, ULONG, void*, bool = false);
|
||||||
void EXE_release(Jrd::thread_db*, Jrd::Request*);
|
void EXE_release(Jrd::thread_db*, Jrd::Request*);
|
||||||
void EXE_send(Jrd::thread_db*, Jrd::Request*, USHORT, ULONG, const void*);
|
void EXE_send(Jrd::thread_db*, Jrd::Request*, USHORT, ULONG, const void*);
|
||||||
|
void EXE_activate(Jrd::thread_db*, Jrd::Request*, Jrd::jrd_tra*);
|
||||||
void EXE_start(Jrd::thread_db*, Jrd::Request*, Jrd::jrd_tra*);
|
void EXE_start(Jrd::thread_db*, Jrd::Request*, Jrd::jrd_tra*);
|
||||||
void EXE_unwind(Jrd::thread_db*, Jrd::Request*);
|
void EXE_unwind(Jrd::thread_db*, Jrd::Request*);
|
||||||
|
|
||||||
|
@ -211,7 +211,7 @@ const int DYN_REQUESTS = 2;
|
|||||||
|
|
||||||
// Procedure block
|
// Procedure block
|
||||||
|
|
||||||
class jrd_prc : public Routine
|
class jrd_prc final : public Routine
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
const Format* prc_record_format;
|
const Format* prc_record_format;
|
||||||
@ -233,38 +233,38 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual int getObjectType() const
|
int getObjectType() const override
|
||||||
{
|
{
|
||||||
return obj_procedure;
|
return obj_procedure;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual SLONG getSclType() const
|
SLONG getSclType() const override
|
||||||
{
|
{
|
||||||
return obj_procedures;
|
return obj_procedures;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void releaseFormat()
|
void releaseFormat() override
|
||||||
{
|
{
|
||||||
delete prc_record_format;
|
delete prc_record_format;
|
||||||
prc_record_format = NULL;
|
prc_record_format = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~jrd_prc()
|
~jrd_prc() override
|
||||||
{
|
{
|
||||||
delete prc_external;
|
delete prc_external;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool checkCache(thread_db* tdbb) const;
|
bool checkCache(thread_db* tdbb) const override;
|
||||||
virtual void clearCache(thread_db* tdbb);
|
void clearCache(thread_db* tdbb) override;
|
||||||
|
|
||||||
virtual void releaseExternal()
|
void releaseExternal() override
|
||||||
{
|
{
|
||||||
delete prc_external;
|
delete prc_external;
|
||||||
prc_external = NULL;
|
prc_external = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool reload(thread_db* tdbb); // impl is in met.epp
|
bool reload(thread_db* tdbb) override; // impl is in met.epp
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user