8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-24 12:03:02 +01:00
firebird-mirror/src/jrd/Function.epp

905 lines
23 KiB
Plaintext
Raw Normal View History

/*
* 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"
#include "../common/common.h"
#include "../common/gdsassert.h"
#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"
#include "../jrd/align.h"
#include "../jrd/blb_proto.h"
#include "../jrd/cmp_proto.h"
2010-10-12 10:02:57 +02:00
#include "../common/dsc_proto.h"
#include "../jrd/evl_proto.h"
#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"
#include "../jrd/mov_proto.h"
#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";
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:";
Function* Function::lookup(thread_db* tdbb, USHORT id, bool return_deleted, bool noscan, USHORT flags)
{
Jrd::Attachment* attachment = tdbb->getAttachment();
Database* const dbb = tdbb->getDatabase();
Function* check_function = NULL;
Function* function = (id < dbb->dbb_functions.getCount()) ? dbb->dbb_functions[id] : NULL;
if (function && function->getId() == id &&
!(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;
AutoCacheRequest request(tdbb, irq_l_fun_id, IRQ_REQUESTS);
FOR(REQUEST_HANDLE request)
X IN RDB$FUNCTIONS WITH X.RDB$FUNCTION_ID EQ id
{
function = loadMetadata(tdbb, X.RDB$FUNCTION_ID, noscan, flags);
}
END_FOR
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;
}
}
return function;
}
Function* Function::lookup(thread_db* tdbb, const QualifiedName& name, bool noscan)
{
Jrd::Attachment* attachment = tdbb->getAttachment();
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))
{
if (function->getName() == name)
{
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;
AutoCacheRequest request(tdbb, irq_l_fun_name, IRQ_REQUESTS);
FOR(REQUEST_HANDLE request)
X IN RDB$FUNCTIONS
WITH X.RDB$FUNCTION_NAME EQ name.identifier.c_str() AND
X.RDB$PACKAGE_NAME EQUIV NULLIF(name.package.c_str(), '')
{
function = loadMetadata(tdbb, X.RDB$FUNCTION_ID, noscan, 0);
}
END_FOR
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)
{
Jrd::Attachment* attachment = tdbb->getAttachment();
jrd_tra* sysTransaction = attachment->getSysTransaction();
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
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;
function->setId(id);
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;
lock->lck_key.lck_long = function->getId();
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)
{
bool valid_blr = true;
AutoCacheRequest request_fun(tdbb, irq_l_functions, IRQ_REQUESTS);
Argument temp[MAX_UDF_ARGUMENTS + 1];
FOR(REQUEST_HANDLE request_fun)
X IN RDB$FUNCTIONS
WITH X.RDB$FUNCTION_ID EQ id
{
function->setName(QualifiedName(X.RDB$FUNCTION_NAME,
(X.RDB$PACKAGE_NAME.NULL ? NULL : X.RDB$PACKAGE_NAME)));
if (!X.RDB$SECURITY_CLASS.NULL)
{
function->setSecurityName(X.RDB$SECURITY_CLASS);
}
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)
{
function->setSecurityName(PKG.RDB$SECURITY_CLASS);
}
END_FOR
}
size_t count = 0;
memset(temp, 0, sizeof(temp));
ULONG length = 0;
function->fun_inputs = 0;
function->fun_defaults = 0;
AutoCacheRequest request_arg(tdbb, irq_l_args, IRQ_REQUESTS);
FOR(REQUEST_HANDLE request_arg)
Y IN RDB$FUNCTION_ARGUMENTS
WITH Y.RDB$FUNCTION_NAME EQ function->getName().identifier.c_str() AND
Y.RDB$PACKAGE_NAME EQUIV NULLIF(function->getName().package.c_str(), '')
SORTED BY Y.RDB$ARGUMENT_POSITION
{
if (Y.RDB$ARGUMENT_POSITION != X.RDB$RETURN_ARGUMENT)
function->fun_inputs++;
Argument* const tail = temp + Y.RDB$ARGUMENT_POSITION;
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
SSHORT default_value_null = Y.RDB$DEFAULT_VALUE.NULL;
bid default_value = Y.RDB$DEFAULT_VALUE;
if (!Y.RDB$FIELD_SOURCE.NULL)
{
parameter->prm_field_source = Y.RDB$FIELD_SOURCE;
AutoCacheRequest request_arg_fld(tdbb, irq_l_arg_fld, IRQ_REQUESTS);
FOR(REQUEST_HANDLE request_arg_fld)
F IN RDB$FIELDS
WITH F.RDB$FIELD_NAME = Y.RDB$FIELD_SOURCE
{
DSC_make_descriptor(&parameter->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));
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;
}
}
END_FOR
}
else
{
DSC_make_descriptor(&parameter->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));
}
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 =
MET_parse_blob(tdbb, NULL, &default_value, NULL, NULL, false, false);
}
catch (const Exception&)
{
dbb->deletePool(csb_pool);
throw; // an explicit error message would be better
}
}
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);
count = MAX(count, size_t(Y.RDB$ARGUMENT_POSITION));
}
END_FOR
function->fun_return_arg = X.RDB$RETURN_ARGUMENT;
function->fun_temp_length = length;
function->fun_args.resize(count + 1);
memcpy(function->fun_args.begin(), temp, function->fun_args.getCount() * sizeof(Argument));
// 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.
function->fun_exception_message.printf(EXCEPTION_MESSAGE,
function->getName().toString().c_str(), X.RDB$ENTRYPOINT, X.RDB$MODULE_NAME);
2009-12-24 12:48:17 +01:00
if (!X.RDB$INVARIANT_FLAG.NULL)
{
function->fun_invariant = (X.RDB$INVARIANT_FLAG != 0);
}
function->setUndefined(false);
function->setImplemented(true);
2010-10-12 10:02:57 +02:00
function->fun_entrypoint = NULL;
function->fun_external = NULL;
function->setStatement(NULL);
2010-10-12 10:02:57 +02:00
if (!X.RDB$ENGINE_NAME.NULL)
{
HalfStaticArray<UCHAR, 512> body;
if (!X.RDB$FUNCTION_SOURCE.NULL)
{
blb* const blob = BLB_open(tdbb, sysTransaction, &X.RDB$FUNCTION_SOURCE);
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;
}
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());
if (!function->fun_external)
{
function->setImplemented(false);
}
2009-12-24 12:48:17 +01:00
}
else if (!X.RDB$FUNCTION_BLR.NULL)
{
MemoryPool* const csb_pool = dbb->createPool();
Jrd::ContextPoolHolder context(tdbb, csb_pool);
try
{
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);
}
try
{
function->parseBlr(tdbb, &X.RDB$FUNCTION_BLR, csb);
}
catch (const Exception&)
{
const string name = function->getName().toString();
status_exception::raise(Arg::Gds(isc_bad_fun_BLR) << Arg::Str(name));
}
}
catch (const Exception&)
{
dbb->deletePool(csb_pool);
throw;
}
function->makeFormat();
2009-12-24 12:48:17 +01:00
}
else if (!X.RDB$MODULE_NAME.NULL && !X.RDB$ENTRYPOINT.NULL)
2009-12-24 12:48:17 +01:00
{
function->fun_entrypoint =
Module::lookup(X.RDB$MODULE_NAME, X.RDB$ENTRYPOINT, dbb->dbb_modules);
2009-12-24 12:48:17 +01: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 =
BUILTIN_entrypoint(X.RDB$MODULE_NAME, X.RDB$ENTRYPOINT);
2010-10-12 10:02:57 +02:00
}
if (!function->fun_entrypoint)
{
function->setImplemented(false);
}
}
else
{
function->setUndefined(true);
}
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;
}
END_FOR
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
AutoRequest request_set_valid;
FOR(REQUEST_HANDLE request_set_valid)
F IN RDB$FUNCTIONS WITH F.RDB$FUNCTION_ID EQ function->getId()
{
MODIFY F USING
F.RDB$VALID_BLR = TRUE;
F.RDB$VALID_BLR.NULL = FALSE;
END_MODIFY
}
END_FOR
}
}
// 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);
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
{
setName(QualifiedName());
setSecurityName("");
setId(0);
2009-12-30 01:50:39 +01:00
fun_defaults = 0;
}
}
ULONG Function::allocateImpure(CompilerScratch* csb) const
{
const ULONG impure = CMP_impure(csb, sizeof(impure_value));
if (getStatement())
{
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
return impure;
}
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
JrdStatement* statement = getStatement();
if (statement)
{
statement->release(tdbb);
setStatement(NULL);
statement = NULL;
}
MET_parse_blob(tdbb, NULL, blob_id, &csb, &statement, false, false);
statement->function = this;
setStatement(statement);
}
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
jrd_nod* const args = PAR_args(tdbb, csb, VALUE, arg_count, fun_inputs);
2010-04-16 04:31:36 +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;
return args;
}
void Function::addRef()
{
fun_use_count++;
}
void Function::release(thread_db* tdbb)
{
if (fun_use_count)
{
fun_use_count--;
Database* const dbb = tdbb->getDatabase();
if (fun_use_count == 0 && dbb->dbb_functions[getId()] != this)
{
if (getStatement())
{
getStatement()->release(tdbb);
setStatement(NULL);
}
2009-12-24 12:48:17 +01:00
fun_flags &= ~FUN_being_altered;
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;
}
}
dsc* Function::execute(thread_db* tdbb, const jrd_nod* args, impure_value* value) const
{
if (isUndefined())
{
status_exception::raise(
Arg::Gds(isc_func_pack_not_implemented) <<
Arg::Str(getName().identifier) << Arg::Str(getName().package));
}
2010-04-15 13:46:42 +02:00
jrd_req* const request = tdbb->getRequest();
const Argument* const return_ptr = &fun_args[fun_return_arg];
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;
}
USHORT& invariant_flags = value->vlu_flags;
// If the function is declared as invariant,
// check whether it has already been evaluated
if (fun_invariant)
{
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
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);
}
else if (getStatement())
{
fb_assert(!fun_return_arg);
fb_assert(args->nod_count == fun_inputs);
Jrd::Attachment* attachment = tdbb->getAttachment();
const ULONG in_msg_length = fun_in_msg_format.fmt_length;
const ULONG out_msg_length = fun_out_msg_format.fmt_length;
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);
for (USHORT i = 0; i < fun_inputs; i++)
{
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;
dsc* const src_desc = EVL_expr(tdbb, args->nod_arg[i]);
if (src_desc && !(request->req_flags & req_null))
{
*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;
MOV_move(tdbb, src_desc, &arg_desc);
}
else
{
*reinterpret_cast<SSHORT*>(null_ptr) = TRUE;
}
}
jrd_req* const new_request = getStatement()->findRequest(tdbb);
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);
if (in_msg_length)
{
EXE_send(tdbb, new_request, 0, in_msg_length, in_msg);
}
fb_assert(out_msg_length);
EXE_receive(tdbb, new_request, 1, out_msg_length, out_msg);
// Clean up all savepoints started during execution of the function
if (transaction != attachment->getSysTransaction())
{
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;
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;
UCHAR* const null_ptr = out_msg + null_offset;
if (*reinterpret_cast<SSHORT*>(null_ptr))
{
request->req_flags |= req_null;
}
else
{
dsc arg_desc = fun_args[fun_return_arg].fun_parameter->prm_desc;
arg_desc.dsc_address = out_msg + arg_offset;
request->req_flags &= ~req_null;
MOV_move(tdbb, &arg_desc, &value->vlu_desc);
}
}
else
{
fb_assert(false);
}
// If the function is declared as invariant, mark it as computed
if (fun_invariant)
{
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;
}
USHORT Function::incrementAlterCount()
{
if (fun_alter_count == Function::MAX_ALTER_COUNT)
{
status_exception::raise(Arg::Gds(isc_no_meta_update) <<
Arg::Gds(isc_udf_name) << Arg::Str(getName().toString()) <<
Arg::Gds(isc_version_err));
}
2009-12-25 20:29:58 +01:00
return ++fun_alter_count;
}
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;
}