Функции пользователя (UDFs)

Функциями пользователя (user defined functions) называются функции для выполнения операций, которые непосредственно не поддерживаются InterBase. Для сервера InterBase на платформе Windows функции пользователя располагаются внутри DLL-библиотек, созданных на языках C, C++, Pascal или других языках высокого уровня. К функциям пользователя можно обра-щаться из операторов SELECT, хранимых процедур и триггеров.

Процесс создание функции пользователя состоит из трех шагов:

1. Написание функции на одном из языков, таких как C или Delphi.

2. Создание динамической библиотеки, содержащей функцию и размещение ее в папке "С:\Program Files\Firebird 1.5\UDF\" на сервере.

3. Объявление функции пользователя в базе данных.

10.4.2. Создание динамической библиотеки с UDF

Ниже приведен пример библиотеки и модуля с тремя UDF:

// файл LibUDF.dpr с текстом библиотеки для Delphi 7: library LibUDF; uses ExtFunct in 'ExtFunct.pas'; exports IsDate index 1 name 'ISDATE', IBDateToStr index 2 name 'IBDATETOSTR', RoundFloat index 3 name 'ROUNDFLOAT'; end. 72

// файл ExtFunct.pas с текстом модуля для Delphi 7: unit ExtFunct; interface uses SysUtils; type PIBDateTime = ^TIBDateTime; TIBDateTime = record Date: Integer; Time: Integer; end; function IsDate(var InputDate: TIBDateTime): PIBDateTime; cdecl; function IBDateToStr(var InputDate: TIBDateTime): PChar; cdecl; function RoundFloat(var Value: Double; var Digits: Integer): Double; cdecl; procedure isc_decode_date(IBDateTime: PIBDateTime; P: Pointer); stdcall; implementation procedure isc_decode_date; external 'gds32.dll' name 'isc_decode_date'; var OutDate: TIBDateTime; function IsDate(var InputDate: TIBDateTime): PIBDateTime; cdecl; begin // первые 4 байта в InputDate - дата, // последние 4 байта в InputDate - время; // достаточно обнулить данные о времени чтобы // получить только дату. with OutDate do begin Date:= InputDate.Date; Time:= 0; end; Result:= @OutDate; end;

type PCTimeStructure = ^TCTimeStructure; TCTimeStructure = 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"-1900 tm_wday: integer; // Weekday (0--6) Sunday = 0) tm_yday: integer; // Day of year (0--365) tm_isdst: integer; end; var DateStr: string[11] = #0#0#0#0#0#0#0#0#0#0#0; function IBDateToStr(var InputDate: TIBDateTime): PChar; cdecl; var B: TCTimeStructure; D, M, Y, I: Integer; begin // преобразует дату из формата InterBase. isc_decode_date(@InputDate, @B); D:= B.tm_mday; M:= B.tm_mon + 1; Y:= B.tm_year + 1900; DateStr:= Format('%2d-%2d-%4d', [D, M, Y]); repeat I:= Pos(' ', DateStr); if I > 0 then DateStr[I]:= '0'; until I = 0; Result:= @DateStr[1]; end; function RoundFloat(var Value: Double; var Digits: Integer): Double; cdecl; var F: Double; begin F:= Frac(Value); case Digits of

1: F:= Round(F*10.0)/10.0; 2: F:= Round(F*100.0)/100.0; 3: F:= Round(F*1000.0)/1000.0; else F:= 0; end; Result:= Int(Value) + F; end; end.

Еще один пример создания библиотеки с помощью Delphi с одной функцией пользователя:

