35. Функции, определяемые пользователем
35.1. Понятие функции, определяемой пользователем
Состав встроенных функций
InterBase весьма небогат - в него входят функции вычисления агрегированных значений (MIN, MAX, SUM, A VG), функция преобразования букв UPPER и функция приведения типа CAST.Часто разработчики нуждаются в дополнительных функциях, которые можно было бы использовать так же, как и стандартные встроенные функции
InterBase. Это могут быть функции вычисления модуля от вещественного значения, определения длины строки, усечения хвостовых или ведущих пробелов в символьных значениях, извлечение из даты значения месяца, года и т.д.Специально для таких целей в
InterBase существует аппарат функции, определяемых пользователем (User Defined Functions, UDF).Разработка
UDF может осуществляться на любом алгоритмическом языке, позволяющем создавать DLL (dynamic link library, динамически загружаемые библиотеки). Каждая UDF оформляется в виде функции, входящей в состав DLL. Таким образом, одна динамически загружаемая библиотека состоит минимум из одной функции.После того как
DLL разработана, она либо перемещается в подкаталог BIN каталога размещения InterBase, либо располагается в ином каталоге, путь к которому известен в операционной системе.Каждая функция определяется оператором
DECLARE EXTERNAL FUNCTION. Этот оператор устанавливает связь между функцией из DLL и ее описанием в БД. После этого функция может использоваться в SQL-операторах наряду со стандартными функциями InterBase.Преимущества UDF:
• динамически загружаемая библиотека и определенные в ней функции могут использоваться более чем одной БД и более чем одним приложением; таким образом осуществляется повторное использование однажды написанного кода;
• пользователь может реализовать в UDF достаточно сложные алгоритмы.
DLL и UDF в DelphiЧтобы создать с помощью
Delphi динамически загружаемую библиотеку, необходимо в главном меню выбрать режим New и затем на странице New выбрать пиктограмму DLL для построения шаблона DLL. Текст модуля DLL должен содержать:• заголовок
Library имя, где имя - имя создаваемой DLL;• в разделе реализации модуля нужно разместить один или несколько блоков
exports, в которых через запятую перечисляются имена экспортируемых функций; каждая функция должна описываться выше блока по тексту модуля;• каждая экспортируемая функция объявляется с использованием директив
export и cdecl(последняя указывает компилятору, что функция использует соглашения для передачи параметров, принятые в C/C++).Пример определения функции:
function SomeName(parami : Integer) : Integer; cdecl; export;
begin
end;
exports
SomeName;
35.2.2. Совместимость типов параметров
При описании параметров в БД (оператор
DECLARE EXTERNAL FUNCTION) и параметров функций в DLL следует помнить о совместимости типов Object Pascal и InterBase:Тип
InterBase Тип Object PascalINTEGER Integer
DOUBLE PRECISION Double
CSTRING PChar
DATE IBDateTime = record
// нужно дополнительное преобразование значений
Days : Integer;
Msec: Cardinal;
end;
Данное соответствие типов верно для случая, когда результат функции передается в базу данных по значению. Для того чтобы результат передавался по значению, необходимо в операторе
DECLARE EXTERNAL FUNCTION после слова RETURNS указать слово BY VALUE. В следующем примере объявляется функция DEN типа DATE, содержащаяся в DLL с именем UDF_DLL:DECLARE EXTERNAL FUNCTION DEN DATE
RETURNS INTEGER BY VALUE
ENTRY_POINT "Den"
MODULE_NAME "udf_dll";
В том случае, если результат работы UDF передается в БД по ссылке, необходимо использовать указатели на соответствующие типы:
InterBase Object Pascal
INTEGER integer
DOUBLE PRECISION double
DATE IBDateTime
Тип
PChar всегда передается по ссылке.Будем использовать только параметры, передаваемые по значению, поскольку вызовы
UDF предполагается осуществлять в SQL-операторах типа SELECT, INSERT, UPDATE, DELETE, а эти операторы не будут изменять содержимое параметров.35.2.3. Особенности использования в UDF параметров типа PChar
Параметры типа PChar используются для совместимости с форматом представления строк C/C++, однако в
Object Pascal со строками, передаваемыми как PChar, в теле функции лучше работать как с длинной строкой Pascal (String), воспользовавшись преобразованием из типа PChar в String и обратно.Пример. Функция принимает строку типа PChar и отсекает хвостовые и ведущие пробелы:
function TrimChar(InString : PChar) : PChar; cdecl; export;
begin
Result := PChar(Trim(AnsiString(InString)));
end;
35.2.4. Особенности использования в UDF параметров типа даты и времени
Значения
InterBase типа DATE в Object Pascal интерпретируются как запись, состоящая из двух полей - целочисленного знакового и беззнакового.IBDateTime = record
Days : Integer;
Msec : Cardinal;
end;
Для того чтобы перевести значение из формата
IBDateTime в формат даты и времени Delphi TDateTime, необходимо произвести следующее преобразование: Days -15018 +ДamaInlerBase.MSec/(MSecsPerDay * 10);где константа
MSecsPerDay определена в Delphi как число миллисекунд в сутках. InterBaseДля объявления функции, определенной пользователем, в БД
InterBase, необходимо выполнить операторDECLARE EXTERNAL FUNCTION
ИмяФункции[<Тип данных> |
CSTRING (число) [, <Тип данных> | CSTRING (число) ...]]RETURNS {<
Тип данных > [BY VALUE] | CSTRING (число)}ENTRY_POINT "<
Имя функции в DLL>"MODULE_NAME "<
Имя DLL >";ИмяФункции -
имя функции, под которой функция будет известна в БД. Это имя может отличаться от имени UDF в DLL. После имени функции следует список типов входных параметров функции. Это либо тип данных, разрешенный в InterBase, либо CSTRING (число для строковых значений. Число определяет размер строкового значения в символах. Если число меньше действительного размера строки, строка при передаче в UDF усекается.После слова RETURNS указывается тип возвращаемого параметра функции. Это либо тип данных, разрешенный в InterBase, либо CSTRING (число) для строковых значений. Слова BY VALUE означают, что результат функции возвращается по значению, а не по ссылке.
После ENTR Y_POINTa кавычках указывается имя функции в DLL, а после слов MODULE_NAME - имя модуля DLL (без расширения).
Удалить из БД объявление функции, определенной пользователем, можно при помощи оператора
DROP EXTERNAL FUNCTION
ИмяФункции; DLL с несколькими UDF и объявления их в БДСоздадим
DLL с именем 'UDF_DLL', в состав которой входят три функции:library udf_dll;
uses
SysUtils,
Classes;
type
TIBDateTime = record
Days,
MSecIO : Cardinal;
end;
PInteger = ^Integer;
//
функция усекает ведущие и хвостовые пробелы у строкового // значения, передаваемого как параметр function TrimChar(InString : PChar) : PChar; cdecl; export;begin
Result := PChar(Trim(AnsiString(InString)));
end;
//функция возвращает (по значению) номер дня передаваемой в качестве параметра даты
function Den(var InDate : TIBDateTime) : Integer; cdecl; export;
var DT : TDateTime;
Gd,Ms,Dn : Word;
begin
DT := InDate.Days - 15018 + InDate.MSecIO / (MSecsPerDay * 10);
DecodeDate(DT,Gd,Ms,Dn) ;
Result := Integer(Dn);
end;
//функция возвращает (по ссылке) номер месяца передаваемой в качестве параметра даты
function Mes(var InDate : TIBDateTime) : PInteger; cdecl; export;
var DT : TDateTime;
Gd,Ms,Dn : Word;
R : Integer;
begin
DT := InDate.Days - 15018 + InDate.MSecIO / (MSecsPerDay * 10);
DecodeDate(DT,Gd,Ms,Dn);
R := Ms;
Result := @R;
end;
exports
TrimChar,
Den,
Mes;
begin
end.
После генерации модуля
UDF_DLL.DLL переместим его в подкаталог BIN каталога на диске, в котором расположен сервер InterBase. Затем объявим функции в БД:DECLARE EXTERNAL FUNCTION TRIMCHAR CSTRING(256)
RETURNS CSTRING(256)
ENTRY_POINT "TrimChar"
MODULE_NAME "udf_dll";
DECLARE EXTERNAL FUNCTION DEN DATE
RETURNS INTEGER BY VALUE
ENTRY_POINT "Den"
MODULE_NAME "udf_dll";
DECLARE EXTERNAL FUNCTION MES DATE
RETURNS INTEGER
ENTRY_POINT "Mes"
MODULE_NAME "udf_dll";
Примеры использования объявленных в БД функций пользователя в операторе
SELECT:SELECT TRIMCHAR (POKUP) || ' ' II GOROD
FROM POKUPATELI
SELECT *
FROM RASHOD
WHERE DEN(DAT_RASH) > 10;