НАЗНАЧЕНИЕ И ВОЗМОЖНОСТИ ИСПОЛЬЗОВАНИЯ Пользовательские функции (User defined functions - UDF) - это программы на базовом языке для выполнения в приложениях, часто используемых при работе с базой данных задач. UDF помогают обеспечить модульность приложений, выделяя их в отдельные модули многократного использования.

К UDF и фильтрам BLOB можно обращаться через isql или из программ на базовом языке. К UDF можно также обращаться из хранимых процедур и триггеров.

Замечание. UDF и фильтры BLOB не поддерживаются на серверах NetWare.

Пользовательские функции не являются частью базы. Они помещаются в отдельную библиотеку (DLL), находящуюся на той же машине, что и база данных. Чтобы сделать функции доступными в базе, их надо в ней объявить. После этого обращение к ним осуществляется точно так же, как к стандартным функциям InterBase. Другими словами, вызов этих функций может производиться везде, где синтаксисом SQL предусмотрено использование выражений.

Чтобы можно было использовать внешнюю функцию, она должна быть объявлена в базе как внешняя функция, а реализующий ее программный код помещен в библиотеку, указанную в объявлении.

ОБЪЯВЛЕНИЕ ВНЕШНЕЙ ФУНКЦИИ Как только UDF была написана и откомпилирована в библиотеку, она должна быть объявлена в базе данных, где предполагается ее использовать. Для этого предусмотрена команда DELCARE EXTERNAL FUNCTION.

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

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

DECLARE EXTERNAL FUNCTION name

[LIST_datatypes]

RETURNS {"datatype /"BY VALUE; / CSTRING ( int); [FREEIT] ENTRY_POINT 'entryname'

MODULE_NAME 'modulename';

datatypes ; ; = (datatype / CSTRING ( int) }

datatype - любой разрешенный в InterBase тип данных кроме массивов и BLOB

CSTRING ( int) - специальный тип для представления строк, представляющий собой последовательность символов, заканчивающуюся двоичным 0.

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

Таблица 6.6. Синтаксические конструкции объявления внешних функций

Конструкция Описание
name Имя UDF для использования в командах SQL; может отличаться от имени функции (в библиотеке) указанной после ключевого слова ENTRY_ POINT
datatype Тип данных входного параметра или возвращаемого значения.

Все входные параметры передаются UDF по ссылке. Возвращаемое значения может передаваться как по ссылке, так и по значению.

Использование элементов массива запрещено

RETURNS Определяет возвращаемое функцией значение
BY VALUE Указывает, что возврат производится по значению. Если конструкция опущена, то возврат производится по ссылке
CSTRING (int) Указывает, что UDF возвращает строку, заканчивающуюся ограничителем \0
FREEJT Освобождает память, занятую возвращаемым значением после выполнения UDF.

Использовать только, если память в UDF выделялась динамически

'entryname' Заключенное в кавычки имя UDF, как оно записано в библиотеке
'modulename' Спецификация файла, идентифицирующая библиотеку, которая содержит UDF. Текст должен заключаться в кавычках.

СОЗДАНИЕ ПОЛЬЗОВАТЕЛЬСКИХ ФУНКЦИЙ Тот факт, что в InterBase включен только базовый набор функций для использования в SQL-выражениях, ни в коей мере не является ограничением. Механизм User Defined Functions (UDF), позволяет писать пользовательские функции на любом компилирующем инструменте разработки (базовом языке). Такие функции выполняются на сервере, причем в рамках процесса сервера, что повышает скорость их вызова практически до уровня скорости вызова стандартных SQL-функций.

UDF могут быть написаны, вообще говоря, на любом языке. Здесь мы рассмотрим подготовку функций на С (C++). Прежде всего, отметим, что все передаваемые в UDF параметры всегда передаются по ссылке, то есть должны быть объявлены в функции как указатели. Возвращаемое значение может передаваться и по ссылке и по значению.

Рассмотрим процесс создания библиотеки функций пользователя. Детали процесса зависят, вообще говоря, от используемого компилятора. Построим такую библиотеку для Windows в среде C++Builder. Воспользуемся DLL Wizard’oM, необходимым для корректной настройки компоновщика в файле проекта. В других компиляторах проект будет строиться иначе, но сам текст программ будет идентичен. В начале текста подключается заголовочный файл <windows.h>, за которым объявляются наши функции (их объявления можно также поместить в свой заголовочный) файл).

Пример 6.22

П.!_

# include <windows.h>

extern "C" long_declspec (dllexport)

if_i (double* m, long* a, long* b) ;

extern "C" double *_declspec (dllexport) '

if_d(double* m, double* a, double* b) ;

extern "C" char *_declspec (dllexport)

r_upper(char*a) ;

extern "C" char * declspec (dllexport)

rupper(char * a ) ;

extern "C" int_declspec (dllexport)

idate(int * a ) ;

#pragma argsused

■ int WINAPI DllEntryPoint (HINSTANCE hinst, unsigned long reason, void* lpReserved)

