что такое udf firebird

the database experts

UDFs can be incorporated into the database using the IBExpert DB Explorer, IBExpert SQL Editor, or IBExpert Script Executive.

UDF Editor

The IBExpert UDF Editor displays those UDFs inserted into the list, by double-clicking on the UDF name in the DB Explorer, or alternatively using the navigation icons in the editor toolbar to insert single or all UDFs. The grid display can also be filtered or grouped if wished. The grid displays key information, including name, library, entry point, input parameters, returns, return mechanism (drop-down list of options), whether freed (checkbox), and description. Further information is displayed on the Description, Dependencies, DDL, Comparison and To-Do pages.

UDF definitions are database dependent and not server dependent, i.e. they need to be registered for each database individually. Since InterBase ® 6/Firebird, the libraries need to be stored in the Firebird/InterBase ® UDF folder. This is not critical when working with older InterBase ® versions.

It is important to note that the majority of UDFs, when used in a WHERE condition, prevent indices being used during execution.

New to Firebird 2.0: The following is a summary of the major changes, the details of which can be found in the Firebird 2.0.4 Release Notes in the External functions (UDFs) chapter:

An ideal example of a UDF library is RFunc (written in C++) containing over 80 UDFs (although some of these are only applicable for older InterBase ® versions or for different SQL dialects). It is available for both Windows and Linux platforms in English and Russian and can be downloaded free of charge from https://www.ibexpert.com/download/udf/. FreeUDFLib is an example of a UDF library written in Delphi, and can also be downloaded from this link.

For further functions please refer to IBEBlock Functions and the Firebird documentation: Firebird built-in functions.

Drop external function/drop UDF

The DROP EXTERNAL FUNCTION command removes the declaration of the UDF, specified by an additional parameter, from the database.

The dropped function can no longer be reached by the database, as the relevant reference to the UDF library is deleted. However the UDF still exists in the UDF library, so that it can still be used by other databases.

In IBExpert, a UDF can be dropped from the DB Explorer by selecting the UDF to be deleted and using the right-click menu item Drop UDF or [Ctrl + Del].

IBExpert asks for confirmation

before finally dropping.

An exception can be altered by its creator, the SYSDBA user, and any users with operating system root privileges.

RFunc

RFunc is a UDF library containing over 80 UDFs (although some of these are only applicable for older InterBase ® versions or for different SQL dialects). It is available for both Windows and Linux platforms in English and Russian. It can be downloaded free of charge from https://www.ibexpert.com/download/udf/. The most up-to-date version of this library can found at https://rfunc.sourceforge.net/.

It represents a set of user’s (UDF) string, bit, numerical functions, and can also be used for operations with DATEs and TIME and blobs. Also contains PARSER, i.e. calculator of expressions.

InterBase ® 4.2, 5.x, 6.x, 7.0 (Windows 9x, NT, 2000) and InterBase ® 5.x, 6.x, 7.0 (Linux) or Firebird are supported. The library is written in C++ and is delivered with source codes.

RFunc installation

The ZIP-file should be selected (Windows or Linux; English or Russian) and downloaded.

Windows installation

If several versions of InterBase ® servers are installed on one computer, it is necessary to use the RFunc library appropriate to the installed client IB (GDS32.DLL).

It is recommended before starting the Firebird/InterBase ® server to substitute GDS32.DLL appropriate to the version of the server.

Linux installation

IB 5.x:

InterBase ® 6-7 und Firebird (Windows und Linux):

Copy the RFunc file into directory \UDF.

The rfuncx.sql (x = InterBase ® version; use rfunc6.sql for all Firebird versions) script, found in the UDF\sql directory, should then be copied into the IBExpert Script Executive (found in the Tools menu), and executed [F9]. A database connection must exist, as UDF libraries need to be registered for each database (i.e. they are database-dependent and not server-dependent).

It is then necessary to disconnect and reconnect to the database so that the full list of RFunc UDFs can be viewed in the DB Explorer under the DB object branch UDF.

FreeUDFLib

FreeUDFLib is a free UDF library (October 1998) containing many useful UDFs for use with InterBase ® 4.2 and 5.0 under the Win32 platforms (unfortunately no UNIX support with this). It is written entirely in Delphi and all source code is provided.

It can be downloaded free of charge from https://www.ibexpert.com/download/udf/.

Everything in this release is completely free. However, it’s not a PUBLIC DOMAIN. Please refer to the license.txt, included in the ZIP file for more information on licensing.

FreeUDFLib installation

