Subject UDF assistance
Author Adam
Hello Group,

I am having some difficulty with a small number of UDF calls with
Firebird 2.1.

These have worked for years alongside Firebird 1.5. Furthermore, the
problem is intermittent and difficult to reduce to a simple test case,
as even removing an unrelated join from the query stops the error from
occurring. I do have a reproducable test case, but it involves a 30
line SQL statement and a 1GB database. I think it is better to rather
start with the code I am using to see if there are any obvious errors.
I have not been able to reproduce the error using "select
FB_Addmins('[some time literal]', [some integer]) from RDB$DATABASE;"
query (even using the same values that result in the error.

* 99.9% of the time, it works fine even under 2.1
* The error does not occur until after the function has completed, as
I receive FB_AddMins End in the debugger.
* I have also tried changing the isc_decode_timestamp and
isc_encode_timestamp to cdecl but it made no difference.

The exact error message is:
Statement failed, SQLCODE = -902
Unable to complete network request to host "lappy".
-Error reading data from the connection.
-An existing connection was forcibly closed by the remote host.

The function's purpose is to take a timestamp and a number of minutes,
and to return a timestamp that many minutes later. (Yes, I know this
particular function can also be done in PSQL). Can anyone see anything
glaringly wrong with the code?

TIA
Adam

(I have extracted the function into a new dll to simplify the required
code, but I have confirmed the problem still occurs with this separate
dll).

Declaration:
============

DECLARE EXTERNAL FUNCTION FB_ADDMINS
TIMESTAMP,INTEGER
RETURNS TIMESTAMP FREE_IT
ENTRY_POINT 'FB_AddMins' MODULE_NAME 'MyUDF';

UDF Code (Delphi 7):
==================

library MyUDF;

uses
SysUtils,
Classes,
uMyUDF in 'uMyUDF.pas';

{$R *.res}

exports

FB_AddMins;

begin
end.

---------------------------------------------

unit uMyUDF;

interface

type
Long = LongInt;
ULong = Cardinal;

ISC_TIMESTAMP = record
timestamp_date : Long;
timestamp_time : ULong;
end;

PISC_TIMESTAMP = ^ISC_TIMESTAMP;

TM = record
tm_sec : integer; // Seconds
tm_min : integer; // Minutes
tm_hour : integer; // Hour (0--23)
tm_mday : integer; // Day of month (1--31)
tm_mon : integer; // Month (0--11)
tm_year : integer; // Year (calendar year minus 1900)
tm_wday : integer; // Weekday (0--6) Sunday = 0)
tm_yday : integer; // Day of year (0--365)
tm_isdst : integer; // 0 if daylight savings time is not in effect)
end;

PTM = ^TM;

function FB_AddMins(var DT: ISC_TIMESTAMP; var Minutes: Integer):
PISC_TIMESTAMP; cdecl;

implementation

uses
ibexternals,
Windows,
DateUtils;


procedure isc_decode_timestamp(ib_date: PISC_TIMESTAMP; tm_date: PTM);
stdcall; external 'gds32.dll';
procedure isc_encode_timestamp(tm_date: PTM; ib_date: PISC_TIMESTAMP);
stdcall; external 'gds32.dll';


function TimeStampToDateTime(TimeStamp: ISC_TIMESTAMP): TDateTime;
var
TempTM: TM;
begin
isc_decode_timestamp(@TimeStamp,
@TempTM);

Result := EncodeDateTime(TempTM.tm_year+1900,
TempTM.tm_mon+1,
TempTM.tm_mday,
TempTM.tm_hour,
TempTM.tm_min,
0,
0);
end;

function DateTimeToTimeStamp(DateTime: TDateTime): ISC_TIMESTAMP;
var
Y,M,D,H,N,Dummy: Word;
TempTM: TM;
begin
DecodeDateTime(DateTime,
Y,
M,
D,
H,
N,
Dummy,
Dummy);

TempTM.tm_year := Y - 1900;
TempTM.tm_mon := M - 1;
TempTM.tm_mday := D;
TempTM.tm_hour := H;
TempTM.tm_min := N;
TempTM.tm_sec := 0;

isc_encode_timestamp(@TempTM, @Result);
end;

{Export Functions}

function FB_AddMins(var DT: ISC_TIMESTAMP; var Minutes: Integer):
PISC_TIMESTAMP; cdecl;
var
DTConverted: TDateTime;
begin
OutputDebugString('FB_AddMins Start');
DTConverted := TimeStampToDateTime(DT);
DTConverted := DTConverted + Minutes/1440;
DT := DateTimeToTimeStamp(DTConverted);
Result := @DT;
OutputDebugString('FB_AddMins End');
end;

end.