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
|
|
|
|
*
|
2001-05-23 15:26:42 +02:00
|
|
|
*/
|
|
|
|
/*
|
2003-03-24 14:35:37 +01:00
|
|
|
$Id: fun.epp,v 1.19 2003-03-24 13:35:33 brodsom Exp $
|
2001-05-23 15:26:42 +02:00
|
|
|
*/
|
|
|
|
|
2001-07-29 19:42:23 +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"
|
2001-07-29 19:42:23 +02:00
|
|
|
#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
|
2002-09-06 15:05:33 +02:00
|
|
|
#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
|
2002-09-06 15:05:33 +02:00
|
|
|
#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
|
|
|
|
|
|
|
|
2002-11-12 06:05:01 +01: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;
|
|
|
|
|
|
|
|
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 =
|
2002-09-25 19:12:16 +02:00
|
|
|
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;
|
2002-11-17 01:13:59 +01:00
|
|
|
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;
|
2002-11-12 06:05:01 +01:00
|
|
|
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
|
|
|
|
|
2002-07-29 17:37:59 +02:00
|
|
|
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) {
|
2002-09-25 19:12:16 +02:00
|
|
|
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_sql_time:
|
|
|
|
case dtype_sql_date:
|
|
|
|
case dtype_long:
|
|
|
|
{
|
2001-12-24 03:51:06 +01:00
|
|
|
SLONG l;
|
|
|
|
/* this check added for bug 74193 */
|
|
|
|
if (tail->fun_desc.dsc_dtype == dtype_long)
|
|
|
|
{
|
|
|
|
l =
|
|
|
|
MOV_get_long(input,
|
|
|
|
(SSHORT) tail->fun_desc.dsc_scale);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* for sql_date and sql_time just move the value to a long,
|
|
|
|
scale does not mean anything for it */
|
|
|
|
l = *((SLONG *) (input->dsc_address));
|
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2001-12-24 03:51:06 +01:00
|
|
|
if (tail->fun_mechanism == FUN_value)
|
|
|
|
{
|
|
|
|
*arg_ptr++ = l;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
{
|
|
|
|
SLONG* 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
|
|
|
{
|
2001-07-12 08:32:05 +02: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);
|
2001-07-12 08:32:05 +02:00
|
|
|
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;
|
2002-09-06 15:05:33 +02:00
|
|
|
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 =
|
2002-09-06 15:05:33 +02:00
|
|
|
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)
|
2002-09-06 15:05:33 +02:00
|
|
|
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)
|
2002-09-06 15:05:33 +02:00
|
|
|
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 =
|
2002-09-06 15:05:33 +02:00
|
|
|
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 =
|
2002-09-06 15:05:33 +02:00
|
|
|
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;
|
2002-09-06 15:05:33 +02:00
|
|
|
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:
|
|
|
|
case dtype_cstring:
|
|
|
|
case dtype_varying:
|
|
|
|
temp_desc = value->vlu_desc;
|
|
|
|
temp_desc.dsc_address = temp_ptr;
|
|
|
|
temp_desc.dsc_length = strlen((char*)temp_ptr) + 1;
|
|
|
|
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) {
|
2003-01-16 18:47:10 +01:00
|
|
|
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)
|
|
|
|
{
|
2003-01-16 18:47:10 +01:00
|
|
|
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
|
2003-02-17 09:42:00 +01:00
|
|
|
* 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)
|
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* 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 */
|
2003-03-24 14:35:37 +01:00
|
|
|
#ifdef V4_THREADING
|
2001-05-23 15:26:42 +02:00
|
|
|
V4_JRD_MUTEX_LOCK(dbb->dbb_mutexes + DBB_MUTX_udf);
|
2003-03-24 14:35:37 +01:00
|
|
|
#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) {
|
2003-03-24 14:35:37 +01:00
|
|
|
#ifdef V4_THREADING
|
2001-05-23 15:26:42 +02:00
|
|
|
V4_JRD_MUTEX_UNLOCK(dbb->dbb_mutexes + DBB_MUTX_udf);
|
2003-03-24 14:35:37 +01:00
|
|
|
#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;
|
2002-09-25 19:12:16 +02:00
|
|
|
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 =
|
2002-09-25 19:12:16 +02:00
|
|
|
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;
|
2001-07-12 08:32:05 +02:00
|
|
|
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);
|
|
|
|
|
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;
|
2002-09-25 19:12:16 +02:00
|
|
|
function->fun_symbol = symbol = FB_NEW(*dbb->dbb_permanent) sym;
|
2001-05-23 15:26:42 +02:00
|
|
|
symbol->sym_object = (BLK) function;
|
2002-09-25 19:12:16 +02:00
|
|
|
string = FB_NEW_RPT(*dbb->dbb_permanent, strlen(name)) str;
|
2001-07-12 08:32:05 +02:00
|
|
|
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;
|
|
|
|
|
2003-03-24 14:35:37 +01:00
|
|
|
#ifdef V4_THREADING
|
2001-05-23 15:26:42 +02:00
|
|
|
V4_JRD_MUTEX_UNLOCK(dbb->dbb_mutexes + DBB_MUTX_udf);
|
2003-03-24 14:35:37 +01:00
|
|
|
#endif
|
2001-05-23 15:26:42 +02:00
|
|
|
return prior;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-11-12 06:05:01 +01:00
|
|
|
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;
|
2002-11-12 06:05:01 +01:00
|
|
|
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;
|
2001-07-12 08:32:05 +02:00
|
|
|
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);
|
2003-01-16 18:47:10 +01:00
|
|
|
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;
|
2003-01-16 18:47:10 +01:00
|
|
|
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);
|
2003-01-16 18:47:10 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2001-07-12 08:32:05 +02:00
|
|
|
return sizeof(struct sad) + (dimensions - 1) * sizeof(sad::sad_repeat);
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|