After unzipping FreeUDFLib.zip, copy FreeUDFLib.dll to the Firebird/InterBase ® bin or udf directory, for example: C:\Program Files\InterBase Corp\InterBase\bin, C:\Program Files\Borland\InterBase\udf\bin or C:\Program Files\Firebird\udf\bin.

The ext_funcs.sql script should then be copied into the IBExpert Script Executive (found in the IBExpert Tools menu), and executed using [F9]. A database connection must exist, as UDF libraries need to be registered for each database (i.e. they are database-dependent and not server-dependent). If necessary, use the Script Executive menu item Add CONNECT statement to connect to the desired database, before executing.

It is then necessary to disconnect and reconnect to the database so that the full list of FreeUDF external functions can be viewed in the DB Explorer under the DB object branch UDF.

Источник

Разработка ваших собственных UDF

Разработка ваших собственных UDF

Библиотеки UDF компилируются как стандартные библиотеки совместного использования и выполняются на сервере, где размещена база данных. Библиотеки динамически загружаются базой данных во время выполнения, когда на библиотеку ссылается выражение SQL. Вы можете создавать библиотеки UDF на любой платформе, которая поддерживается в Firebird. Для использования одного и того же набора библиотек UDF в базах данных, выполняющихся на различных платформах, создайте и скомпилируйте отдельные библиотеки для каждой платформы, где располагается база данных.

Библиотека в этом контексте является совместно используемым объектом, который обычно имеет расширение dll на платформе Windows, расширение so для UNIX и Solaris и расширение si на HP-UX. Она может содержать одну или более точек входа для функций, определенных пользователем.

СОВЕТ. Каталог Firebird /examples содержит некоторые старые примеры make- файлов (makefile.be и makefile.msc для систем Windows, makefile для UNIX), которые создают библиотеку ib_udf function из ib_udf.c.

Процесс создания UDF состоит из четырех шагов:

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

2. Скомпилируйте функцию и свяжите с динамически связываемой библиотекой (DLL) или с библиотекой совместно используемых объектов (SO) в соответствии с платформой.

3. Поместите библиотеку и любые требуемые символические ссылки в подходящее место на диске на серверной машине так, чтобы сервер мог получить к ней доступ в каталог по умолчанию /UDF или в альтернативный каталог, который вы сконфигурировали для библиотек внешних функций.

4. Используйте DECLARE EXTERNAL FUNCTION для объявления каждой индивидуальной UDF для каждой базы данных, в которой вам нужно ее использовать.

ПРИМЕЧАНИЕ. Очень хорошей практикой является создание скрипта, содержащего объявления ваших UDF и некоторых комментариев, объясняющих использование.

Читайте также

A.2. Изменение и очистка ваших таблиц

A.2. Изменение и очистка ваших таблиц По мере того как вы продолжите углубляться в исследование iptables, перед вами все актуальнее будет вставать вопрос об удалении отдельных правил из цепочек без необходимости перезагрузки машины. Сейчас я попробую на него ответить. Если вы

Ждем ваших отзывов!

Ждем ваших отзывов! Вы, уважаемый читатель, и есть главный критик и комментатор этой книги. Мы ценим ваше мнение и хотим знать, что было сделано нами правильно, что можно было сделать лучше и что еще вы хотели бы увидеть изданным нами. Нам интересно услышать и любые другие

Читайте также:  что такое абстрактные темы

7.4.2.6. Добавление в LiveCD собственных пакетов

7.4.2.6. Добавление в LiveCD собственных пакетов В предыдущем параграфе мы создали LiveCD с GNOME. Сейчас попробуем добавить в него другие пакеты, например Open Office (популярный офисный пакет) и Evolution (почтовый клиент и органайзер).Добавление пакетов в LiveCD осуществляется с помощью

Проведите интернет-анализ ваших конкурентов

Проведите интернет-анализ ваших конкурентов Что мешает многим предпринимателям продвигать свой сайт? Главная проблема – это конкуренция. В этой главе мы рассмотрим, как быстро и эффективно провести анализ конкурентов в вашей нише в Интернете.Пошаговый алгоритм анализа

Применение собственных моделей с h-параметрами Сравним теперь нахождение коэффициентов усиления по напряжению и по току двумя методами: при использовании встроенной модели PSpice и при применении нашей собственной модели в h-параметрах для усилителя ОЭ. Последний метод

Глава 19. ПОДДЕРЖКА ЦЕЛОСТНОСТИ ВАШИХ ДАННЫХ

