2009-12-21 18:53:49 +01:00
|
|
|
/*
|
|
|
|
* The contents of this file are subject to the Interbase 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.Inprise.com/IPL.html
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an
|
|
|
|
* "AS IS" basis, 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 Inprise Corporation
|
|
|
|
* and its predecessors. Portions created by Inprise Corporation are
|
|
|
|
* Copyright (C) Inprise Corporation.
|
|
|
|
*
|
|
|
|
* All Rights Reserved.
|
|
|
|
* Contributor(s): ______________________________________.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "firebird.h"
|
2010-10-13 12:39:52 +02:00
|
|
|
#include "../common/common.h"
|
|
|
|
#include "../common/gdsassert.h"
|
2009-12-21 18:53:49 +01:00
|
|
|
#include "../jrd/flags.h"
|
|
|
|
#include "../jrd/ibase.h"
|
|
|
|
#include "../jrd/jrd.h"
|
|
|
|
#include "../jrd/val.h"
|
|
|
|
#include "../jrd/irq.h"
|
|
|
|
#include "../jrd/tra.h"
|
|
|
|
#include "../jrd/lck.h"
|
|
|
|
#include "../jrd/req.h"
|
|
|
|
#include "../jrd/exe.h"
|
|
|
|
#include "../jrd/blb.h"
|
|
|
|
#include "../jrd/met.h"
|
2009-12-25 12:15:13 +01:00
|
|
|
#include "../jrd/align.h"
|
2009-12-21 18:53:49 +01:00
|
|
|
#include "../jrd/blb_proto.h"
|
|
|
|
#include "../jrd/cmp_proto.h"
|
2010-10-12 10:02:57 +02:00
|
|
|
#include "../common/dsc_proto.h"
|
2009-12-25 12:15:13 +01:00
|
|
|
#include "../jrd/evl_proto.h"
|
2009-12-21 18:53:49 +01:00
|
|
|
#include "../jrd/exe_proto.h"
|
|
|
|
#include "../jrd/flu_proto.h"
|
|
|
|
#include "../jrd/fun_proto.h"
|
|
|
|
#include "../jrd/lck_proto.h"
|
|
|
|
#include "../jrd/met_proto.h"
|
2009-12-25 12:15:13 +01:00
|
|
|
#include "../jrd/mov_proto.h"
|
2009-12-21 18:53:49 +01:00
|
|
|
#include "../jrd/par_proto.h"
|
|
|
|
#include "../jrd/vio_proto.h"
|
|
|
|
#include "../jrd/thread_proto.h"
|
|
|
|
#include "../common/utils_proto.h"
|
|
|
|
#include "../jrd/DebugInterface.h"
|
|
|
|
|
|
|
|
#include "../jrd/Function.h"
|
|
|
|
|
|
|
|
using namespace Firebird;
|
|
|
|
using namespace Jrd;
|
|
|
|
|
|
|
|
DATABASE DB = FILENAME "ODS.RDB";
|
|
|
|
|
2009-12-21 21:55:07 +01:00
|
|
|
const char* const Function::EXCEPTION_MESSAGE = "The user defined function: \t%s\n\t referencing"
|
|
|
|
" entrypoint: \t%s\n\t in module: \t%s\n\tcaused the fatal exception:";
|
2009-12-21 18:53:49 +01:00
|
|
|
|
|
|
|
Function* Function::lookup(thread_db* tdbb, USHORT id, bool return_deleted, bool noscan, USHORT flags)
|
|
|
|
{
|
2010-04-02 23:48:15 +02:00
|
|
|
Jrd::Attachment* attachment = tdbb->getAttachment();
|
2009-12-21 18:53:49 +01:00
|
|
|
Database* const dbb = tdbb->getDatabase();
|
|
|
|
Function* check_function = NULL;
|
|
|
|
|
2009-12-21 21:55:07 +01:00
|
|
|
Function* function = (id < dbb->dbb_functions.getCount()) ? dbb->dbb_functions[id] : NULL;
|
2009-12-21 18:53:49 +01:00
|
|
|
|
2009-12-27 23:05:22 +01:00
|
|
|
if (function && function->getId() == id &&
|
2009-12-21 18:53:49 +01:00
|
|
|
!(function->fun_flags & FUN_being_scanned) &&
|
|
|
|
((function->fun_flags & FUN_scanned) || noscan) &&
|
|
|
|
!(function->fun_flags & FUN_being_altered) &&
|
|
|
|
(!(function->fun_flags & FUN_obsolete) || return_deleted))
|
|
|
|
{
|
|
|
|
if (!(function->fun_flags & FUN_check_existence))
|
|
|
|
{
|
|
|
|
return function;
|
|
|
|
}
|
|
|
|
|
|
|
|
check_function = function;
|
|
|
|
LCK_lock(tdbb, check_function->fun_existence_lock, LCK_SR, LCK_WAIT);
|
|
|
|
}
|
|
|
|
|
|
|
|
// We need to look up the function in RDB$FUNCTIONS
|
|
|
|
|
|
|
|
function = NULL;
|
|
|
|
|
2010-04-18 17:04:52 +02:00
|
|
|
AutoCacheRequest request(tdbb, irq_l_fun_id, IRQ_REQUESTS);
|
2009-12-21 18:53:49 +01:00
|
|
|
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
|
|
X IN RDB$FUNCTIONS WITH X.RDB$FUNCTION_ID EQ id
|
2010-04-18 17:04:52 +02:00
|
|
|
{
|
2009-12-21 18:53:49 +01:00
|
|
|
function = loadMetadata(tdbb, X.RDB$FUNCTION_ID, noscan, flags);
|
2010-04-18 17:04:52 +02:00
|
|
|
}
|
|
|
|
END_FOR
|
2009-12-21 18:53:49 +01:00
|
|
|
|
|
|
|
if (check_function)
|
|
|
|
{
|
|
|
|
check_function->fun_flags &= ~FUN_check_existence;
|
|
|
|
if (check_function != function)
|
|
|
|
{
|
|
|
|
LCK_release(tdbb, check_function->fun_existence_lock);
|
2009-12-24 12:48:17 +01:00
|
|
|
check_function->fun_flags |= FUN_obsolete;
|
2009-12-21 18:53:49 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return function;
|
|
|
|
}
|
|
|
|
|
|
|
|
Function* Function::lookup(thread_db* tdbb, const QualifiedName& name, bool noscan)
|
|
|
|
{
|
2010-04-02 23:48:15 +02:00
|
|
|
Jrd::Attachment* attachment = tdbb->getAttachment();
|
2009-12-21 18:53:49 +01:00
|
|
|
Database* const dbb = tdbb->getDatabase();
|
|
|
|
|
|
|
|
Function* check_function = NULL;
|
|
|
|
|
|
|
|
// See if we already know the function by name
|
|
|
|
|
|
|
|
for (Function** iter = dbb->dbb_functions.begin(); iter < dbb->dbb_functions.end(); ++iter)
|
|
|
|
{
|
|
|
|
Function* const function = *iter;
|
|
|
|
|
|
|
|
if (function && !(function->fun_flags & FUN_obsolete) &&
|
|
|
|
((function->fun_flags & FUN_scanned) || noscan) &&
|
|
|
|
!(function->fun_flags & FUN_being_scanned) &&
|
|
|
|
!(function->fun_flags & FUN_being_altered))
|
|
|
|
{
|
2009-12-27 23:05:22 +01:00
|
|
|
if (function->getName() == name)
|
2009-12-21 18:53:49 +01:00
|
|
|
{
|
|
|
|
if (function->fun_flags & FUN_check_existence)
|
|
|
|
{
|
|
|
|
check_function = function;
|
|
|
|
LCK_lock(tdbb, check_function->fun_existence_lock, LCK_SR, LCK_WAIT);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return function;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// We need to look up the function in RDB$FUNCTIONS
|
|
|
|
|
|
|
|
Function* function = NULL;
|
|
|
|
|
2010-04-18 17:04:52 +02:00
|
|
|
AutoCacheRequest request(tdbb, irq_l_fun_name, IRQ_REQUESTS);
|
2009-12-21 18:53:49 +01:00
|
|
|
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
|
|
X IN RDB$FUNCTIONS
|
|
|
|
WITH X.RDB$FUNCTION_NAME EQ name.identifier.c_str() AND
|
2010-01-27 05:32:27 +01:00
|
|
|
X.RDB$PACKAGE_NAME EQUIV NULLIF(name.package.c_str(), '')
|
2010-04-18 17:04:52 +02:00
|
|
|
{
|
2009-12-21 18:53:49 +01:00
|
|
|
function = loadMetadata(tdbb, X.RDB$FUNCTION_ID, noscan, 0);
|
2010-04-18 17:04:52 +02:00
|
|
|
}
|
|
|
|
END_FOR
|
2009-12-21 18:53:49 +01:00
|
|
|
|
|
|
|
if (check_function)
|
|
|
|
{
|
|
|
|
check_function->fun_flags &= ~FUN_check_existence;
|
|
|
|
if (check_function != function)
|
|
|
|
{
|
|
|
|
LCK_release(tdbb, check_function->fun_existence_lock);
|
|
|
|
check_function->fun_flags |= FUN_obsolete;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return function;
|
|
|
|
}
|
|
|
|
|
|
|
|
Function* Function::loadMetadata(thread_db* tdbb, USHORT id, bool noscan, USHORT flags)
|
|
|
|
{
|
2010-04-02 23:48:15 +02:00
|
|
|
Jrd::Attachment* attachment = tdbb->getAttachment();
|
|
|
|
jrd_tra* sysTransaction = attachment->getSysTransaction();
|
2009-12-21 18:53:49 +01:00
|
|
|
Database* const dbb = tdbb->getDatabase();
|
|
|
|
|
|
|
|
if (id >= dbb->dbb_functions.getCount())
|
|
|
|
{
|
|
|
|
dbb->dbb_functions.grow(id + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
Database::CheckoutLockGuard guard(dbb, dbb->dbb_meta_mutex);
|
|
|
|
|
|
|
|
Function* function = dbb->dbb_functions[id];
|
|
|
|
|
|
|
|
if (function && !(function->fun_flags & FUN_obsolete))
|
|
|
|
{
|
|
|
|
// Make sure FUN_being_scanned and FUN_scanned are not set at the same time
|
|
|
|
fb_assert(!(function->fun_flags & FUN_being_scanned) || !(function->fun_flags & FUN_scanned));
|
|
|
|
|
|
|
|
if ((function->fun_flags & FUN_being_scanned) || (function->fun_flags & FUN_scanned))
|
|
|
|
{
|
|
|
|
return function;
|
|
|
|
}
|
|
|
|
}
|
2009-12-22 01:08:49 +01:00
|
|
|
|
2009-12-21 18:53:49 +01:00
|
|
|
if (!function)
|
|
|
|
{
|
|
|
|
function = FB_NEW(*dbb->dbb_permanent) Function(*dbb->dbb_permanent);
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
function->fun_flags |= (FUN_being_scanned | flags);
|
|
|
|
function->fun_flags &= ~FUN_obsolete;
|
|
|
|
|
2009-12-27 23:05:22 +01:00
|
|
|
function->setId(id);
|
2009-12-21 18:53:49 +01:00
|
|
|
dbb->dbb_functions[id] = function;
|
|
|
|
|
|
|
|
if (!function->fun_existence_lock)
|
|
|
|
{
|
|
|
|
Lock* const lock = FB_NEW_RPT(*dbb->dbb_permanent, 0) Lock;
|
|
|
|
function->fun_existence_lock = lock;
|
|
|
|
lock->lck_parent = dbb->dbb_lock;
|
|
|
|
lock->lck_dbb = dbb;
|
2009-12-27 23:05:22 +01:00
|
|
|
lock->lck_key.lck_long = function->getId();
|
2009-12-21 18:53:49 +01:00
|
|
|
lock->lck_length = sizeof(lock->lck_key.lck_long);
|
|
|
|
lock->lck_type = LCK_fun_exist;
|
|
|
|
lock->lck_owner_handle = LCK_get_owner_handle(tdbb, lock->lck_type);
|
|
|
|
lock->lck_object = function;
|
|
|
|
lock->lck_ast = blockingAst;
|
|
|
|
}
|
|
|
|
|
|
|
|
LCK_lock(tdbb, function->fun_existence_lock, LCK_SR, LCK_WAIT);
|
|
|
|
|
|
|
|
if (!noscan)
|
|
|
|
{
|
2009-12-25 08:09:02 +01:00
|
|
|
bool valid_blr = true;
|
2009-12-21 18:53:49 +01:00
|
|
|
|
2010-04-18 17:04:52 +02:00
|
|
|
AutoCacheRequest request_fun(tdbb, irq_l_functions, IRQ_REQUESTS);
|
2009-12-21 18:53:49 +01:00
|
|
|
|
2010-10-12 13:36:51 +02:00
|
|
|
Argument temp[MAX_UDF_ARGUMENTS + 1];
|
2009-12-21 18:53:49 +01:00
|
|
|
|
|
|
|
FOR(REQUEST_HANDLE request_fun)
|
|
|
|
X IN RDB$FUNCTIONS
|
|
|
|
WITH X.RDB$FUNCTION_ID EQ id
|
2010-04-18 17:04:52 +02:00
|
|
|
{
|
2009-12-27 23:05:22 +01:00
|
|
|
function->setName(QualifiedName(X.RDB$FUNCTION_NAME,
|
|
|
|
(X.RDB$PACKAGE_NAME.NULL ? NULL : X.RDB$PACKAGE_NAME)));
|
2009-12-21 18:53:49 +01:00
|
|
|
|
|
|
|
if (!X.RDB$SECURITY_CLASS.NULL)
|
|
|
|
{
|
2009-12-27 23:05:22 +01:00
|
|
|
function->setSecurityName(X.RDB$SECURITY_CLASS);
|
2009-12-21 18:53:49 +01:00
|
|
|
}
|
|
|
|
else if (!X.RDB$PACKAGE_NAME.NULL)
|
|
|
|
{
|
|
|
|
AutoCacheRequest requestHandle(tdbb, irq_l_procedure_pkg_class, IRQ_REQUESTS);
|
|
|
|
|
|
|
|
FOR (REQUEST_HANDLE requestHandle)
|
|
|
|
PKG IN RDB$PACKAGES
|
|
|
|
WITH PKG.RDB$PACKAGE_NAME EQ X.RDB$PACKAGE_NAME
|
|
|
|
|
|
|
|
if (!PKG.RDB$SECURITY_CLASS.NULL)
|
|
|
|
{
|
2009-12-27 23:05:22 +01:00
|
|
|
function->setSecurityName(PKG.RDB$SECURITY_CLASS);
|
2009-12-21 18:53:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
END_FOR
|
|
|
|
}
|
|
|
|
|
2009-12-25 15:37:44 +01:00
|
|
|
size_t count = 0;
|
2009-12-21 18:53:49 +01:00
|
|
|
memset(temp, 0, sizeof(temp));
|
|
|
|
ULONG length = 0;
|
|
|
|
|
2009-12-25 15:37:44 +01:00
|
|
|
function->fun_inputs = 0;
|
|
|
|
function->fun_defaults = 0;
|
|
|
|
|
2010-04-18 17:04:52 +02:00
|
|
|
AutoCacheRequest request_arg(tdbb, irq_l_args, IRQ_REQUESTS);
|
2009-12-21 18:53:49 +01:00
|
|
|
|
|
|
|
FOR(REQUEST_HANDLE request_arg)
|
|
|
|
Y IN RDB$FUNCTION_ARGUMENTS
|
2009-12-27 23:05:22 +01:00
|
|
|
WITH Y.RDB$FUNCTION_NAME EQ function->getName().identifier.c_str() AND
|
2010-01-27 05:32:27 +01:00
|
|
|
Y.RDB$PACKAGE_NAME EQUIV NULLIF(function->getName().package.c_str(), '')
|
2009-12-21 18:53:49 +01:00
|
|
|
SORTED BY Y.RDB$ARGUMENT_POSITION
|
2010-04-18 17:04:52 +02:00
|
|
|
{
|
2009-12-21 18:53:49 +01:00
|
|
|
if (Y.RDB$ARGUMENT_POSITION != X.RDB$RETURN_ARGUMENT)
|
2009-12-25 15:37:44 +01:00
|
|
|
function->fun_inputs++;
|
2009-12-21 18:53:49 +01:00
|
|
|
|
2010-10-12 13:36:51 +02:00
|
|
|
Argument* const tail = temp + Y.RDB$ARGUMENT_POSITION;
|
2009-12-21 18:53:49 +01:00
|
|
|
tail->fun_mechanism = (FUN_T) Y.RDB$MECHANISM;
|
|
|
|
|
|
|
|
Parameter* const parameter = FB_NEW(*dbb->dbb_permanent) Parameter(*dbb->dbb_permanent);
|
|
|
|
tail->fun_parameter = parameter;
|
|
|
|
parameter->prm_number = Y.RDB$ARGUMENT_POSITION;
|
|
|
|
parameter->prm_name = Y.RDB$ARGUMENT_NAME.NULL ? "" : Y.RDB$ARGUMENT_NAME;
|
|
|
|
parameter->prm_nullable = Y.RDB$NULL_FLAG.NULL || Y.RDB$NULL_FLAG == 0;
|
|
|
|
parameter->prm_mechanism = Y.RDB$ARGUMENT_MECHANISM.NULL ?
|
|
|
|
prm_mech_normal : (prm_mech_t) Y.RDB$ARGUMENT_MECHANISM;
|
|
|
|
|
|
|
|
const SSHORT collation_id_null = Y.RDB$COLLATION_ID.NULL;
|
|
|
|
const SSHORT collation_id = Y.RDB$COLLATION_ID;
|
2009-12-25 20:29:58 +01:00
|
|
|
|
2009-12-25 15:37:44 +01:00
|
|
|
SSHORT default_value_null = Y.RDB$DEFAULT_VALUE.NULL;
|
|
|
|
bid default_value = Y.RDB$DEFAULT_VALUE;
|
2009-12-21 18:53:49 +01:00
|
|
|
|
|
|
|
if (!Y.RDB$FIELD_SOURCE.NULL)
|
|
|
|
{
|
|
|
|
parameter->prm_field_source = Y.RDB$FIELD_SOURCE;
|
|
|
|
|
2010-04-18 17:04:52 +02:00
|
|
|
AutoCacheRequest request_arg_fld(tdbb, irq_l_arg_fld, IRQ_REQUESTS);
|
2009-12-21 18:53:49 +01:00
|
|
|
|
|
|
|
FOR(REQUEST_HANDLE request_arg_fld)
|
|
|
|
F IN RDB$FIELDS
|
|
|
|
WITH F.RDB$FIELD_NAME = Y.RDB$FIELD_SOURCE
|
2010-04-18 17:04:52 +02:00
|
|
|
{
|
2009-12-21 18:53:49 +01:00
|
|
|
DSC_make_descriptor(¶meter->prm_desc, F.RDB$FIELD_TYPE,
|
|
|
|
F.RDB$FIELD_SCALE, F.RDB$FIELD_LENGTH,
|
|
|
|
F.RDB$FIELD_SUB_TYPE, F.RDB$CHARACTER_SET_ID,
|
|
|
|
(collation_id_null ? F.RDB$COLLATION_ID : collation_id));
|
|
|
|
|
2009-12-25 15:37:44 +01:00
|
|
|
if (default_value_null && fb_utils::implicit_domain(F.RDB$FIELD_NAME))
|
|
|
|
{
|
|
|
|
default_value_null = F.RDB$DEFAULT_VALUE.NULL;
|
|
|
|
default_value = F.RDB$DEFAULT_VALUE;
|
|
|
|
}
|
2010-04-18 17:04:52 +02:00
|
|
|
}
|
2009-12-21 18:53:49 +01:00
|
|
|
END_FOR
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DSC_make_descriptor(¶meter->prm_desc, Y.RDB$FIELD_TYPE,
|
|
|
|
Y.RDB$FIELD_SCALE, Y.RDB$FIELD_LENGTH,
|
|
|
|
Y.RDB$FIELD_SUB_TYPE, Y.RDB$CHARACTER_SET_ID,
|
|
|
|
(collation_id_null ? 0 : collation_id));
|
|
|
|
}
|
|
|
|
|
2009-12-25 15:37:44 +01:00
|
|
|
if (Y.RDB$ARGUMENT_POSITION != X.RDB$RETURN_ARGUMENT && !default_value_null)
|
|
|
|
{
|
|
|
|
function->fun_defaults++;
|
|
|
|
|
|
|
|
MemoryPool* const csb_pool = dbb->createPool();
|
|
|
|
Jrd::ContextPoolHolder context(tdbb, csb_pool);
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
parameter->prm_default_value =
|
2010-10-12 13:36:51 +02:00
|
|
|
MET_parse_blob(tdbb, NULL, &default_value, NULL, NULL, false, false);
|
2009-12-25 15:37:44 +01:00
|
|
|
}
|
|
|
|
catch (const Exception&)
|
|
|
|
{
|
2010-04-13 16:48:37 +02:00
|
|
|
dbb->deletePool(csb_pool);
|
2009-12-25 15:37:44 +01:00
|
|
|
throw; // an explicit error message would be better
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-21 18:53:49 +01:00
|
|
|
if (tail->fun_parameter->prm_desc.dsc_dtype == dtype_cstring)
|
|
|
|
tail->fun_parameter->prm_desc.dsc_length++;
|
|
|
|
|
|
|
|
length += (tail->fun_parameter->prm_desc.dsc_dtype == dtype_blob) ?
|
|
|
|
sizeof(udf_blob) : FB_ALIGN(tail->fun_parameter->prm_desc.dsc_length, FB_DOUBLE_ALIGN);
|
|
|
|
|
2009-12-21 21:55:07 +01:00
|
|
|
count = MAX(count, size_t(Y.RDB$ARGUMENT_POSITION));
|
2010-04-18 17:04:52 +02:00
|
|
|
}
|
|
|
|
END_FOR
|
2009-12-21 18:53:49 +01:00
|
|
|
|
|
|
|
function->fun_return_arg = X.RDB$RETURN_ARGUMENT;
|
|
|
|
function->fun_temp_length = length;
|
2009-12-25 12:15:13 +01:00
|
|
|
function->fun_args.resize(count + 1);
|
2010-10-12 13:36:51 +02:00
|
|
|
memcpy(function->fun_args.begin(), temp, function->fun_args.getCount() * sizeof(Argument));
|
2009-12-21 18:53:49 +01:00
|
|
|
|
|
|
|
// Prepare the exception message to be used in case this function ever
|
|
|
|
// causes an exception. This is done at this time to save us from preparing
|
|
|
|
// (thus allocating) this message every time the function is called.
|
2009-12-27 23:05:22 +01:00
|
|
|
function->fun_exception_message.printf(EXCEPTION_MESSAGE,
|
|
|
|
function->getName().toString().c_str(), X.RDB$ENTRYPOINT, X.RDB$MODULE_NAME);
|
2009-12-21 18:53:49 +01:00
|
|
|
|
2010-11-13 16:03:44 +01:00
|
|
|
if (!X.RDB$DETERMINISTIC_FLAG.NULL)
|
2009-12-24 12:48:17 +01:00
|
|
|
{
|
2010-11-13 16:03:44 +01:00
|
|
|
function->fun_deterministic = (X.RDB$DETERMINISTIC_FLAG != 0);
|
2009-12-21 18:53:49 +01:00
|
|
|
}
|
|
|
|
|
2010-10-12 13:36:51 +02:00
|
|
|
function->setUndefined(false);
|
|
|
|
function->setImplemented(true);
|
2010-10-12 10:02:57 +02:00
|
|
|
|
2010-10-12 13:36:51 +02:00
|
|
|
function->fun_entrypoint = NULL;
|
|
|
|
function->fun_external = NULL;
|
|
|
|
function->setStatement(NULL);
|
2010-10-12 10:02:57 +02:00
|
|
|
|
2010-10-12 13:36:51 +02:00
|
|
|
if (!X.RDB$ENGINE_NAME.NULL)
|
|
|
|
{
|
2009-12-21 18:53:49 +01:00
|
|
|
HalfStaticArray<UCHAR, 512> body;
|
|
|
|
|
|
|
|
if (!X.RDB$FUNCTION_SOURCE.NULL)
|
|
|
|
{
|
2010-04-02 23:48:15 +02:00
|
|
|
blb* const blob = BLB_open(tdbb, sysTransaction, &X.RDB$FUNCTION_SOURCE);
|
2009-12-21 18:53:49 +01:00
|
|
|
const ULONG len = BLB_get_data(tdbb, blob,
|
|
|
|
body.getBuffer(blob->blb_length + 1), blob->blb_length + 1);
|
|
|
|
body[MIN(blob->blb_length, len)] = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
body.getBuffer(1)[0] = 0;
|
|
|
|
}
|
|
|
|
|
2010-10-12 13:36:51 +02:00
|
|
|
function->fun_external =
|
|
|
|
dbb->dbb_extManager.makeFunction(tdbb, function, X.RDB$ENGINE_NAME,
|
2009-12-24 12:48:17 +01:00
|
|
|
(X.RDB$ENTRYPOINT.NULL ? "" : X.RDB$ENTRYPOINT), (char*) body.begin());
|
2010-10-12 13:36:51 +02:00
|
|
|
|
|
|
|
if (!function->fun_external)
|
|
|
|
{
|
|
|
|
function->setImplemented(false);
|
|
|
|
}
|
2009-12-24 12:48:17 +01:00
|
|
|
}
|
|
|
|
else if (!X.RDB$FUNCTION_BLR.NULL)
|
|
|
|
{
|
2009-12-21 18:53:49 +01:00
|
|
|
MemoryPool* const csb_pool = dbb->createPool();
|
|
|
|
Jrd::ContextPoolHolder context(tdbb, csb_pool);
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2010-04-14 06:39:36 +02:00
|
|
|
AutoPtr<CompilerScratch> csb(CompilerScratch::newCsb(*csb_pool, 5));
|
|
|
|
|
|
|
|
if (!X.RDB$DEBUG_INFO.NULL)
|
|
|
|
{
|
|
|
|
DBG_parse_debug_info(tdbb, &X.RDB$DEBUG_INFO, csb->csb_dbg_info);
|
|
|
|
}
|
|
|
|
|
2010-04-14 11:45:27 +02:00
|
|
|
try
|
2009-12-21 18:53:49 +01:00
|
|
|
{
|
2010-04-14 11:45:27 +02:00
|
|
|
function->parseBlr(tdbb, &X.RDB$FUNCTION_BLR, csb);
|
2009-12-21 18:53:49 +01:00
|
|
|
}
|
2010-04-14 11:45:27 +02:00
|
|
|
catch (const Exception&)
|
2009-12-21 18:53:49 +01:00
|
|
|
{
|
2010-04-14 11:45:27 +02:00
|
|
|
const string name = function->getName().toString();
|
|
|
|
status_exception::raise(Arg::Gds(isc_bad_fun_BLR) << Arg::Str(name));
|
2009-12-21 18:53:49 +01:00
|
|
|
}
|
|
|
|
}
|
2010-04-14 11:45:27 +02:00
|
|
|
catch (const Exception&)
|
|
|
|
{
|
|
|
|
dbb->deletePool(csb_pool);
|
|
|
|
throw;
|
|
|
|
}
|
2010-04-15 13:07:05 +02:00
|
|
|
|
|
|
|
function->makeFormat();
|
2009-12-24 12:48:17 +01:00
|
|
|
}
|
2010-10-12 13:36:51 +02:00
|
|
|
else if (!X.RDB$MODULE_NAME.NULL && !X.RDB$ENTRYPOINT.NULL)
|
2009-12-24 12:48:17 +01:00
|
|
|
{
|
2010-10-12 13:36:51 +02:00
|
|
|
function->fun_entrypoint =
|
|
|
|
Module::lookup(X.RDB$MODULE_NAME, X.RDB$ENTRYPOINT, dbb->dbb_modules);
|
2009-12-24 12:48:17 +01:00
|
|
|
|
2010-10-12 13:36:51 +02:00
|
|
|
// Could not find a function with given MODULE, ENTRYPOINT.
|
|
|
|
// Try the list of internally implemented functions.
|
|
|
|
if (!function->fun_entrypoint)
|
2009-12-24 12:48:17 +01:00
|
|
|
{
|
|
|
|
function->fun_entrypoint =
|
2010-10-12 13:36:51 +02:00
|
|
|
BUILTIN_entrypoint(X.RDB$MODULE_NAME, X.RDB$ENTRYPOINT);
|
2010-10-12 10:02:57 +02:00
|
|
|
}
|
2010-10-12 13:36:51 +02:00
|
|
|
|
|
|
|
if (!function->fun_entrypoint)
|
2009-12-31 12:24:28 +01:00
|
|
|
{
|
2010-10-12 13:36:51 +02:00
|
|
|
function->setImplemented(false);
|
2009-12-31 12:24:28 +01:00
|
|
|
}
|
2009-12-21 18:53:49 +01:00
|
|
|
}
|
2010-10-12 13:36:51 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
function->setUndefined(true);
|
|
|
|
}
|
2009-12-21 18:53:49 +01:00
|
|
|
|
2009-12-25 08:09:02 +01:00
|
|
|
if (X.RDB$VALID_BLR.NULL || X.RDB$VALID_BLR == FALSE)
|
|
|
|
{
|
|
|
|
valid_blr = false;
|
|
|
|
}
|
|
|
|
|
2009-12-25 07:52:09 +01:00
|
|
|
function->fun_flags |= FUN_scanned;
|
2010-04-18 17:04:52 +02:00
|
|
|
}
|
|
|
|
END_FOR
|
2009-12-25 08:09:02 +01:00
|
|
|
|
|
|
|
if (!(dbb->dbb_flags & DBB_read_only) && !valid_blr)
|
|
|
|
{
|
|
|
|
// if the BLR was marked as invalid but the function was compiled,
|
|
|
|
// mark the BLR as valid
|
|
|
|
|
2010-04-20 00:49:18 +02:00
|
|
|
AutoRequest request_set_valid;
|
2009-12-25 08:09:02 +01:00
|
|
|
|
|
|
|
FOR(REQUEST_HANDLE request_set_valid)
|
2009-12-27 23:05:22 +01:00
|
|
|
F IN RDB$FUNCTIONS WITH F.RDB$FUNCTION_ID EQ function->getId()
|
2010-04-20 00:49:18 +02:00
|
|
|
{
|
2009-12-25 08:09:02 +01:00
|
|
|
MODIFY F USING
|
|
|
|
F.RDB$VALID_BLR = TRUE;
|
|
|
|
F.RDB$VALID_BLR.NULL = FALSE;
|
2010-04-20 00:49:18 +02:00
|
|
|
END_MODIFY
|
|
|
|
}
|
|
|
|
END_FOR
|
2009-12-25 08:09:02 +01:00
|
|
|
}
|
2009-12-21 18:53:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure that it is really being scanned
|
|
|
|
fb_assert(function->fun_flags & FUN_being_scanned);
|
|
|
|
|
|
|
|
function->fun_flags &= ~FUN_being_scanned;
|
|
|
|
|
|
|
|
} // try
|
|
|
|
catch (const Exception&)
|
|
|
|
{
|
|
|
|
function->fun_flags &= ~(FUN_being_scanned | FUN_scanned);
|
|
|
|
|
|
|
|
if (function->fun_existence_lock)
|
|
|
|
{
|
|
|
|
LCK_release(tdbb, function->fun_existence_lock);
|
|
|
|
function->fun_existence_lock = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
|
|
|
|
return function;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Function::blockingAst(void* ast_object)
|
|
|
|
{
|
|
|
|
Function* const function = static_cast<Function*>(ast_object);
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Database* const dbb = function->fun_existence_lock->lck_dbb;
|
|
|
|
|
|
|
|
Database::SyncGuard dsGuard(dbb, true);
|
|
|
|
|
|
|
|
ThreadContextHolder tdbb;
|
|
|
|
tdbb->setDatabase(dbb);
|
|
|
|
tdbb->setAttachment(function->fun_existence_lock->lck_attachment);
|
|
|
|
|
|
|
|
Jrd::ContextPoolHolder context(tdbb, 0);
|
|
|
|
|
2009-12-25 12:24:34 +01:00
|
|
|
LCK_release(tdbb, function->fun_existence_lock);
|
2009-12-21 18:53:49 +01:00
|
|
|
|
|
|
|
function->fun_flags |= FUN_obsolete;
|
|
|
|
}
|
|
|
|
catch (const Firebird::Exception&)
|
|
|
|
{} // no-op
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Function::remove(thread_db* tdbb)
|
|
|
|
{
|
|
|
|
if (fun_existence_lock)
|
|
|
|
{
|
|
|
|
LCK_release(tdbb, fun_existence_lock);
|
|
|
|
delete fun_existence_lock;
|
|
|
|
fun_existence_lock = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(fun_flags & FUN_being_altered) && !fun_use_count)
|
|
|
|
{
|
|
|
|
delete this;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-12-27 23:05:22 +01:00
|
|
|
setName(QualifiedName());
|
|
|
|
setSecurityName("");
|
|
|
|
setId(0);
|
2009-12-30 01:50:39 +01:00
|
|
|
fun_defaults = 0;
|
2009-12-21 18:53:49 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-10-12 13:36:51 +02:00
|
|
|
ULONG Function::allocateImpure(CompilerScratch* csb) const
|
2010-04-15 13:07:05 +02:00
|
|
|
{
|
|
|
|
const ULONG impure = CMP_impure(csb, sizeof(impure_value));
|
|
|
|
|
2010-04-19 00:19:11 +02:00
|
|
|
if (getStatement())
|
2010-04-15 13:07:05 +02:00
|
|
|
{
|
|
|
|
if (fun_in_msg_format.fmt_count)
|
|
|
|
{
|
|
|
|
fb_assert(fun_in_msg_format.fmt_length);
|
|
|
|
CMP_impure(csb, fun_in_msg_format.fmt_length);
|
|
|
|
}
|
|
|
|
|
|
|
|
fb_assert(fun_out_msg_format.fmt_count == 2);
|
|
|
|
fb_assert(fun_out_msg_format.fmt_length);
|
|
|
|
CMP_impure(csb, fun_out_msg_format.fmt_length);
|
|
|
|
}
|
2010-04-16 04:31:36 +02:00
|
|
|
|
2010-04-15 13:07:05 +02:00
|
|
|
return impure;
|
|
|
|
}
|
|
|
|
|
2009-12-21 18:53:49 +01:00
|
|
|
void Function::parseBlr(thread_db* tdbb, bid* blob_id, CompilerScratch* csb)
|
|
|
|
{
|
2009-12-25 08:26:50 +01:00
|
|
|
fb_assert(blob_id && !blob_id->isEmpty());
|
2009-12-25 08:26:49 +01:00
|
|
|
|
2010-04-19 00:19:11 +02:00
|
|
|
JrdStatement* statement = getStatement();
|
2010-04-14 11:45:27 +02:00
|
|
|
|
2010-04-19 00:19:11 +02:00
|
|
|
if (statement)
|
2010-04-14 11:45:27 +02:00
|
|
|
{
|
2010-04-19 00:19:11 +02:00
|
|
|
statement->release(tdbb);
|
|
|
|
setStatement(NULL);
|
|
|
|
statement = NULL;
|
2010-04-14 11:45:27 +02:00
|
|
|
}
|
|
|
|
|
2010-10-12 13:36:51 +02:00
|
|
|
MET_parse_blob(tdbb, NULL, blob_id, &csb, &statement, false, false);
|
2010-04-19 00:19:11 +02:00
|
|
|
statement->function = this;
|
|
|
|
setStatement(statement);
|
2009-12-21 18:53:49 +01:00
|
|
|
}
|
|
|
|
|
2010-04-14 11:45:27 +02:00
|
|
|
jrd_nod* Function::parseArgs(thread_db* tdbb, CompilerScratch* csb)
|
|
|
|
{
|
|
|
|
const UCHAR arg_count = csb->csb_blr_reader.getByte();
|
|
|
|
|
|
|
|
// Check to see if the argument count matches
|
|
|
|
if (arg_count < fun_inputs - fun_defaults || arg_count > fun_inputs)
|
|
|
|
{
|
|
|
|
PAR_error(csb, Arg::Gds(isc_funmismat) << Arg::Str(getName().toString()));
|
|
|
|
}
|
2010-04-16 04:31:36 +02:00
|
|
|
|
2010-04-14 11:45:27 +02:00
|
|
|
jrd_nod* const args = PAR_args(tdbb, csb, VALUE, arg_count, fun_inputs);
|
2010-04-16 04:31:36 +02:00
|
|
|
|
2010-04-14 11:45:27 +02:00
|
|
|
for (USHORT i = arg_count; i < fun_inputs; i++)
|
|
|
|
{
|
|
|
|
Parameter* const parameter = fun_args[i + 1].fun_parameter;
|
|
|
|
args->nod_arg[i] = CMP_clone_node(tdbb, csb, parameter->prm_default_value);
|
|
|
|
}
|
|
|
|
|
|
|
|
args->nod_count = fun_inputs;
|
2010-04-15 13:07:05 +02:00
|
|
|
|
2010-04-14 11:45:27 +02:00
|
|
|
return args;
|
|
|
|
}
|
|
|
|
|
2009-12-21 18:53:49 +01:00
|
|
|
void Function::addRef()
|
|
|
|
{
|
|
|
|
fun_use_count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Function::release(thread_db* tdbb)
|
|
|
|
{
|
|
|
|
if (fun_use_count)
|
|
|
|
{
|
|
|
|
fun_use_count--;
|
|
|
|
|
|
|
|
Database* const dbb = tdbb->getDatabase();
|
2009-12-27 23:05:22 +01:00
|
|
|
if (fun_use_count == 0 && dbb->dbb_functions[getId()] != this)
|
2009-12-21 18:53:49 +01:00
|
|
|
{
|
2010-04-19 00:19:11 +02:00
|
|
|
if (getStatement())
|
2009-12-21 18:53:49 +01:00
|
|
|
{
|
2010-04-19 00:19:11 +02:00
|
|
|
getStatement()->release(tdbb);
|
|
|
|
setStatement(NULL);
|
2009-12-21 18:53:49 +01:00
|
|
|
}
|
2009-12-24 12:48:17 +01:00
|
|
|
fun_flags &= ~FUN_being_altered;
|
2009-12-21 18:53:49 +01:00
|
|
|
remove(tdbb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Function::releaseLocks(thread_db* tdbb)
|
|
|
|
{
|
|
|
|
if (fun_existence_lock)
|
|
|
|
{
|
|
|
|
LCK_release(tdbb, fun_existence_lock);
|
|
|
|
fun_flags |= FUN_check_existence;
|
|
|
|
fun_use_count = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-13 16:03:44 +01:00
|
|
|
dsc* Function::execute(thread_db* tdbb, const jrd_nod* args, impure_value* value, bool invariant) const
|
2009-12-21 18:53:49 +01:00
|
|
|
{
|
2009-12-31 12:24:28 +01:00
|
|
|
if (isUndefined())
|
|
|
|
{
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_func_pack_not_implemented) <<
|
2010-01-27 05:32:27 +01:00
|
|
|
Arg::Str(getName().identifier) << Arg::Str(getName().package));
|
2009-12-31 12:24:28 +01:00
|
|
|
}
|
|
|
|
|
2010-04-15 13:46:42 +02:00
|
|
|
jrd_req* const request = tdbb->getRequest();
|
|
|
|
|
2010-10-12 13:36:51 +02:00
|
|
|
const Argument* const return_ptr = &fun_args[fun_return_arg];
|
2009-12-21 18:53:49 +01:00
|
|
|
value->vlu_desc = return_ptr->fun_parameter->prm_desc;
|
|
|
|
|
|
|
|
// If the return data type is any of the string types,
|
|
|
|
// allocate space to hold value
|
|
|
|
|
|
|
|
if (value->vlu_desc.dsc_dtype <= dtype_varying)
|
|
|
|
{
|
|
|
|
const USHORT ret_length = value->vlu_desc.dsc_length;
|
|
|
|
VaryingString* string = value->vlu_string;
|
|
|
|
if (string && string->str_length < ret_length)
|
|
|
|
{
|
|
|
|
delete string;
|
|
|
|
string = NULL;
|
|
|
|
}
|
|
|
|
if (!string)
|
|
|
|
{
|
|
|
|
string = FB_NEW_RPT(*tdbb->getDefaultPool(), ret_length) VaryingString;
|
|
|
|
string->str_length = ret_length;
|
|
|
|
value->vlu_string = string;
|
|
|
|
}
|
|
|
|
value->vlu_desc.dsc_address = string->str_data;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
value->vlu_desc.dsc_address = (UCHAR*) &value->vlu_misc;
|
|
|
|
}
|
|
|
|
|
2010-11-13 09:56:15 +01:00
|
|
|
USHORT& invariant_flags = value->vlu_flags;
|
|
|
|
|
2010-11-13 16:03:44 +01:00
|
|
|
// If the function is known as being both deterministic and invariant,
|
2010-11-13 09:56:15 +01:00
|
|
|
// check whether it has already been evaluated
|
|
|
|
|
2010-11-13 16:03:44 +01:00
|
|
|
if (fun_deterministic && invariant)
|
2010-11-13 09:56:15 +01:00
|
|
|
{
|
|
|
|
if (invariant_flags & VLU_computed)
|
|
|
|
{
|
|
|
|
if (invariant_flags & VLU_null)
|
|
|
|
request->req_flags |= req_null;
|
|
|
|
else
|
|
|
|
request->req_flags &= ~req_null;
|
|
|
|
|
|
|
|
return (request->req_flags & req_null) ? NULL : &value->vlu_desc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Evaluate the function
|
|
|
|
|
2009-12-21 18:53:49 +01:00
|
|
|
if (fun_entrypoint)
|
|
|
|
{
|
|
|
|
FUN_evaluate(tdbb, this, args, value);
|
|
|
|
}
|
|
|
|
else if (fun_external)
|
|
|
|
{
|
2009-12-24 12:48:17 +01:00
|
|
|
fun_external->execute(tdbb, args, value);
|
2009-12-21 18:53:49 +01:00
|
|
|
}
|
2010-10-12 13:36:51 +02:00
|
|
|
else if (getStatement())
|
2009-12-21 18:53:49 +01:00
|
|
|
{
|
2009-12-25 15:37:44 +01:00
|
|
|
fb_assert(!fun_return_arg);
|
2010-04-15 13:07:05 +02:00
|
|
|
fb_assert(args->nod_count == fun_inputs);
|
2009-12-21 18:53:49 +01:00
|
|
|
|
2010-04-02 23:48:15 +02:00
|
|
|
Jrd::Attachment* attachment = tdbb->getAttachment();
|
2009-12-25 12:15:13 +01:00
|
|
|
|
2010-04-15 13:07:05 +02:00
|
|
|
const ULONG in_msg_length = fun_in_msg_format.fmt_length;
|
|
|
|
const ULONG out_msg_length = fun_out_msg_format.fmt_length;
|
2009-12-25 12:15:13 +01:00
|
|
|
|
2010-04-15 13:07:05 +02:00
|
|
|
UCHAR* const impure = reinterpret_cast<UCHAR*>(value);
|
|
|
|
UCHAR* const in_msg = (UCHAR*) FB_ALIGN((IPTR) impure + sizeof(impure_value), FB_ALIGNMENT);
|
|
|
|
UCHAR* const out_msg = (UCHAR*) FB_ALIGN((IPTR) in_msg + in_msg_length, FB_ALIGNMENT);
|
2009-12-25 12:15:13 +01:00
|
|
|
|
2010-04-15 13:07:05 +02:00
|
|
|
for (USHORT i = 0; i < fun_inputs; i++)
|
2009-12-25 12:15:13 +01:00
|
|
|
{
|
2010-04-15 13:07:05 +02:00
|
|
|
const ULONG arg_offset = (IPTR) fun_in_msg_format.fmt_desc[i * 2].dsc_address;
|
|
|
|
const ULONG null_offset = (IPTR) fun_in_msg_format.fmt_desc[i * 2 + 1].dsc_address;
|
|
|
|
UCHAR* const null_ptr = in_msg + null_offset;
|
2009-12-25 12:15:13 +01:00
|
|
|
|
2010-04-15 13:07:05 +02:00
|
|
|
dsc* const src_desc = EVL_expr(tdbb, args->nod_arg[i]);
|
2009-12-25 12:15:13 +01:00
|
|
|
if (src_desc && !(request->req_flags & req_null))
|
|
|
|
{
|
2010-04-15 13:07:05 +02:00
|
|
|
*reinterpret_cast<SSHORT*>(null_ptr) = FALSE;
|
|
|
|
|
|
|
|
dsc arg_desc = fun_args[i + 1].fun_parameter->prm_desc;
|
|
|
|
arg_desc.dsc_address = in_msg + arg_offset;
|
2009-12-25 12:15:13 +01:00
|
|
|
MOV_move(tdbb, src_desc, &arg_desc);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*reinterpret_cast<SSHORT*>(null_ptr) = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-19 00:19:11 +02:00
|
|
|
jrd_req* const new_request = getStatement()->findRequest(tdbb);
|
2009-12-25 12:15:13 +01:00
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
// Save the old pool
|
|
|
|
Jrd::ContextPoolHolder context(tdbb, request->req_pool);
|
|
|
|
|
|
|
|
jrd_tra* const transaction = request->req_transaction;
|
|
|
|
const SLONG save_point_number = transaction->tra_save_point ?
|
|
|
|
transaction->tra_save_point->sav_number : 0;
|
|
|
|
|
|
|
|
new_request->req_timestamp = request->req_timestamp;
|
|
|
|
|
|
|
|
EXE_start(tdbb, new_request, transaction);
|
|
|
|
|
2010-04-15 13:07:05 +02:00
|
|
|
if (in_msg_length)
|
2009-12-25 12:15:13 +01:00
|
|
|
{
|
|
|
|
EXE_send(tdbb, new_request, 0, in_msg_length, in_msg);
|
|
|
|
}
|
|
|
|
|
2010-04-15 13:07:05 +02:00
|
|
|
fb_assert(out_msg_length);
|
2009-12-25 12:15:13 +01:00
|
|
|
EXE_receive(tdbb, new_request, 1, out_msg_length, out_msg);
|
|
|
|
|
|
|
|
// Clean up all savepoints started during execution of the function
|
|
|
|
|
2010-04-02 23:48:15 +02:00
|
|
|
if (transaction != attachment->getSysTransaction())
|
2009-12-25 12:15:13 +01:00
|
|
|
{
|
|
|
|
for (const Savepoint* save_point = transaction->tra_save_point;
|
|
|
|
save_point && save_point_number < save_point->sav_number;
|
|
|
|
save_point = transaction->tra_save_point)
|
|
|
|
{
|
|
|
|
VIO_verb_cleanup(tdbb, transaction);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (const Firebird::Exception&)
|
|
|
|
{
|
|
|
|
tdbb->setRequest(request);
|
|
|
|
EXE_unwind(tdbb, new_request);
|
|
|
|
new_request->req_attachment = NULL;
|
|
|
|
new_request->req_flags &= ~req_in_use;
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
|
|
|
|
tdbb->setRequest(request);
|
|
|
|
EXE_unwind(tdbb, new_request);
|
|
|
|
new_request->req_attachment = NULL;
|
|
|
|
new_request->req_flags &= ~req_in_use;
|
|
|
|
|
2010-04-15 13:07:05 +02:00
|
|
|
const ULONG arg_offset = (IPTR) fun_out_msg_format.fmt_desc[0].dsc_address;
|
|
|
|
const ULONG null_offset = (IPTR) fun_out_msg_format.fmt_desc[1].dsc_address;
|
|
|
|
|
2009-12-25 12:15:13 +01:00
|
|
|
UCHAR* const null_ptr = out_msg + null_offset;
|
|
|
|
if (*reinterpret_cast<SSHORT*>(null_ptr))
|
|
|
|
{
|
|
|
|
request->req_flags |= req_null;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-04-15 13:07:05 +02:00
|
|
|
dsc arg_desc = fun_args[fun_return_arg].fun_parameter->prm_desc;
|
|
|
|
arg_desc.dsc_address = out_msg + arg_offset;
|
2009-12-25 12:15:13 +01:00
|
|
|
request->req_flags &= ~req_null;
|
2010-04-15 13:07:05 +02:00
|
|
|
MOV_move(tdbb, &arg_desc, &value->vlu_desc);
|
2009-12-25 12:15:13 +01:00
|
|
|
}
|
2009-12-21 18:53:49 +01:00
|
|
|
}
|
2010-10-12 13:36:51 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
fb_assert(false);
|
|
|
|
}
|
2009-12-21 18:53:49 +01:00
|
|
|
|
2010-11-13 09:56:15 +01:00
|
|
|
// If the function is declared as invariant, mark it as computed
|
|
|
|
|
2010-11-13 16:03:44 +01:00
|
|
|
if (fun_deterministic && invariant)
|
2010-11-13 09:56:15 +01:00
|
|
|
{
|
|
|
|
invariant_flags |= VLU_computed;
|
|
|
|
|
|
|
|
if (request->req_flags & req_null)
|
|
|
|
invariant_flags |= VLU_null;
|
|
|
|
}
|
|
|
|
|
2010-04-15 13:46:42 +02:00
|
|
|
return (request->req_flags & req_null) ? NULL : &value->vlu_desc;
|
2009-12-21 18:53:49 +01:00
|
|
|
}
|
2009-12-24 15:15:21 +01:00
|
|
|
|
|
|
|
USHORT Function::incrementAlterCount()
|
|
|
|
{
|
|
|
|
if (fun_alter_count == Function::MAX_ALTER_COUNT)
|
|
|
|
{
|
|
|
|
status_exception::raise(Arg::Gds(isc_no_meta_update) <<
|
2009-12-27 23:05:22 +01:00
|
|
|
Arg::Gds(isc_udf_name) << Arg::Str(getName().toString()) <<
|
2009-12-24 15:15:21 +01:00
|
|
|
Arg::Gds(isc_version_err));
|
|
|
|
}
|
2009-12-25 20:29:58 +01:00
|
|
|
|
2009-12-24 15:15:21 +01:00
|
|
|
return ++fun_alter_count;
|
|
|
|
}
|
2010-04-15 13:07:05 +02:00
|
|
|
|
|
|
|
void Function::makeFormat()
|
|
|
|
{
|
|
|
|
// Input format
|
|
|
|
|
|
|
|
const USHORT count = fun_inputs * 2;
|
|
|
|
fun_in_msg_format.fmt_desc.resize(count);
|
|
|
|
fun_in_msg_format.fmt_count = count;
|
|
|
|
|
|
|
|
ULONG offset = 0;
|
|
|
|
|
|
|
|
for (USHORT i = 0; i < fun_inputs; i++)
|
|
|
|
{
|
|
|
|
dsc arg_desc = fun_args[i + 1].fun_parameter->prm_desc;
|
|
|
|
|
|
|
|
if (arg_desc.dsc_dtype >= dtype_aligned)
|
|
|
|
{
|
|
|
|
offset = FB_ALIGN(offset, type_alignments[arg_desc.dsc_dtype]);
|
|
|
|
}
|
|
|
|
arg_desc.dsc_address = (UCHAR*)(IPTR) offset;
|
|
|
|
offset += arg_desc.dsc_length;
|
|
|
|
fun_in_msg_format.fmt_desc[i * 2] = arg_desc;
|
|
|
|
|
|
|
|
arg_desc.makeShort(0);
|
|
|
|
offset = FB_ALIGN(offset, sizeof(SSHORT));
|
|
|
|
arg_desc.dsc_address = (UCHAR*)(IPTR) offset;
|
|
|
|
offset += sizeof(SSHORT);
|
|
|
|
fun_in_msg_format.fmt_desc[i * 2 + 1] = arg_desc;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (offset > MAX_FORMAT_SIZE)
|
|
|
|
{
|
|
|
|
status_exception::raise(Arg::Gds(isc_imp_exc) << Arg::Gds(isc_blktoobig));
|
|
|
|
}
|
|
|
|
|
|
|
|
fun_in_msg_format.fmt_length = offset;
|
|
|
|
|
|
|
|
// Output format
|
|
|
|
|
|
|
|
fun_out_msg_format.fmt_desc.resize(2);
|
|
|
|
fun_out_msg_format.fmt_count = 2;
|
|
|
|
|
|
|
|
offset = 0;
|
|
|
|
|
|
|
|
dsc ret_desc = fun_args[fun_return_arg].fun_parameter->prm_desc;
|
|
|
|
ret_desc.dsc_address = (UCHAR*)(IPTR) offset;
|
|
|
|
offset += ret_desc.dsc_length;
|
|
|
|
fun_out_msg_format.fmt_desc[0] = ret_desc;
|
|
|
|
|
|
|
|
ret_desc.makeShort(0);
|
|
|
|
offset = FB_ALIGN(offset, sizeof(SSHORT));
|
|
|
|
ret_desc.dsc_address = (UCHAR*)(IPTR) offset;
|
|
|
|
offset += sizeof(SSHORT);
|
|
|
|
fun_out_msg_format.fmt_desc[1] = ret_desc;
|
|
|
|
|
|
|
|
fun_out_msg_format.fmt_length = offset;
|
|
|
|
}
|