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

1047 lines
26 KiB
Plaintext
Raw Normal View History

2001-05-23 15:26:42 +02:00
/*
* PROGRAM: JRD Access Method
* MODULE: fun.e
* DESCRIPTION: External Function handling code.
*
* 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): ______________________________________.
2002-06-30 12:44:49 +02:00
*
* 2001.9.18 Claudio Valderrama: Allow return parameter by descriptor
* to signal NULL by testing the flags of the parameter's descriptor.
2002-10-30 07:40:58 +01:00
*
* 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
*
* 2003.07.31 Fred Polizo, Jr. - Made FUN_evaluate() correctly determine
* the length of string types containing binary data (char. set octets).
2001-05-23 15:26:42 +02:00
*/
/*
$Id: fun.epp,v 1.23 2003-08-04 10:20:19 skidder Exp $
2001-05-23 15:26:42 +02:00
*/
#include "firebird.h"
2001-05-23 15:26:42 +02:00
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "../jrd/jrd.h"
#include "../jrd/val.h"
#include "../jrd/exe.h"
#include "../jrd/gds.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/req.h"
#include "../jrd/lls.h"
#include "../jrd/blb.h"
#include "../jrd/flu.h"
#include "../jrd/common.h"
#include "../jrd/constants.h"
#include "../jrd/ibsetjmp.h"
#include "../jrd/irq.h"
#include "../jrd/all_proto.h"
#include "../jrd/blb_proto.h"
#include "../jrd/cmp_proto.h"
#include "../jrd/dsc_proto.h"
#include "../jrd/err_proto.h"
#include "../jrd/evl_proto.h"
#include "../jrd/exe_proto.h"
#include "../jrd/flu_proto.h"
#include "../jrd/fun_proto.h"
#include "../jrd/gds_proto.h"
#include "../jrd/mov_proto.h"
#include "../jrd/sym_proto.h"
#include "../jrd/thd_proto.h"
#include "../jrd/sch_proto.h"
#include "../jrd/isc_s_proto.h"
#ifdef VMS
#define CALL_UDF(ptr, udftype) (*(udftype (*)(SLONG, SLONG, SLONG, SLONG, SLONG, SLONG, SLONG, SLONG, SLONG, SLONG))(ptr)) (args [0], args[1], args[2], args[3], args [4], args [5], args [6], args [7], args [8], args [9])
2001-05-23 15:26:42 +02:00
#else
#define CALL_UDF(ptr, udftype) ((udftype (*)(SLONG, SLONG, SLONG, SLONG, SLONG, SLONG, SLONG, SLONG, SLONG, SLONG))(ptr)) (args [0], args[1], args[2], args[3], args [4], args [5], args [6], args [7], args [8], args [9])
2001-05-23 15:26:42 +02:00
#endif
DATABASE DB = FILENAME "ODS.RDB";
#ifdef VMS
extern double MTH$CVT_D_G(), MTH$CVT_G_D();
#endif
#define 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:"
static SSHORT blob_get_segment(BLB, UCHAR *, USHORT, USHORT *);
static void blob_put_segment(BLB, UCHAR *, USHORT);
static SLONG blob_lseek(BLB, USHORT, SLONG);
2001-12-24 03:51:06 +01:00
static SLONG get_scalar_array(fun_repeat *, DSC *, SAD, LLS *);
2001-05-23 15:26:42 +02:00
void DLL_EXPORT FUN_evaluate(FUN function, JRD_NOD node, VLU value)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* F U N _ e v a l u a t e
*
**************************************
*
* Functional description
* Evaluate a function.
*
**************************************/
2001-12-24 03:51:06 +01:00
SLONG args[MAX_UDF_ARGUMENTS + 1];
USHORT length;
UCHAR temp[800];
STR temp_string;
SSHORT unsup_datatype = 0;
SLONG l, *lp;
2001-12-24 03:51:06 +01:00
TDBB tdbb = GET_THREAD_DATA;
2001-05-23 15:26:42 +02:00
2001-12-24 03:51:06 +01:00
// Start by constructing argument list
UCHAR* temp_ptr;
2001-05-23 15:26:42 +02:00
2001-12-24 03:51:06 +01:00
if (function->fun_temp_length < sizeof(temp))
{
2001-05-23 15:26:42 +02:00
temp_string = NULL;
temp_ptr = temp;
MOVE_CLEAR(temp, sizeof(temp));
}
else
{
/* Need to align the starting address because it consists of
a number of data blocks with aligned length */
temp_string =
FB_NEW_RPT(*tdbb->tdbb_default,
2001-12-24 03:51:06 +01:00
function->fun_temp_length + DOUBLE_ALIGN - 1) str;
2001-05-23 15:26:42 +02:00
MOVE_CLEAR(temp_string->str_data, temp_string->str_length);
temp_ptr =
(UCHAR *) FB_ALIGN((U_IPTR) temp_string->str_data, DOUBLE_ALIGN);
}
MOVE_CLEAR(args, sizeof(args));
2001-12-24 03:51:06 +01:00
SLONG* arg_ptr = args;
LLS blob_stack = 0;
LLS array_stack = 0;
JRD_REQ request = tdbb->tdbb_request;
2001-12-24 03:51:06 +01:00
USHORT null_flag = (USHORT)(request->req_flags & req_null);
fun_repeat* return_ptr = function->fun_rpt + function->fun_return_arg;
JRD_NOD* ptr = node->nod_arg;
2001-12-24 03:51:06 +01:00
2001-05-23 15:26:42 +02:00
value->vlu_desc = return_ptr->fun_desc;
value->vlu_desc.dsc_address = (UCHAR *) & value->vlu_misc;
/* Trap any potential errors */
2001-12-24 03:51:06 +01:00
#pragma FB_COMPILER_MESSAGE("Note that we cannot use C++ EH here for Win32 SS!")
2003-02-11 01:36:30 +01:00
#if !defined(SUPERSERVER) && !defined(WIN_NT)
2001-12-24 03:51:06 +01:00
try {
#endif
START_CHECK_FOR_EXCEPTIONS( (TEXT*) function->fun_exception_message);
2001-05-23 15:26:42 +02:00
2001-12-24 03:51:06 +01:00
// If the return data type is any of the string types,
// allocate space to hold value
2001-05-23 15:26:42 +02:00
2001-12-24 03:51:06 +01:00
if (value->vlu_desc.dsc_dtype <= dtype_varying)
{
2001-05-23 15:26:42 +02:00
length = value->vlu_desc.dsc_length;
2001-12-24 03:51:06 +01:00
STR string = value->vlu_string;
if (string && string->str_length < length) {
delete string;
2001-05-23 15:26:42 +02:00
string = NULL;
}
if (!string) {
string = FB_NEW_RPT(*tdbb->tdbb_default, length) str;
2001-05-23 15:26:42 +02:00
string->str_length = length;
value->vlu_string = string;
}
value->vlu_desc.dsc_address = string->str_data;
}
2001-12-24 03:51:06 +01:00
DSC temp_desc;
double d;
double* dp;
// Process arguments
const fun_repeat* end = function->fun_rpt + 1 + function->fun_count;
for (fun_repeat* tail = function->fun_rpt + 1; tail < end; ++tail)
{
DSC* input;
if (tail == return_ptr) {
2002-06-30 12:44:49 +02:00
input = &value->vlu_desc; /*(DSC*) value;*/
2001-12-24 03:51:06 +01:00
} else {
2001-05-23 15:26:42 +02:00
input = EVL_expr(tdbb, *ptr++);
2001-12-24 03:51:06 +01:00
}
SLONG** ap = (SLONG **) arg_ptr;
2001-05-23 15:26:42 +02:00
2001-12-24 03:51:06 +01:00
// If we're passing data type ISC descriptor, there's
// nothing left to be done
2001-05-23 15:26:42 +02:00
if (tail->fun_mechanism == FUN_descriptor) {
*ap++ = (SLONG *) input;
arg_ptr = (SLONG *) ap;
continue;
}
temp_desc = tail->fun_desc;
temp_desc.dsc_address = temp_ptr;
length = FB_ALIGN(temp_desc.dsc_length, DOUBLE_ALIGN);
/* If we've got a null argument, just pass zeros (got any better ideas?) */
if (!input || (request->req_flags & req_null))
{
if (tail->fun_mechanism == FUN_value)
{
2001-12-24 03:51:06 +01:00
UCHAR* p = (UCHAR *) arg_ptr;
2001-05-23 15:26:42 +02:00
MOVE_CLEAR(p, (SLONG) length);
p += length;
arg_ptr = (SLONG *) p;
continue;
}
else
{
MOVE_CLEAR(temp_ptr, (SLONG) length);
}
}
else if ((SSHORT) abs(tail->fun_mechanism) == FUN_scalar_array)
{
length = get_scalar_array(tail, input, (SAD)temp_ptr, &array_stack);
}
else
{
switch (tail->fun_desc.dsc_dtype)
{
case dtype_short:
2001-12-24 03:51:06 +01:00
{
SSHORT s = MOV_get_long(input,
(SSHORT) tail->fun_desc.dsc_scale);
if (tail->fun_mechanism == FUN_value) {
/* For (apparent) portability reasons, SHORT by value
* is always passed as a LONG. See v3.2 release notes
* Passing by value is not supported in SQL due to
* these problems, but can still occur in GDML.
* 1994-September-28 David Schnepper
*/
*arg_ptr++ = (SLONG) s;
continue;
}
{
SSHORT* sp = (SSHORT *) temp_ptr;
*sp = s;
}
2001-05-23 15:26:42 +02:00
}
break;
case dtype_long:
l = MOV_get_long(input, (SSHORT) tail->fun_desc.dsc_scale);
if (tail->fun_mechanism == FUN_value)
2001-05-23 15:26:42 +02:00
{
*arg_ptr++ = l;
continue;
}
lp = (SLONG *) temp_ptr;
*lp = l;
break;
2001-05-23 15:26:42 +02:00
case dtype_sql_time:
l = MOV_get_sql_time(input);
if (tail->fun_mechanism == FUN_value)
{
*arg_ptr++ = l;
continue;
}
lp = (SLONG *) temp_ptr;
*lp = l;
break;
case dtype_sql_date:
l = MOV_get_sql_date(input);
if (tail->fun_mechanism == FUN_value)
{
*arg_ptr++ = l;
continue;
2001-05-23 15:26:42 +02:00
}
lp = (SLONG *) temp_ptr;
*lp = l;
2001-05-23 15:26:42 +02:00
break;
case dtype_int64:
{
2001-12-24 03:51:06 +01:00
SINT64* pi64;
SINT64 i64 =
MOV_get_int64(input, (SSHORT)tail->fun_desc.dsc_scale);
if (tail->fun_mechanism == FUN_value)
{
pi64 = (SINT64 *) arg_ptr;
*pi64++ = i64;
arg_ptr = (SLONG *) pi64;
continue;
}
pi64 = (SINT64 *) temp_ptr;
*pi64 = i64;
2001-05-23 15:26:42 +02:00
}
break;
case dtype_real:
{
2001-12-24 03:51:06 +01:00
float f = (float) MOV_get_double(input);
if (tail->fun_mechanism == FUN_value)
{
/* For (apparent) portability reasons, FLOAT by value
* is always passed as a DOUBLE. See v3.2 release notes
* Passing by value is not supported in SQL due to
* these problems, but can still occur in GDML.
* 1994-September-28 David Schnepper
*/
double* dp = (double *) arg_ptr;
*dp++ = (double) f;
arg_ptr = (SLONG *) dp;
continue;
}
float* fp = (float *) temp_ptr;
*fp = f;
2001-05-23 15:26:42 +02:00
}
break;
case dtype_double:
#ifdef VMS
case dtype_d_float:
d = MOV_get_double(input);
if (tail->fun_desc.dsc_dtype == SPECIAL_DOUBLE)
{
d = CNVT_FROM_DFLT(&d);
}
#else
d = MOV_get_double(input);
#endif
if (tail->fun_mechanism == FUN_value)
{
dp = (double *) arg_ptr;
*dp++ = d;
arg_ptr = (SLONG *) dp;
continue;
}
dp = (double *) temp_ptr;
*dp = d;
break;
case dtype_text:
case dtype_cstring:
case dtype_varying:
if (tail == return_ptr) {
temp_ptr = value->vlu_desc.dsc_address;
length = 0;
}
else
MOV_move(input, &temp_desc);
break;
2002-06-30 12:44:49 +02:00
/* CVC: There's no other solution for now: timestamp can't be returned
by value and the other way is to force the user to pass a dummy value as
an argument to keep the engine happy. So, here's the hack. */
2001-05-23 15:26:42 +02:00
case dtype_timestamp:
2002-06-30 12:44:49 +02:00
if (tail == return_ptr)
{
temp_ptr = value->vlu_desc.dsc_address;
length = sizeof(GDS_TIMESTAMP);
}
else
{
MOV_move(input, &temp_desc);
}
break;
2001-05-23 15:26:42 +02:00
case dtype_quad:
case dtype_array:
MOV_move(input, &temp_desc);
break;
case dtype_blob:
2001-12-24 03:51:06 +01:00
{
BLOB blob_desc = (BLOB) temp_ptr;
BLB blob;
2001-05-23 15:26:42 +02:00
length = sizeof(struct blob);
if (tail == return_ptr)
{
blob =
BLB_create(tdbb, tdbb->tdbb_request->req_transaction,
(BID) & value->vlu_misc);
}
else
{
2001-12-24 03:51:06 +01:00
SLONG* blob_id;
if (request->req_flags & req_null)
{
SLONG null_quad[2];
2001-05-23 15:26:42 +02:00
null_quad[0] = null_quad[1] = 0;
blob_id = null_quad;
}
else
{
if (input->dsc_dtype != dtype_quad &&
input->dsc_dtype != dtype_blob)
2001-12-24 03:51:06 +01:00
{
ERR_post(gds_wish_list,
gds_arg_gds, gds_blobnotsup,
2001-05-23 15:26:42 +02:00
gds_arg_string, "conversion", 0);
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
blob_id = (SLONG *) input->dsc_address;
}
blob = BLB_open(tdbb,
tdbb->tdbb_request->req_transaction,
(BID)blob_id);
}
LLS_PUSH(blob, &blob_stack);
blob_desc->blob_get_segment = (SSHORT (*)())blob_get_segment;
blob_desc->blob_put_segment = (void (*)())blob_put_segment;
blob_desc->blob_seek = (SLONG (*)())blob_lseek;
2001-05-23 15:26:42 +02:00
blob_desc->blob_handle = (int *) blob;
blob_desc->blob_number_segments = blob->blb_count;
blob_desc->blob_max_segment = blob->blb_max_segment;
blob_desc->blob_total_length = blob->blb_length;
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
break;
default:
assert(FALSE);
MOV_move(input, &temp_desc);
break;
}
}
*ap++ = (SLONG *) temp_ptr;
arg_ptr = (SLONG *) ap;
temp_ptr += length;
}
if (function->fun_return_arg)
{
THREAD_EXIT;
CALL_UDF(function->fun_entrypoint, void);
2001-05-23 15:26:42 +02:00
THREAD_ENTER;
}
else if ((SLONG) return_ptr->fun_mechanism == FUN_value)
{
switch (value->vlu_desc.dsc_dtype)
{
case dtype_sql_time:
case dtype_sql_date:
case dtype_long:
THREAD_EXIT;
value->vlu_misc.vlu_long =
CALL_UDF(function->fun_entrypoint, SLONG);
2001-05-23 15:26:42 +02:00
THREAD_ENTER;
break;
case dtype_short:
/* For (apparent) portability reasons, SHORT by value
* must always be returned as a LONG. See v3.2 release notes
* 1994-September-28 David Schnepper
*/
THREAD_EXIT;
value->vlu_misc.vlu_short = (SSHORT)
CALL_UDF(function->fun_entrypoint, SLONG);
2001-05-23 15:26:42 +02:00
THREAD_ENTER;
break;
case dtype_real:
/* For (apparent) portability reasons, FLOAT by value
* must always be returned as a DOUBLE. See v3.2 release notes
* 1994-September-28 David Schnepper
*/
THREAD_EXIT;
value->vlu_misc.vlu_float = (float)
CALL_UDF(function->fun_entrypoint, double);
2001-05-23 15:26:42 +02:00
THREAD_ENTER;
break;
case dtype_int64:
THREAD_EXIT;
value->vlu_misc.vlu_int64 =
CALL_UDF(function->fun_entrypoint, SINT64);
2001-05-23 15:26:42 +02:00
THREAD_ENTER;
break;
case dtype_double:
#ifdef VMS
case dtype_d_float:
#endif
THREAD_EXIT;
value->vlu_misc.vlu_double =
CALL_UDF(function->fun_entrypoint, double);
2001-05-23 15:26:42 +02:00
THREAD_ENTER;
break;
case dtype_timestamp:
default:
unsup_datatype = 1;
break;
}
}
else
{
THREAD_EXIT;
typedef UCHAR* pUCHAR;
temp_ptr = CALL_UDF(function->fun_entrypoint, pUCHAR);
2001-05-23 15:26:42 +02:00
THREAD_ENTER;
if (temp_ptr != NULL)
{
2002-06-30 12:44:49 +02:00
/* CVC: Allow processing of return by descriptor.
The assumption is that the user didn't modify the return type,
that has no sense after all, because it is carved in stone. */
if (return_ptr->fun_mechanism == FUN_descriptor)
2001-05-23 15:26:42 +02:00
{
2002-06-30 12:44:49 +02:00
temp_ptr = ((DSC*) temp_ptr)->dsc_address;
}
if (temp_ptr)
{
switch (value->vlu_desc.dsc_dtype)
{
case dtype_sql_date:
case dtype_sql_time:
case dtype_long:
value->vlu_misc.vlu_long = *(SLONG *) temp_ptr;
break;
2001-05-23 15:26:42 +02:00
2002-06-30 12:44:49 +02:00
case dtype_short:
value->vlu_misc.vlu_short = *(SSHORT *) temp_ptr;
break;
2001-05-23 15:26:42 +02:00
2002-06-30 12:44:49 +02:00
case dtype_real:
value->vlu_misc.vlu_float = *(float *) temp_ptr;
break;
2001-05-23 15:26:42 +02:00
2002-06-30 12:44:49 +02:00
case dtype_int64:
value->vlu_misc.vlu_int64 = *(SINT64 *) temp_ptr;
break;
2001-05-23 15:26:42 +02:00
2002-06-30 12:44:49 +02:00
case dtype_double:
2001-05-23 15:26:42 +02:00
#ifdef VMS
2002-06-30 12:44:49 +02:00
case dtype_d_float:
2001-05-23 15:26:42 +02:00
#endif
2002-06-30 12:44:49 +02:00
value->vlu_misc.vlu_double = *(double *) temp_ptr;
break;
case dtype_text:
temp_desc = value->vlu_desc;
temp_desc.dsc_address = temp_ptr;
MOV_move(&temp_desc, &value->vlu_desc);
break;
2002-06-30 12:44:49 +02:00
case dtype_cstring:
/* For the ttype_binary char. set, this will truncate
* the string after the first zero octet copied.
*/
temp_desc = value->vlu_desc;
temp_desc.dsc_address = temp_ptr;
temp_desc.dsc_length =
strlen(reinterpret_cast<char *>(temp_ptr)) + 1;
MOV_move(&temp_desc, &value->vlu_desc);
break;
2002-06-30 12:44:49 +02:00
case dtype_varying:
temp_desc = value->vlu_desc;
temp_desc.dsc_address = temp_ptr;
temp_desc.dsc_length =
reinterpret_cast<VARY *>(temp_ptr)->vary_length + sizeof(USHORT);
2002-06-30 12:44:49 +02:00
MOV_move(&temp_desc, &value->vlu_desc);
break;
case dtype_timestamp:
{
SLONG* ip = (SLONG *) temp_ptr;
value->vlu_misc.vlu_dbkey[0] = *ip++;
value->vlu_misc.vlu_dbkey[1] = *ip;
}
break;
2001-05-23 15:26:42 +02:00
2002-06-30 12:44:49 +02:00
default:
unsup_datatype = 1;
break;
2001-12-24 03:51:06 +01:00
}
2002-06-30 12:44:49 +02:00
/* check if this is function has the FREE_IT set, if set and
return_value is not null, then free the return value */
if (((SLONG) return_ptr->fun_mechanism < 0) && temp_ptr)
free(temp_ptr);
2001-05-23 15:26:42 +02:00
}
}
}
2001-12-24 03:51:06 +01:00
END_CHECK_FOR_EXCEPTIONS((TEXT*)function->fun_exception_message);
2001-05-23 15:26:42 +02:00
2001-12-24 03:51:06 +01:00
if (unsup_datatype) {
2001-05-23 15:26:42 +02:00
IBERROR(169); /* msg 169 return data type not supported */
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
2003-02-11 01:36:30 +01:00
#if !defined(SUPERSERVER) && !defined(WIN_NT)
2001-12-24 03:51:06 +01:00
}
2003-02-13 14:33:57 +01:00
catch (const std::exception&) {
2001-12-24 03:51:06 +01:00
delete temp_string;
while (array_stack) {
delete[] (UCHAR*)LLS_POP(&array_stack);
2001-12-24 03:51:06 +01:00
}
ERR_punt();
}
#endif
delete temp_string;
2001-05-23 15:26:42 +02:00
while (blob_stack)
{
BLB_close(tdbb, (BLB)LLS_POP(&blob_stack));
}
while (array_stack)
{
delete[] (UCHAR*)LLS_POP(&array_stack);
2001-05-23 15:26:42 +02:00
}
2002-06-30 12:44:49 +02:00
if (temp_ptr == NULL || function->fun_return_arg &&
return_ptr->fun_mechanism == FUN_descriptor &&
(value->vlu_desc.dsc_flags & DSC_null))
2001-05-23 15:26:42 +02:00
{
request->req_flags |= req_null;
}
else
{
request->req_flags &= ~req_null;
}
request->req_flags |= null_flag;
}
void DLL_EXPORT FUN_fini( TDBB tdbb)
{
/**************************************
*
* F U N _ f i n i
*
**************************************
*
* Functional description
* Unregister interest in external function modules.
2001-05-23 15:26:42 +02:00
*
**************************************/
DBB dbb;
dbb = tdbb->tdbb_database;
while (dbb->dbb_modules)
FLU_unregister_module((MOD) LLS_POP(&dbb->dbb_modules));
}
void DLL_EXPORT FUN_init(void)
{
/**************************************
*
* F U N _ i n i t
*
**************************************
*
* Functional description
* Initialize external function mechanism.
*
**************************************/
}
FUN DLL_EXPORT FUN_lookup_function(TEXT * name, bool ShowAccessError)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* F U N _ l o o k u p _ f u n c t i o n
*
**************************************
*
* Functional description
* Lookup function by name.
*
**************************************/
TDBB tdbb;
DBB dbb;
BLK request_fun, request_arg;
FUN function, prior;
SYM symbol;
STR string;
STR exception_msg;
TEXT *p;
MOD module;
LLS stack;
USHORT count, args, l;
ULONG length;
2001-12-24 03:51:06 +01:00
fun_repeat *tail, temp[MAX_UDF_ARGUMENTS + 1];
2001-05-23 15:26:42 +02:00
tdbb = GET_THREAD_DATA;
dbb = tdbb->tdbb_database;
/* Start by looking for already defined symbol */
#ifdef V4_THREADING
2001-05-23 15:26:42 +02:00
V4_JRD_MUTEX_LOCK(dbb->dbb_mutexes + DBB_MUTX_udf);
#endif
2001-05-23 15:26:42 +02:00
for (symbol = SYM_lookup(name); symbol; symbol = symbol->sym_homonym)
2001-12-24 03:51:06 +01:00
{
2002-06-30 12:44:49 +02:00
if (symbol->sym_type == SYM_fun) {
#ifdef V4_THREADING
2001-05-23 15:26:42 +02:00
V4_JRD_MUTEX_UNLOCK(dbb->dbb_mutexes + DBB_MUTX_udf);
#endif
2001-05-23 15:26:42 +02:00
return (FUN) symbol->sym_object;
}
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
prior = NULL;
request_fun = (BLK) CMP_find_request(tdbb, irq_l_functions, IRQ_REQUESTS);
request_arg = (BLK) CMP_find_request(tdbb, irq_l_args, IRQ_REQUESTS);
FOR(REQUEST_HANDLE request_fun) X IN RDB$FUNCTIONS
WITH X.RDB$FUNCTION_NAME EQ name
if (!REQUEST(irq_l_functions))
REQUEST(irq_l_functions) = request_fun;
count = args = 0;
MOVE_CLEAR(temp, (SLONG) sizeof(temp));
length = 0;
FOR(REQUEST_HANDLE request_arg) Y IN RDB$FUNCTION_ARGUMENTS
WITH Y.RDB$FUNCTION_NAME EQ X.RDB$FUNCTION_NAME
SORTED BY Y.RDB$ARGUMENT_POSITION
if (!REQUEST(irq_l_args))
REQUEST(irq_l_args) = request_arg;
tail = temp + Y.RDB$ARGUMENT_POSITION;
tail->fun_mechanism = (FUN_T) Y.RDB$MECHANISM;
count = MAX(count, Y.RDB$ARGUMENT_POSITION);
DSC_make_descriptor(&tail->fun_desc, Y.RDB$FIELD_TYPE,
Y.RDB$FIELD_SCALE, Y.RDB$FIELD_LENGTH,
Y.RDB$FIELD_SUB_TYPE, Y.RDB$CHARACTER_SET_ID,
0);
if (tail->fun_desc.dsc_dtype == dtype_cstring)
tail->fun_desc.dsc_length++;
if (Y.RDB$ARGUMENT_POSITION != X.RDB$RETURN_ARGUMENT)
++args;
l = FB_ALIGN(tail->fun_desc.dsc_length, DOUBLE_ALIGN);
if (tail->fun_desc.dsc_dtype == dtype_blob)
l = sizeof(struct blob);
length += l;
END_FOR;
function = FB_NEW_RPT(*dbb->dbb_permanent, count + 1) fun;
2001-05-23 15:26:42 +02:00
function->fun_count = count;
function->fun_args = args;
function->fun_return_arg = X.RDB$RETURN_ARGUMENT;
function->fun_type = X.RDB$FUNCTION_TYPE;
function->fun_temp_length = length;
MOVE_FAST(temp, function->fun_rpt,
2001-12-24 03:51:06 +01:00
(count + 1) * sizeof(fun_repeat));
2001-05-23 15:26:42 +02:00
#ifndef WIN_NT /* NT allows blanks in file paths */
for (p = X.RDB$MODULE_NAME; *p && *p != ' '; p++);
*p = 0;
#endif
for (p = X.RDB$ENTRYPOINT; *p && *p != ' '; p++);
*p = 0;
/* 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. */
exception_msg =
FB_NEW_RPT(*dbb->dbb_permanent,
2001-05-23 15:26:42 +02:00
strlen(EXCEPTION_MESSAGE) + strlen(name) +
strlen(X.RDB$ENTRYPOINT) +
2001-12-24 03:51:06 +01:00
strlen(X.RDB$MODULE_NAME) + 1) str;
sprintf((char*)exception_msg->str_data, EXCEPTION_MESSAGE, name,
2001-05-23 15:26:42 +02:00
X.RDB$ENTRYPOINT, X.RDB$MODULE_NAME);
function->fun_exception_message = exception_msg;
function->fun_entrypoint =
ISC_lookup_entrypoint(X.RDB$MODULE_NAME, X.RDB$ENTRYPOINT,
ISC_EXT_LIB_PATH_ENV, ShowAccessError);
2001-05-23 15:26:42 +02:00
2001-12-24 03:51:06 +01:00
if ( (module = FLU_lookup_module(X.RDB$MODULE_NAME)) ) {
2001-05-23 15:26:42 +02:00
/* Register interest in the module by database. */
for (stack = dbb->dbb_modules; stack; stack = stack->lls_next)
if (module == (MOD) stack->lls_object)
break;
/* If the module was already registered with this database
then decrement the use count that was incremented in
ISC_lookup_entrypoint() above. Otherwise push it onto
the stack of registered modules. */
if (stack)
{
FLU_unregister_module(module);
}
else
{
2001-12-24 03:51:06 +01:00
JrdMemoryPool *old_pool;
2001-05-23 15:26:42 +02:00
old_pool = tdbb->tdbb_default;
tdbb->tdbb_default = dbb->dbb_permanent;
LLS_PUSH(module, &dbb->dbb_modules);
tdbb->tdbb_default = old_pool;
}
}
/* 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 (prior) {
function->fun_homonym = prior->fun_homonym;
prior->fun_homonym = function;
}
else
{
prior = function;
function->fun_symbol = symbol = FB_NEW(*dbb->dbb_permanent) sym;
2001-05-23 15:26:42 +02:00
symbol->sym_object = (BLK) function;
string = FB_NEW_RPT(*dbb->dbb_permanent, strlen(name)) str;
strcpy((char*)string->str_data, name);
2001-05-23 15:26:42 +02:00
symbol->sym_string = (TEXT *) string->str_data;
symbol->sym_type = SYM_fun;
SYM_insert(symbol);
}
END_FOR;
if (!REQUEST(irq_l_functions))
REQUEST(irq_l_functions) = request_fun;
if (!REQUEST(irq_l_args))
REQUEST(irq_l_args) = request_arg;
#ifdef V4_THREADING
2001-05-23 15:26:42 +02:00
V4_JRD_MUTEX_UNLOCK(dbb->dbb_mutexes + DBB_MUTX_udf);
#endif
2001-05-23 15:26:42 +02:00
return prior;
}
FUN DLL_EXPORT FUN_resolve(CSB csb, FUN function, JRD_NOD args)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* F U N _ r e s o l v e
*
**************************************
*
* Functional description
* Resolve instance of potentially overloaded function.
*
**************************************/
FUN best;
JRD_NOD *ptr, *end;
2001-05-23 15:26:42 +02:00
DSC arg;
int best_score, score;
2001-12-24 03:51:06 +01:00
fun_repeat *tail;
2001-05-23 15:26:42 +02:00
TDBB tdbb;
tdbb = GET_THREAD_DATA;
best = NULL;
best_score = 0;
end = args->nod_arg + args->nod_count;
for (; function; function = function->fun_homonym)
if (function->fun_entrypoint && function->fun_args == args->nod_count) {
score = 0;
for (ptr = args->nod_arg, tail = function->fun_rpt + 1; ptr < end;
ptr++, tail++) {
CMP_get_desc(tdbb, csb, *ptr, &arg);
if ((SSHORT) abs(tail->fun_mechanism) == FUN_descriptor)
score += 10;
else if (tail->fun_desc.dsc_dtype == dtype_blob ||
arg.dsc_dtype == dtype_blob) {
score = 0;
break;
}
else if (tail->fun_desc.dsc_dtype >= arg.dsc_dtype)
score += 10 - (arg.dsc_dtype - tail->fun_desc.dsc_dtype);
else
score += 1;
}
if (!best || score > best_score) {
best_score = score;
best = function;
}
}
return best;
}
static SLONG blob_lseek( BLB blob, USHORT mode, SLONG offset)
{
/**************************************
*
* b l o b _ l s e e k
*
**************************************
*
* Functional description
* lseek a a blob segement. Return the offset
*
**************************************/
SLONG return_offset;
/* add thread enter and thread_exit wrappers */
THREAD_ENTER;
return_offset = BLB_lseek(blob, mode, offset);
THREAD_EXIT;
return (return_offset);
}
static void blob_put_segment( BLB blob, UCHAR * buffer, USHORT length)
{
/**************************************
*
* b l o b _ p u t _ s e g m e n t
*
**************************************
*
* Functional description
* Put segment into a blob. Return nothing
*
**************************************/
TDBB tdbb;
/* As this is a call-back from a UDF, must reacquire the
engine mutex */
THREAD_ENTER;
tdbb = GET_THREAD_DATA;
BLB_put_segment(tdbb, blob, buffer, length);
THREAD_EXIT;
}
static SSHORT blob_get_segment(
BLB blob,
UCHAR * buffer,
USHORT length, USHORT * return_length)
{
/**************************************
*
* b l o b _ g e t _ s e g m e n t
*
**************************************
*
* Functional description
* Get next segment of a blob. Return the following:
*
* 1 -- Complete segment has been returned.
* 0 -- End of blob (no data returned).
* -1 -- Current segment is incomplete.
*
**************************************/
TDBB tdbb;
/* add thread enter and thread_exit wrappers */
THREAD_ENTER;
tdbb = GET_THREAD_DATA;
*return_length = BLB_get_segment(tdbb, blob, buffer, length);
THREAD_EXIT;
if (blob->blb_flags & BLB_eof)
return 0;
if (blob->blb_fragment_size)
return -1;
return 1;
}
2001-12-24 03:51:06 +01:00
static SLONG get_scalar_array(fun_repeat* arg,
2001-05-23 15:26:42 +02:00
DSC* value,
SAD scalar_desc,
LLS* stack)
{
/**************************************
*
* g e t _ s c a l a r _ a r r a y
*
**************************************
*
* Functional description
* Get and format a scalar array descriptor, then allocate space
* and fetch the array. If conversion is required, convert.
* Return length of array desc.
*
**************************************/
TDBB tdbb;
BLB blob;
ADS array_desc;
SLONG stuff[ADS_LEN(16) / 4], n;
UCHAR *data, *temp;
DSC from, to;
USHORT dimensions;
ads::ads_repeat *tail1;
sad::sad_repeat *tail2, *end;
2001-05-23 15:26:42 +02:00
tdbb = GET_THREAD_DATA;
/* Get first the array descriptor, then the array */
array_desc = (ADS) stuff;
blob = BLB_get_array(tdbb,
tdbb->tdbb_request->req_transaction,
(BID)value->dsc_address,
array_desc);
data = FB_NEW(*getDefaultMemoryPool()) UCHAR[array_desc->ads_total_length];
2001-05-23 15:26:42 +02:00
BLB_get_data(tdbb, blob, data, array_desc->ads_total_length);
dimensions = array_desc->ads_dimensions;
/* Convert array, if necessary */
to = arg->fun_desc;
from = array_desc->ads_rpt[0].ads_desc;
if (to.dsc_dtype != from.dsc_dtype ||
to.dsc_scale != from.dsc_scale || to.dsc_length != from.dsc_length) {
n = array_desc->ads_count;
to.dsc_address = temp = FB_NEW(*getDefaultMemoryPool()) UCHAR[(SLONG) to.dsc_length * n];
2001-05-23 15:26:42 +02:00
from.dsc_address = data;
for (; n; --n, to.dsc_address += to.dsc_length,
from.dsc_address += array_desc->ads_element_length)
MOV_move(&from, &to);
delete[] data;
2001-05-23 15:26:42 +02:00
data = temp;
}
/* Fill out the scalar array descriptor */
LLS_PUSH(data, stack);
scalar_desc->sad_desc = arg->fun_desc;
scalar_desc->sad_desc.dsc_address = data;
scalar_desc->sad_dimensions = dimensions;
for (tail2 = scalar_desc->sad_rpt, end = tail2 + dimensions, tail1 =
array_desc->ads_rpt; tail2 < end; ++tail1, ++tail2) {
tail2->sad_upper = tail1->ads_upper;
tail2->sad_lower = tail1->ads_lower;
}
return sizeof(struct sad) + (dimensions - 1) * sizeof(sad::sad_repeat);
2001-05-23 15:26:42 +02:00
}