{

return 1;

}

В объявлении функций стоит обратить внимание на конструкции extern "С'' и declspec(dllexport). Эти конструкции обеспечивают корректность хранения имен и вызова функций из создаваемой DLL. Далее идет текст самих функций. Остановимся на нем несколько подробнее.

Первая функция if_i является аналогом условной операции С. Она возвращает второй аргумент, если значение первого положительно, и третий - в противном случае.

long_declspec(dllexport)

if_i(double* m,long* a,long* b)

(return (*m>0)?*a:*b;}

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

DECLARE EXTERNAL FUNCTION IIF DOUBLE PRECISION, INTEGER, INTEGER RETURNS INTEGER BY VALUE ENTRY_POINT " i f_i"

MODULE_NAME "e:\USERSYS\MDLL\MyDLL";

Здесь "e:\USERSYSVMDLLVMyDLL" задает полный путь к DLL.

Если библиотека помещена в стандартную директорию InterBase, то путь к ней указывать не надо.

DECLARE EXTERNAL FUNCTION IIF DOUBLE PRECISION, INTEGER, INTEGER RETURNS INTEGER BY VALUE ENTRY_POINT "i f_i"

MODULE_NAME "MyDLL"; ■

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

Замечание В последних версиях InterBase местоположение DLL предопределено, это одна из поддиректорий директории, где размещен InterBase после его установки. Обычно при инсталляции устанавливается поставляемая с InterBase библиотека UDF -udf_lib.dll. Она всегда помещена «там, где надо», так что все пользовательские библиотеки надо размещать там же.

Функции типа IIF весьма полезны в тех случаях, когда выбираемое из базы значение зависит от некоторого условия. В определенном смысле даже странно, что они не входят в состав стандартной библиотеки UDF.

Например, в таблице Т поле А может быть положительным или отрицательным.

Пример 6.23

Таблица 6.7. Содержимое полей А в таблице Т

В итоговой таблице нужно просуммировать отдельно положительные и отрицательные значения (типичным примером может служить суммирование прихода и расхода на складах). Такой запрос с помощью нашей функции можно записать непосредственно:

SELECT sum (IIF (A, A, 0)) PLUS,

Sum(IIF(A, 0, -A)) MINUS FROM T '

Таблица 6.8. Результат выборки с помощью функции IIF

PLUS MINUS
24 13

Функция if_i возвращает результат по значению. В этом случае важно точное совпадение типов данных, объявленных для функции в базе и фактически используемых функцией.

Рассмотрим теперь следующую функцию, выполняющую те же действия, но для чисел с плавающей точкой.

Пример 6.24

double *_declspec(dllexport)

if_d(double* m,double* a,double* b)

{ return (*m>0)?a:b; }

Здесь возвращается не значение, а указатель на него, то есть мы имеем случай возврата по ссылке.

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

DECLARE EXTERNAL FUNCTION DIF

DOUBLE PRECISION, DOUBLE PRECISION, DOUBLE PRECISION RETURNS DOUBLE PRECISION ENTRY_POINT "if_d"

MODULE_NAME "MyDLL";

Следует обратить внимание на то, что при обращении к DLL ей передаются указатели и результатом также является указатель. Соответствие типов указателей не проверяется. Последнее означает, что данная функция может быть использована и для получения результатов другого типа. Рассмотрим объявление внешней функции CIF.

Пример 6.25

DECLARE EXTERNAL FUNCTION CIF

DOUBLE PRECISION, VARCHAR(256 ) , VARCHAR(256)

RETURNS VARCHAR(256)

ENTRY_POINT "if_d"

MODULE_NAME "MyDLL";

И сразу же рассмотрим SQL-запрос

SELECT DIF(A, A, 0) PLUS, DIF(A, 0, -A) MINUS,

CIF(A, 'Положител.', 'Отрицат . ' ) Text

FROM T

В конечном счете, получим:

Таблица 6.9. Результат выборки с помощью функций DIF, CIF

PLUS MINUS TEXT
7,000 0,000 Положител.
0,000 6,000 Отрицат.
12,000 0,000 Положител.
0,000 4,000 Отрицат.
5,000 0,000 Положител.
0,000 3,000 Отрицат.

Здесь результатом должна являться символьная строка, хотя функция работает вроде бы с числами с плавающей точкой. На самом деле, плавающая точка тут не причем. Используются только указатели. Так что наша функция if_d не зависит от типов данных, указатель на которые она возвращает. В этом смысле честнее было бы написать ее в виде

void *_declspec(dllexport)

if d(double* m, void * a, void * b)