Глава 19. ПОДДЕРЖКА ЦЕЛОСТНОСТИ ВАШИХ ДАННЫХ РАНЕЕ В ЭТОЙ КНИГЕ, МЫ УКАЗЫВАЛИ НА ОПРЕДЕЛЕННЫЕ связи которые существуют между некоторыми полями наших типовых таблиц. Поле snum таблицы Заказчиков, например, соответствует полю snum в таблице Продавцов и таблице Порядков. Поле cnum

Переносимость ваших скриптов на bash

Переносимость ваших скриптов на bash При написании ваших собственных скриптов важно делать это так, чтобы они оставались переносимыми. Термин «переносимость» означает, что если ваш скрипт работает под Linux, то он должен работать в другой Unix-системе с малыми изменениями или

Использование своих собственных объектов

Использование своих собственных объектов Объекты, основанные на созданных вами классах, используются аналогично встроенным объектам VBA и объектам вашего приложения.1. Объявите переменную для объекта, например:Dim objCustomThermostat As Thermostat2. Используйте оператор Set, чтобы создать

Обеспечение поддержки подписывания объектов в ваших классах

Обеспечение поддержки подписывания объектов в ваших классах Традиционно при необходимости доступа к объектам, содержащимся в коллекциях — например, массивах и словарях, — программисту требовалось получить доступ к методу в словаре или массиве, чтобы получить или

Создание собственных диалоговых окон

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

Создание собственных шаблонов

Создание собственных шаблонов Теперь сфокусируемся на том, как создать собственный шаблон. Перед началом работы над шаблоном необходимо обдумать некоторые ключевые моменты. Рассмотрим их по

Источник

Книга про разработку приложений для Firebird

Читатели Хабра знают, что я (Денис Симонов) уже некоторое время работаю над серией статей и примеров, которые подробно раскрывают особенности разработки приложений для СУБД Firebird для популярных языков и фреймворков: уже написаны 3 статьи по ADO.NET desktop, ASP.NET MVC и Delphi, в работе для PHP, Java, Android. Также, c 2014 года я являюсь редактором русской документации языку Firebird и членом международной группы документации Firebird.

В процессе работы над примерами я пришел к выводу, что нужно объединять эти статьи и логически дополнять до полноценной книги, посвященной разработке именно под Firebird. Хотя по разработке есть много материалов, и есть русская документация по языку SQL, книга, с последовательным изложением процесса создания приложений и описанием основных ошибок новичков, будет полезна.

Вы спросите – собственно, причем здесь Хабр?

А притом, что я планирую публиковать материалы для основных глав книги на Хабре. Это довольно необычно – сейчас публикуются только переводные главы из зарубежных книг, но я надеюсь, что администрация Хабра не будет против русской книги.

Сейчас Firebird незаслуженно рассматривается как СУБД для маленьких БД, хотя есть примеры промышленной эксплуатации БД до 4 терабайт (в Металинвестбанке, например) и с несколькими тысячами пользователей, поэтому в книгу обязательно будет раздел о преимуществах Firebird перед другими СУБД и поддержке больших данных.

В целом, я планирую довольно традиционный план для книги: основное внимание будет уделено процессу разработки приложений с использование различных технологий, сред разработки и языков программирования. Помимо этого, рассмотрим как устанавливать Firebird на Windows, Linux, MacOS, Android и конфигурировать его.

Небольшая глава будет посвящена основам языка SQL, небольшая – потому что полное руководство по языку SQL вы можете найти здесь. Также будет рассказано об основах администрирования Firebird, средствах трассировки и мониторинга, обеспечении безопасности ваших баз данных. В заключительной части будет рассказано о расширении возможностей самой СУБД Firebird, написании внешних функций (UDF) и новых внешних модулей UDR (внешние процедуры, функции и триггеры), а также других видов плагинов.

В написании книги мне обещали помогать консультациями Дмитрий Еманов (ведущий архитектор Firebird), Роман Симаков (ведущий разработчик РедБазы), а также Алексей Ковязин и Дмитрий Кузьменко из компании iBase.ru (которая также выступает материально-денежным спонсором написания книги).

Если у кого-то есть предложения по содержанию книги, буду рад их учесть.

Источник

Как научиться писать UDF на Delphi за 21 минуту?

Олег Кукарцев, 1996