T// Файл 'TestUDF.dpr'T для Delphi 7T: library TestUDF; // функция определения длины текста в строковых полях // типов CHAR(n) и VARCHAR(n): function LengthCharField(C: PChar): Integer; cdecl; begin // ищем нулевой символ, который является завершителем // строки: Result:= 0; while (C[Result] <> #0) do Inc(Result); // Если бы эта функция использовалась бы только для // полей типа VARCHAR, то больше ничего делать не // надо было бы. // Если поле имеет тип CHAR, то оно всегда // дополняется до максимальной длины пробелами, // поэтому их надо учесть: Dec(Result); while (Result >= 0) and (C[Result] = ' ') do Dec(Result); Inc(Result); end; exports LengthCharField name 'LENGTH_CHAR_FIELD'; end.

Чтобы получить файл с DLL-библиотекой, содержащей UDF, можно, например, ввести исходный текст библиотеки с помощью любого редактора в

файл 'TestUDF.dpr' и откомпилировать его с помощью утилиты командной строки 'dcc32.exe', введя в командной строке следующую команду:

C:\Рабочая папка>dcc32 TestUDF.dpr

В результате этой команды будет создан файл 'TestUDF.DLL', который перед использованием необходимо поместить в специальную папку 'UDF', расположенную в папке с установленным сервером Firebird 1.5.

10.4.3. Объявление функций пользователя в базе данных

Синтаксис оператора объявления функции пользователя следующий:

DECLARE EXTERNAL FUNCTION name [datatype | CSTRING(int) [, datatype | CSTRING(int)...]] RETURNS {datatype [BY VALUE] | CSTRING(int)} [FREE_IT] ENTRY_POINT 'entryname' MODULE_NAME 'modulename';

Параметры, входящие в этот оператор, пояснены в табл. 5.

Таблица 5

Описание параметров оператора описания UDF

Ниже приведены примеры объявления в базе данных созданных выше функций пользователя.

Параметр Описание
name Имя функции внутри базы данных. Оно не обязательно должно совпадать с названием функции в DLL
datatype Определяет тип параметров. Все параметры всегда передаются по ссылке. значение функ-ции (результат) может возвращаться по зна-чению
CSTRING Используется для строковых параметров. В скобках необходимо указать максимальную длину строки. Если строка является результа-том функции, она всегда передается по ссыл-ке, т.е. возвращается адрес строки (указатель)
'entryname' Строка с названиемTP‡‡PT функции в DLL, кото-рую мы объявляем как пользовательскую функцию
'modulename' Строка с названием файла DLL, в котором находится функция

‡‡ Параметры entryname и modulename чувствительны к регистру 76