{■return (*m>0)?a:b;J

Следующие функции иллюстрируют работу со строковыми данными. Функции г_иррег и гиррег обеспечивают перевод алфавитных данных в кодировке WIN 1251 из нижнего регистра в верхний как для латыни, так и для кириллицы. Эта функция может быть полезна в том случае, если кодовая страница для базы не указана (NONE). Пара функций приведена для того, чтобы проиллюстрировать различные методы обработки параметров и возвращаемых величин.

Пример 6.26

char *_declspec(dllexport) r_upper(char *a) {

char *c,t; int i,n=(short) *a; c=(char *)malloc(256); for(i=0;i<n;i++){ ,

t = a[i + 2 ] ;

if(t>='al && t<='z') t=t+'A'-'a'; else if(t>='a' && t<=1 я') t=t+'A,-'Hl;

c[i]=t;

)

c[n]=0; return c;

}

Для формирования выходного параметра необходимо вьщеление области памяти. Статическое выделение при многопрограммной работе не подходит, поскольку в этом случае нельзя гарантировать, что память используемая. одним запросом не будет испорчена другим. Автоматическая память (стековая) не годится для передачи по ссылке, поскольку она будет освобождена сразу же. по выходе из программы. Остается только явное выделение памяти в программе. Для этого используется функция malloc. Последнее существенно потому, что выделенная память должна освобождаться уже в InterBase. Следовательно, механизмы выделения и освобождения памяти должны быть согласованы, а механизм работы malloc■ как раз такой, какой используется в, InterBase.

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

В ряде случаев можно- обойтись ■ без ■ явного выделения памяти. Для этого следует ■ использовать память, занимаемую- входными параметрами.

Пример 6.27

char *_declspec(dllexport) srupper(char *a) {

unsigned char t; int i ;

for(int i=0;a[i];i++){ t=a[i];

if(t>='a' && t<='z') t=t+,A'-'a';

else if(t>=224) t-=32;

a[i]=t;

}

return a;

}

Особенности представленияданныхв InterBase

При работе с данными различных типов необходимо учитывать особенности их представления в InterBase.

Таблица 6.10. Соответствие типов данных в InterBase и С

Тип InterBase> Объявление данных в С (C++)
SMALLINT' shorttiht tp;;
INTEGER; iftt ip;;
FLOAT float fp;;
DOUBLEjPRECISIpNi doMbJejp;;
Тип InterBase Объявление данньх в С(C+ +)
NUMERIC(m, n) / DECIMAL(m, n) если m<5, то short int p; если 5<m<10, то int p;

Для версий до 6

если m>10, то double p

Для версий от 6.0

если 10<m<18, то int_64 p;

значение задается с масштабирующим множителем 10’"

DATE массив из 2 int p[2];

р[0] - количество дней с 17 ноября 1858;

р[1] - количество секунд* 10000 от начала суток

CHARACTER(n) / CHAR(n) char p[n]; (двоичный 0 в конце не предполагается, заполняются пробелами справа до явно указанной длины)
VARYING CHARACTER /

struct / short int pi; char p2[n];

/p;

p.pl - фактическая длина поля;

p.p2 - символьная строка (двоичный 0 в конце не предполагается)

VARCHAR(n)
CSTRING(n) char p[n+l]; (с двоичным 0 в конце в качестве ограничителя)
BLOB см. ниже пример UDF для работы С BLOB

Пример ийРдляработы с BLOB

Данная функция читает содержимое BLOB и записывает его в файл COPYBLOB.ooo. В данном случае используется явный вызов функций API, о котором говорилось в разделе 6.2, для доступа к данным InterBase. Естественно, что аналогичные средства можно применять не только в UDF, но и в любой прикладной программе, хотя в прикладных программах, по-моему, проще использовать тот сервис, который представляется средой разработки.

Пример 6.26 ♦include <stdio.h>

/ / описание управляющей структуры BLOB

struct blobs (

void (*blob_get_segment)(int *,char *,long, long&);

int *blob_handle;

long number_segments;

long max_seglen;

long total_size;

void (*blob_put_segment) () ;

typedef struct blobs* SBLOB;

extern "C" int_declspec(dllexport)

pr_blob(SBLOB Ы ) ;

int_declspec(dllexport) pr_blob(SBLOB bl)

{

long length=100; char *buffer;

FILE *out;

if(!(bl->blob_get_segment)) return 0;

// проверка, что действительно передан BLOB if((out=fopen("COPYBLOB.ooo", "wb"))==NULL) return - 1;

// проверка успешности создания файла buffer=new char[bl->max_seglen+l] ;

// создание буфера для чтения for(int i=0;i<bl->number_segments;i++)

{

H цикл чтения BLOB и записи его в файл (Ы->Ыob_get_segment)< Ы - >Ыob_handle,bu f £er, bl->max_seglen,length); fwrite(buffer, 1, length, out);

}

fclose(out); delete[] buffer; return 1;

}

II цикл чтения BLOB и записи его в файл (bl->blob_get_segment)(bl->blob_handle,buffer, bl->max_seglen,length);

£wri.te(buffer, 1, length,-out) ;

6.2. работа с blob | Введение в InterBase | 6.4. фильтры blob


Введение в InterBase



Новости за месяц

  • Октябрь
    2019
  • Пн
  • Вт
  • Ср
  • Чт
  • Пт
  • Сб
  • Вс