Как известно, InterBase включает только базовый набор функций для использования в SQL-выражениях. Однако, пугаться этого не стоит – InterBase предоставляет вам мощное средство – механизм User Defined Functions (UDF), позволяющий писать пользовательские функции на любом компилирующем инструменте разработки (хост-языке). При этом, такие функции будут выполняться на сервере, причем в рамках процесса сервера, что повышает скорость их вызова практически до уровня скорости вызова стандартных SQL-функций. Несмотря на то, что последнее время мы все слышим много разговоров о “новой технологии” встраиваемых в серверы баз данных модулях расширения, InterBase был первым сервером, который реализовал концепцию UDF на практке.

В этой статье мы рассмотрим процесс создания UDF для InterBase на платформах Windows NT и Windows 95 с помощью Delphi 3.0/4.0 (подойдет также Delphi 2.0) – как передаются и возвращаются параметры, и некоторые особенности, связанные с каждым типом данных.

Передача и возврат целых чисел, передача параметров

function Add_A(var iSmall: SmallInt; var iLong: Integer): Integer; cdecl; export;

Для того, чтобы параметры передавались в правильном порядке, и выполнялись определенные правила входа/выхода из функции, при ее объявлении всегда используются следующие ключевые слова: cdecl – передача параметров и очистка стека по правилам C; export – для доступа к функции извне DLL.

Наша функция Add_A(var iSmall: SmallInt; var iLong: Integer) имеет два входных параметра iSmall и iLong, которые передаются по ссылке (ключевое слово var). Результат функции имеет тип Integer, и передается по значению.

А вот как аналогичная функция возвращает результат по ссылке:

function Add_B(var iSmall: SmallInt; var iLong: Integer): PInteger; cdecl; export;
// ^^^ ^^^ ^^^^^^^^
// by reference by reference by reference
begin
ResultInteger := iSmall + iLong;
Result := @ResultInteger;
end;

Не правда ли, чуточку посложнее? Для того чтобы вернуть результат по ссылке, нужно объявить глобальную переменную, а результат возвращать в виде ссылки на эту переменную. Чувствую, у опытных программистов к этому моменту должно возникнуть много вопросов. Немного терпения, господа! Я постараюсь ответить на них чуть ниже.

Вернемся к нашей функции Add_B. Для того, чтобы она могла вернуть результат, нам пришлось ввести переменную ResultInteger. Обратите внимание, не локальную переменную для функции Add_B, а именно глобальную. Разница между ними состоит в «продолжительности жизни» этих переменных.

Глобальная переменная размещается в сегменте данных и доступна и после выхода из функции, в то время как значение локальной переменной после выхода из функции не определено. Но что произойдет, если нашу функцию одновременно вызовут два пользователя? (Хотя слово «одновременно» трактуется немного по-разному для одно и многопроцессорных компьютеров, в данном случае это не имеет никакого значения.) InterBase 4.2 и 5.x полностью поддерживает многонитевую архитектуру [multithreaded architecture], т. е. действительно две и более нити (threads) могут в один и тот же момент времени вызвать функцию Add_B! Какой же результат вернет функция? Поскольку сегмент данных у нитей общий, то результат одной нити перезапишет результат другой. А что же делать? Нужно использовать свою переменную для каждой нити. К счастью, выход прост: вместо ключевого слова var нужно использовать threadvar. Тогда каждая нить будет иметь свою переменную ResultInteger и не «испортит» результат другой нити. (к сожалению, реализация threadvar в Delphi 2,3,4 имеет ошибки, приводящие к «падению» dll при работе на многопроцессорном компьютере. Вместо threadvar лучше использовать прямые вызовы WinAPI TLSAlloc и т. п., или стараться совсем не использовать threadvar).

Читайте также:  что значит цепочка в 1xbet

Для того чтобы InterBase смог «увидеть» и использовать наши функции, их необходимо поместить в библиотеку DLL и объявить «экспортируемыми».

Но, прежде чем использовать наши новые функции в SQL, нам еще раз необходимо описать их, но уже на SQL:

declare external function Add_Short_Long
smallint, integer
returns
integer by value
entry_point 'Add_A'
module_name 'UDFDemo';

Рассмотрим каждую строчку поподробнее:

declare external function Add_Short_Long

Под именем Add_Short_Long функция будет известна в SQL, оно может не совпадать с тем, под которым мы экспортировали ее из DLL.

Входные параметры (всегда передаются по ссылке)

Результат, может передаваться как по ссылке, так и по значению, по умолчанию принимается вариант передачи по ссылке, поэтому мы явно указываем by value.

entry_point 'Add_A'
module_name 'UDFDemo';

А дальше все просто – Add_A это имя, под которым функция была экспортирована из DLL, и UDFDemo – имя этой DLL.

Одна «тонкость» – имя функции чувствительно к регистру, т. е. Add_A и ADD_A это разные имена. А имя DLL – нет, т. е. UDFDemo и UDFDEMO это одно и тоже имя.

Еще одно объявление функции:

declare external function Add_Short_Long2
smallint, integer
returns
integer
entry_point 'Add_B'
module_name 'UDFDemo';

Если вы уже нашли все отличия этого объявления от предыдущего примера, то можно считать, что вы уже полностью освоили технику программировани UDF! Если нет, то давайте продолжим.

Вот в чем состоят отличия: изменилось имя функции, под которым она будет известна в SQL, т. к. нельзя иметь две функции с одинаковыми именами:

declare external function Add_Short_Long2

Из описания результата исчезло by value и изменилось имя Add_B т. е. мы экспортируем из DLL уже другую функцию. А что мешает нам экспортировать одну и ту же функцию под разными именами? В принципе, ничего, кроме, наверное, здравого смысла.

Итак, входные параметры всегда передаются по ссылке, а результат может возвращаться как по ссылке, так и по значению. А что будет, если изменить значение входного параметра, ведь он передается по ссылке? Изменится ли соответствующая переменная в процедуре, из которой вызвана наша UDF? А если в качестве параметров используются не локальные переменные сохраненной процедуры SQL, а значения полей из таблицы, например,

select AddA(S, 5), AddB(5, I)
from TestUDF
where AddA(S, I) = 110;

где S и I – поля таблицы TestUDF, соответственно SmallInt и Integer.

Для ответа на этот вопрос нужно чуть подробнее остановиться на механизме передачи параметров в процедуру. Перед тем, как выполнить UDF, InterBase создает копии входных переменных в памяти, и передает указатели именно на эти копии. Таким образом, вы можете совершенно спокойно модифицировать значения входных параметров, на IB это не произведет никакого впечатления! Зачем останавливаться на таких мелочах, спросите вы? Я отвечу вам конкретными примерами. Есть несколько «нестандартных» возможностей использовать такой способ передачи параметров.

В последнем примере для возврата результата по ссылке нам нужна была «глобальная» переменная, причем уникальная для каждой нити, вызывающей UDF. А ведь на роль такой переменной вполне подходит входной параметр соответствующего типа:

function Add_B2(var iSmall: SmallInt; var iLong: Integer): PInteger; cdecl; export;
// ^^^ ^^^ ^^^^^^^^
// by reference by reference by reference
begin
iLong := iSmall + iLong;
Result := @iLong;
end;

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

В вышеприведенных примерах мы использовали переменные типа integer, smallint – эти типы полностью совпадают в IB и Delphi.
А вот таблица соответствия остальных типов данных:

И для удобства можно объявить несколько «указательных» типов:

PSmallInt = ^SmallInt;
PInteger = ^Integer;
PShort = ^Short;
PLong = ^Long;
PFloat = ^Float;
PDouble = ^Double;

Числа с плавающей запятой

Строки

Строки могут быть представлены в таблице InterBase двумя типами: Char(N) и VarChar(N). Внутренне представление (хранение) этих типов совершенно идентично, разница только в том, как их видит «клиент». Тип Char(N) всегда дополняется пробелами до длины N, в тоже время дл типа VarChar(N) возвращается только то, что было записано, и без всяких дополнений (поэтому при передаче в UDF значений типа CHAR не забывайте, что внутрь UDF строка попадет дополненная пробелами).

В UDF те же самые строки могут быть представлены тремя типами: CString, VarChar и Char.

CString – это строка символов оканчивающаяся нулевым символом, т. е. типичная строка языка С (или Delphi PChar), что и видно из названия.

И последний тип, Char(N) – похож на массив символов (Array[0..N-1] of Char). Этот тип также не обязательно заканчивается нулевым символом.

Давайте рассмотрим функцию:

declare external function Test
cstring(5), varchar(5), char(5)

При вызове Test(‘Aaa’, ‘Bbb’, ‘Ccc’) параметры будут выглядеть следующим образом:

т. к. первый параметр (cstring) был объявлен с длинной 5, минимум 6 байт будет выделено для хранения этого параметра, т. е. 5 байт + 1 концевой ноль (+ 0 или 2 для выравнивания на границу слова или двойного слова, но нас сейчас это не интересует).
#? – это один байт или символ, значение которого не определено,