DECLARE EXTERNAL FUNCTION ISDATE DATE RETURNS DATE ENTRY_POINT 'ISDATE' MODULE_NAME 'LIBUDF'; DECLARE EXTERNAL FUNCTION IBDATETOSTR DATE RETURNS CSTRING(11) ENTRY_POINT 'IBDATETOSTR' MODULE_NAME 'LIBUDF'; DECLARE EXTERNAL FUNCTION ROUNDFLOAT DOUBLE PRECISION, INTEGER RETURNS DOUBLE PRECISION BY VALUE ENTRY_POINT 'ROUNDFLOAT' MODULE_NAME 'LIBUDF'; -- функция определение длины строкового поля с -- отбрасыванием конечных пробелов: DECLARE EXTERNAL FUNCTION STRING_LENGTH CSTRING(100) -- в функцию передается указатель на -- строку, заканчивающуюся нулевым -- символом (#0) и длина здесь должна -- быть не меньше, чем длина поля CHAR -- или VARCHAR RETURNS INTEGER BY VALUE –- возврат по значению ENTRY_POINT 'LENGTH_CHAR_FIELD' MODULE_NAME 'EducatorUDF';

10.4.4. Использование UDF в базе данных

Объявленную в базе данных функцию пользователя можно использо-вать при создании доменов и таблиц в вычислимых полях, значениях по умолчанию и контрольных ограничениях. Их можно использовать в операто-рах изменения данных, триггерах, хранимых процедурах и просто в запросах.

Приведем пример запроса с созданными и объявленными выше функ-циями пользователя:

-- это запрос для базы данных Employee.fdb: SELECT SALARY, HIRE_DATE, ROUNDFLOAT(SALARY, 2), IBDateToStr(HIRE_DATE), IsDate(HIRE_DATE), CAST(SALARY AS INTEGER) FROM EMPLOYEE;

10.5. Задание

Лабораторную работу №10 следует выполнять в следующем порядке:

1. Создать аналогично предыдущим лабораторным работам рабочую папку и назвать ее "ЛР10".

2. Разработать для своей базы данных не менее трех функций пользователя.

3. Создать любым текстовым редактором файл, содержащий текст на языке Pascal с этими функциями. Назвать этот файл таким же именем, как базу данных. Дать ему расширение '.dpr'.

4. Откомпилировать его либо с помощью Delphi 7.0, либо с помощью ком-пилятора командной строки 'dcc32.exe'.

5. Поместить исходный файл проекта и откомпилированную библиотеку в рабочую папку "ЛР10" на сервере для копирования в папку 'UDF'.

6. Скопировать в эту папку файл сценария, созданный при выполнении пре-дыдущей лабораторной работы.

7. Открыть в приложении "IB Expert" этот сценарий.

8. Исправить текст комментариев и сделать, чтобы база данных теперь соз-давалась в папке "ЛР10".

9. Добавить в сценарий операторы описания разработанных функций поль-зователя. Для каждой функции пользователя должны присутствовать комментарии, поясняющие ее назначение.

10. Выполнить сценарий и сохранить его в папке "ЛР10".

11. Зарегистрировать созданную базу данных в программе "IB Expert" и под-ключиться к ней.

12. Выполнить в окне "SQL Editor" по одному запросу с каждой созданной функцией пользователя. Описать результаты этих запросов в отчете.

13. Создать в папке "ЛР10" резервную копию базы данных.

14. Создать и сохранить в папке "ЛР10" файл с отчетом о выполнении лабо-раторной работы, который должен называться "Отчет.doc".

10.6. Ход работы

Для создания файла проекта Delphi с исходным текстом библиотеки можно использовать либо среду программирования Delphi 7.0, либо любой текстовый редактор. Процесс получения откомпилированной библиотеки (файла с расширением '*.DLL') описан в разделе "Создание динамической библиотеки с UDF".

После помещения команд объявления функций пользователя в сцена-рий, его надо выполнить с помощью программы "IB Expert" аналогично пре-дыдущим лабораторным работам.78

10.7. Отчет о выполнении работы

Отчет о выполнении лабораторной работы №10 необходимо оформить на листах формата A4. Отчет должен содержать описание и результаты рабо-ты, представляемые в следующей последовательности:

1. Описание разработанных для своей базы данных функций пользователя.

2. Распечатка исходного текста проекта с функциями пользователя. Обяза-тельно должны присутствовать комментарии ко всему проекту со сведе-ниями о назначении файла и его авторе, а также комментарии к создан-ным функциям пользователя.

3. Текст операторов объявления UDF в базе данных.

4. Операторы, использующие созданные и объявленные в базе данных функции пользователя.

5. Примеры запросов с использованием разработанных функций и описание результата их выполнения.

6. Перечень файлов, полученных при выполнении лабораторной работы с указанием их имен, места расположения, даты изменения и размеров (сценарий, база данных, резервная копия базы данных, исходный текст проекта с функциями пользователя, откомпилированная библиотека с функциями пользователя, файл с отчетом).

10.8. Контрольные вопросы

1. Что такое функция пользователя?

2. Как расшифровывается UDF?

3. Как расшифровывается DLL?

4. Где должна располагаться библиотека, чтобы ее могла использовать СУБД Firebird 1.5?

5. Что означают в исходных текстах на Pascal: cdecl, stdcall, exports, external, Result, PChar?

6. Какие существуют способы передачи параметров в функцию?

7. Какой способ передачи параметров используется для UDF в InterBase?


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: