/* * 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" #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); 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; } } 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; 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(¶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)); 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(¶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)); } 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); if (!X.RDB$INVARIANT_FLAG.NULL) { function->fun_invariant = (X.RDB$INVARIANT_FLAG != 0); } function->setUndefined(false); function->setImplemented(true); function->fun_entrypoint = NULL; function->fun_external = NULL; function->setStatement(NULL); if (!X.RDB$ENGINE_NAME.NULL) { HalfStaticArray 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, (X.RDB$ENTRYPOINT.NULL ? "" : X.RDB$ENTRYPOINT), (char*) body.begin()); if (!function->fun_external) { function->setImplemented(false); } } else if (!X.RDB$FUNCTION_BLR.NULL) { MemoryPool* const csb_pool = dbb->createPool(); Jrd::ContextPoolHolder context(tdbb, csb_pool); try { AutoPtr 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(); } else if (!X.RDB$MODULE_NAME.NULL && !X.RDB$ENTRYPOINT.NULL) { function->fun_entrypoint = Module::lookup(X.RDB$MODULE_NAME, X.RDB$ENTRYPOINT, dbb->dbb_modules); // Could not find a function with given MODULE, ENTRYPOINT. // Try the list of internally implemented functions. if (!function->fun_entrypoint) { function->fun_entrypoint = BUILTIN_entrypoint(X.RDB$MODULE_NAME, X.RDB$ENTRYPOINT); } 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; } 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(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); 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); 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); } return impure; } void Function::parseBlr(thread_db* tdbb, bid* blob_id, CompilerScratch* csb) { fb_assert(blob_id && !blob_id->isEmpty()); 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())); } jrd_nod* const args = PAR_args(tdbb, csb, VALUE, arg_count, fun_inputs); 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); } 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)); } 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; } if (fun_entrypoint) { FUN_evaluate(tdbb, this, args, value); } else if (fun_external) { 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(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(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(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(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); } 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)); } 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; }