для второго параметра (varchar) выделяется 5 байт + 2 байта длины, без учета выравнивания,

для третьего параметра (char) выделяется ровно 5 байт, также без учета выравнивания. Т. е. типы CString, VarChar «несут в себе» информацию и длине переданного параметра, а тип Char всегда дополняется проделами до объявленной длины.

Поясним вышесказанное несколькими примерами:

Эта функция имеет не два входных параметра, а всего один (и даже может быть процедурой, а не функцией). Но у нее есть также и один выходной параметр. Перед вызовом InterBase резервирует место под оба, а помещает входное значение лишь в первый параметр. В UDF передаются указатели на оба параметра, а после выполнения UDF InterBase «считает», что результат помещен во второй параметр.

Лучше, как всегда, обратиться к примеру:

В данном случае мы используем не function, а procedure. У нас два параметра, один указывает на входное значение, а в другой мы должны поместить результат. Технология ничем не отличается от используемой в примере SubString, но т. к. входной и выходной параметры имеют разный тип (вернее разный размер, integer и varchar(10) требуют разного «количества» памяти), мы «просим» InterBase, чтобы он сам выделил нам место для результата.

InterBase спокойно «проглотит» функцию типа:

но результат вас «разочарует».

Вовсе необязательно делать «выходной» параметр последним, вы можете «поставить его на любое место», главное, чтобы самим потом не запутаться, где «вход», а где «выход» у данной функции. Большей проблемой является то, что IB не хранит в метаданных информацию о том, какой номер параметра был взят в качестве возвращаемого значения. Например, при декларации функции HexStr как returns parameter 2, обратно (извлечением DDL или просмотром текста в Database Explorer) мы получим следующее объявление функции:

declare external function
integer
returns
varchar(10)

Действительно, функция именно так и работает, т. е. вызывается в коде хранимых процедур, триггеров или SQL как HexStr(x). И такое объявление фукнции будет правильным в соответствии с порядком передаваемых параметров в стеке. Но лучше всего оригинальные объявления таких функций сохранять в отдельном скрипте.

Кстати, немного об исключениях (exception) – лучше их обрабатывать самим, не передавая Interbase, а в результат функции передайте специальное значение, по которому вы сможете определить, что «что-то не так».

Все вышесказанное работает для InterBase 4.2 Начиная с версии 5.0, появился еще один способ передачи параметров:

// в документации InterBase 5.0 Language Reference для написания UDF с free_it на
// Delphi
// упоминается несуществующая функция SysAlloc. Вместо нее нужно использовать malloc,
// объявленную ниже, как для Delphi так и для Borland C++Builder.
// Т.е. для аллокирования памяти должен использоваться менеджер MSVC.

function malloc(Size: Integer): PChar; cdecl; external 'msvcrt.dll';

function HexValue2(var iLong: Integer): PChar; cdecl; export;
var
HexStr: ShortString;
begin
HexStr := IntToHex(iLong, 8);
Result := malloc(Length(HexStr) + 1);
StrPCopy(Result, HexStr);
end;

То есть я сам распределяю памяти столько, чтобы поместился мой текущий результат, а «освободит» эту память InterBase. Преимущества этого подхода очевидны (для тех кто любит экономить память), если UFD возвращает один раз из тысячи строку длинной 32 кБ, а в остальных случаях по несколько байт, то при старом подходе мы должны были бы объявить:

Читайте также:  что такое автокредит тинькофф

и перед каждым вызовом InterBase должен был бы распределять 32кБ, не зная, какой объем памяти потребуется на самом деле.

Пользуясь новым подходом, можно значительно сэкономить «память», особенно «в масштабах всей страны»! На самом, деле перерасход памяти действительно возможен, если много пользователей вызовут одновременно наш UDF, и вычисления в нем окажутся достаточно длительными по времени.

На что нужно обратить внимание? Если изменить описание функции на

Т. е. наш UDF возвращает как минимум 8 байтовую строку, а мы объявили как cstring(3). В этом случае результат будет «усекаться» до. 2 символов, т.к. последний символ в cstring обязан содержать ноль. Так что при объявлении UDF экономить не стоит.

А почему мы «распределяли» память таким способом, не проще ли было через GetMem?

К сожалению, нет. Функции GetMem и FreeMem работают с менеджером памяти Deplhi, который является «надстройкой» над менеджером памяти Win32. Поэтому, поручая Interbase»освободить» память, мы должны распределить ее «заранее оговоренным» способом, т. е. при помощи стандартной функции malloc.

Дата и время

Для хранения даты и времени в Interbase существует тип date.

Внутреннее представление таково – э то запись из двух 32 разрядных знаковых целых чисел.

В первом числе хранится число дней, прошедших с 17 ноября 1858, а во втором – число время в десятых долях миллисекунды, прошедшее после полуночи

PIBDateTime = ^TIBDateTime;
TIBDateTime = record
Days, // Date: Days since 17 November 1858
MSec10: Integer; // Time: Millisecond * 10 since midnigth
end;

В Delphi дата и время представлено типом TDateTime, который объявлен как TDateTime = type Double;

Таким образом, совсем не сложно преобразовать даты из формата InterBase в Delphi и наоборот.

const // константы трансляции даты:
MSecsPerDay10 = MSecsPerDay * 10; // миллисекунд в сутках * 10
IBDateDelta = 15018; // разница в днях между датами Delphi 2.0 и InterBase

StrPCopy(CString, FormatDateTime('"Date is" dddd, dd mmmm yyyy "Time is" h:mm:ss', DateTime));
end;

UDF не может вернуть дату «по значению», также мне не удалось воспользоваться способом ‘returns parameter N’.

Соответственно, остаются два способа: первый – использовать переменную типа threadvar; второй – ввести дополнительный входной параметр и в нем передать результат. Таким образом, совсем не сложно преобразовать даты из формата InterBase в Delphi и наоборот.

При этом трюк с возвратом адреса входного параметра в качестве выходного позволяет отказаться от threadvar. А получать значение из этой функции можно следующим способом:

select ServerDate('01.01.0001') from rdb$database.

BLOb (Binary Large Object) – этот тип данных предназначен для хранения произвольных данных (текст, картинки, звук и т. п.). Размер хранимых данных в одном поле BLOb может быть максимально от 64Мб до 32Гб (зависит от размера страницы БД).

Основное применение UDF для BLOb, это быстрая загрузка и выгрузка данных, а также всевозможные операции поиска. BLOb хранятся, читаются и записываютс по сегментам, соответственно, каждый конкретный BLOb имеет такие характеристики, как число сегментов, максимальная длина сегмента, и суммарная длина BLOb. Для доступа к BLOb определена специальная структура (отдаленно напоминает тип File в Delphi), в которой описаны все характеристики, а также есть функции чтения, записи и позиционирования.

TBLOb = record
GetSegment: function(Handle: Pointer; Buffer: PChar;
MaxLength: Long; var ReadLength: Long): WordBool; cdecl;
Handle: Pointer; // BLOb handle
SegCount, // Number of BLOb segments
MaxSegLength, // Max length of BLOb segment
TotalLength: Long; // Total BLOb length
PutSegment: procedure(Handle: Pointer; Buffer: PChar;
Length: Long); cdecl;
// Seek: function: Long; cdecl; // Я не знаю входные параметры.
end;

Надеюсь, что эта тема целиком будет ясна из примеров. Здесь также встречаются примеры отладки UDF,

их мы рассмотрим несколько позже.

// Размер буфера для чтения BLOb
const
MaxBufSize = 32768;

function SearchSample(Buf, Sample: PChar): Boolean;
begin
Result := StrPos(Buf, Sample) <> nil;
end;

function FillBuffer(var BLOb: TBLOb; Buf: PChar; FreeBufLen: Integer;
var ReadLen: Integer): Boolean;
var
EndOfBLOb: Boolean;
FreeBufLenX, GotLength: Long;
begin
try
ReadLen := 0;
repeat
GotLength := 0;

if FreeBufLen > MaxBLObPutLength then FreeBufLenX := MaxBLObPutLength
else FreeBufLenX := FreeBufLen;

with BLOb do
EndOfBLOb := not GetSegment(Handle, Buf + ReadLen, FreeBufLenX, GotLength);

Inc(ReadLen, GotLength);
Dec(FreeBufLen, GotLength);
until EndOfBLOb or (FreeBufLen = 0);
except
on E: Exception do
begin

Writeln(X, E.Message);
Writeln(X, ReadLen, ' ', FreeBufLen, ' ', GotLength, ' ', EndOfBLOb);
Flush(X);

EndOfBLOb := True;
end;
end;
Buf[ReadLen] := #0;
Result := EndOfBLOb;
end;

const
MaxVarCharLength = 32767; // Max InterBase Char and VarChar length

procedure BLObToCString(var BLOb: TBLOb; CString: PChar); cdecl; export;
var
ReadLength: Integer;
begin
try
CString[0] := #0;
with BLOb do
if (not Assigned(Handle)) or (TotalLength = 0) then Exit;

while CStringLength > 0 do
begin
if CStringLength > MaxBLObPutLength then PutLength := MaxBLObPutLength
else PutLength := CStringLength;

with BLOb do
PutSegment(Handle, CString, PutLength);

Dec(CStringLength, PutLength);
Inc(CString, PutLength);
end;

except

on E: Exception do
begin
Writeln(X, 'Exception in CStringToBLOb. ');
Writeln(X, '>', CString, '

if StreamSize > MaxBufSize then BufSize := MaxBufSize else BufSize := StreamSize;
GetMem(Buffer, BufSize);
try
while StreamSize <> 0 do
begin
if StreamSize > BufSize then ReadLength := BufSize else ReadLength := StreamSize;
Stream.ReadBuffer(Buffer^, ReadLength);

with BLOb do
PutSegment(Handle, Buffer, ReadLength);

Значения null

К сожалению, механизмы определения значений null во входных параметрах UDF в настоящее время отсутствуют. При передаче параметров в UDF это можно легко обойти, передавая по два параметра, вместо одного, первый – собственно параметр, а второй индикатор, по которому можно определить, не является ли первый параметр значением null. Для того чтобы вернуть null из UDF, необходимо сделать две разные функции – первая вернет значение, а вторая – null-индикатор. В данном случае нельзя обойтись одним вызовом, т. к. UDF может вернуть только одно значение.

Я не привожу пример реализации UDF на Delphi, т. к. здесь "вся соль" именно в SQL.

create procedure IsNull(I Integer)
returns (V VarChar(10))
as
declare variable Ix smallint;
begin
/* Мы должны написать что-то типа:
if (I is null) then V = 'null';
else V = IntToHex(I);

Но мы хотим, чтобы именно UDF знала, что передаваемый параметр равен null! */

if (I is null) then Ix = 1;
else Ix = 0;
V = TestNullValue(I, Ix);

create procedure TestIsNull
returns (V VarChar(10))
as
declare variable I Integer;
begin
execute procedure IsNull(100) returning_values V;
suspend;

I = null;
execute procedure IsNull(I) returning_values V;
suspend;
end^

select * from TestIsNull;

Отладка

Вы можете также использовать этот метод для отслеживания логики вызовов процедур SQL и срабатывания триггеров. Надеюсь, что со временем в IB появится "фирменный" отладчик, а пока. посмотрите пример:

// Debugging usage examples:
var
Indent: SmallInt = 0; // Made it threadvar and init properly

function CheckPoint(CString: PChar): Integer; cdecl; export;
begin

if StrPos(CString, 'Exit from ') <> nil then Dec(Indent, 2);
if Indent nil then Inc(Indent, 2);
if Indent > 128 then Indent := 128;
Writeln(X, CString);
Flush(X);

Result := 0;
end;

create procedure XXX()
returns (. )
as
declare variable Dummy integer;
begin
Dummy = CheckPoint('Enter to procedure " XXX "');
.
Dummy = CheckPoint('LastPrintedDate ' || cast(LastPrintedDate as varchar(10)));
.
Dummy = CheckPoint('Exit from procedure " XXX "');
end^

Пока интересующие нас процедуры SQL и триггеры обрамляются блоками Enter/Exit, а в теле можно просто распечатать значения переменных и параметров. Печать из UDF попадает на эту же консоль.

Все недостающие тексты UDF и процедуры SQL, а также модули необходимые для отладки вы сможете найти в UDF Starter Kit.

На этом, наверное, можно было бы и закончить, последние "традиционные" несколько слов. Данная статья написана на основе UDF Starter Kit. Это совсем маленькое "руководство для начинающих" по написанию и использованию UDF, написанное автором во время работы в фирме Epsylon Technologies.

Примечание kdv по отладке UDF в Delphi

Лучше всего сразу оформить новый модуль (new unit) в Delphi, и все функции располагать в нем. Проверить работу функции проще всего в дельфийском приложении, состоящем из полей ввода (входные значения UDF) и кнопки, котора будет вызывать нужную функцию. После того, как вы убедились, что функция работает, сделайте DLL, и поместите ее в каталог UDF для IB 6 и выше, или для предыдущих версий в Interbase\Bin, InterBase\Lib (для 5.0),WinNT\System32 или Windows\System. (В любой каталог Path!).

Источник

Строительный портал