Sources
Delphi Russian Knowledge Base
DRKB - это самая большая и удобная в использовании база знаний по Дельфи в рунете, составленная Виталием Невзоровым

InterBase для программиста

01.01.2007

InterBase для программиста

коряво перевел Хаос

Работа с базой данных

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

Следующая таблица показывает API для работы с БД. Функции напечатаны в порядке их обычного появления в приложении.

isc_expand_dpb()

Определяет дополнительные параметры для доступа к БД, типа имени пользователя и пароля, использует предварительно объявленный и заполненный DPB

isc_attach_database()

Соединяется с БД и инициализирует параметры для доступа к БД, типа числа буферов кэша для использования; использует объявленный и заполненный DPB

isc_database_info()

Запрашивает информацию о подключенной БД, типа версии дисковой структуры(ODS) и др.

isc_detach_database()

Отключает от подключенной БД и освобождает системные ресурсы связанные с подключением

isc_drop_database()

Удаляет БД и все поддерживаемые файлы, типа теневого файла

Соединение с БД

Подключение к одной или нескольким БД состоит из четырех шагов:

1.Создание и инициализация  дескриптора БД для каждой подключаемой БД
2.Создание и заполнение DPB(буфер параметров БД) для каждой подключаемой БД
3.Вызов isc_expand_dpb() до подключения к БД для предварительного создания и заполнения DPB (если лень заполнять его руками).
4. Вызов isc_attach_database() для каждой БД к которой будете подключаться

Рассмотрим все это детально по шагам.

1. Создание дескриптора БД

Каждая БД, которая используется приложением должна быть связана с некоторым уникальным целым числом -  дескриптором БД, это число идентификатор БД, его возвращает при подключении к БД функция isc_attach_database(). Заголовочный файл  ibase.h содержит следующее объявление дескриптора БД:

typedef void ISC_FAR *isc_db_handle;

используйте этот тип для объявления дескриптора БД.

Пример, обьявление дескриптора БД:

#include <ibase.h>

. . .

isc_db_handle db1;

isc_db_handle db2;

/*

дескриптор должен быть установлен в 0 перед использованием,  иначе

isc_attach_database() вернет код ошибки

*/

. . .

db1 = 0L;

db2 = 0L;

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

2. Создание и заполнение DPB

DPB это обычный char массив объявленный в приложении, который следующую структуру:

1.Первый байт, определяет версию буфера параметров,   и равен определенной в ibase.h константе isc_dpb_version1.
2.Далее идет длинный ряд состоящий из одного или нескольких  кластеров байт. Каждый  кластер описывает отдельный параметр.

Кластер состоит в свою очередь из нескольких частей:

1.Первый байт кластера определяет параметр который будет описываться далее в кластере. Этот байт равен одной из констант – идентификаторов параметров объявленных в ibase.h  (например, константа isc_dpb_num_buffers говорит, что после нее последует определение числа буферов кэша БД).
2.Второй байт кластера содержит число, определяющее размер параметра в байтах.
3.Далее следует сам параметр который может занимать несколько байт. Число этих байт и указывает предыдущий байт. (это может быть, например, число или строка символов, \0 не считается для строки символов и не входит в строковый параметр).

К примеру, следующий код создает DPB c одним параметром, который описывает число буферов кэша, используемых при соединении с БД.

char dpb_buffer[256], *dpb,;

short dpb_length;

/* Создание буфера параметров базы данных. */

dpb = dpb_buffer;/*длина буфера*/

dpb++ = isc_dpb_version1; /* версия буфера параметров БД */

dpb++ = isc_num_buffers; /*говорит что далее пойдет описание параметра числа буферов*/

dpb++ = 1; /* в кластере остается 1 байт */

dpb++ = 90; /*  этот байт содержит число 90 */

dpb_length = dpb - dpb_buffer;/*длина буфера */

Обратите внимание: Все числа в буфере параметров базы данных должны быть представлены в универсальном формате, сначала идет самый младший байт, потом старшие по возрастанию. Числа со знаком должны иметь признак знака в последнем байте (старшем). Можно использовать функцию isc_vax_integer () которая меняет порядок байт на обратный (см. Приложение) .

Следующая таблица содержит константы идентификаторов параметров, которые можно передать в  DPB для получения каких – то специфических характеристик соединения с БД

Идентификация пользователя

Имя пользователя                                                                 isc_dpb_user_name

Пароль                                                                         isc_dpb_password

Зашифрованный пароль                                                         isc_dpb_password_enc

Название роли                                                                    isc_dpb_sql_role_name

Имя администратора системной БД                                          isc_dpb_sys_user_name

Авторизированный ключ для лицензии                                        isc_dpb_license

Зашифрованный ключ БД                                                        isc_dpb_encrypt_key

Управление средой

Число буферов кэша                                                        isc_dpb_num_buffers

dbkey context scope                                                                 isc_dpb_dbkey_scope

Управление системой

Синхронная  или асинхронная запись в БД                                 isc_dpb_force_write

(т.е. писать на диск сразу, или покоптить в памяти)

Определяет резервировать или нет небольшое место 

на каждой странице БД для хранения старых версий

модифицированных записей когда модификации уже сделаны        isc_dpb_no_reserve

Определяет, должна БД быть помечена как поврежденная или нет        isc_dpb_damaged

Выполнять последовательно проверку внутренних структур                isc_dpb_verify

Управление теневым файлом БД

Активизировать теневой файл БД, который необязателен, но является

синхро-копией БД                                                        isc_dpb_activate_shadow

Удалять теневой файл БД                                                isc_dpb_delete_shadow

Управление системой регистрациии

Активизировать управление системой регистрации, для слежения

за всеми запросами к БД                                                        isc_dpb_begin_log

Деактивировать систему регистрации                                        isc_dpb_quit_log

Спецификация файла сообщений и символьной кодировки

Языковая спецификация файла сообщений                                isc_dpb_lc_messages

Используемая кодировка                                                        isc_dpb_lc_ctype

Следующая таблица показывает DPB параметры в алфавитном порядке.

Параметр

Назначение

Длина

Значение

isc_dpb_activate_shadow

Директива активации теневого файла БД, синхрокопии БД,  которая необязательна.

1(Игнорируется)

0(Игнорируется)

isc_dpb_damaged

Число определяющее помечать или нет БД как поврежденную

1 = помечать как поврежденную

0 = непомечать

1

0 или 1

isc_dpb_dbkey_scope

Scope of dbkey context. 0 limits scope to the current1 transaction, 1 extends scope to the database session

1

0 или 1

isc_dpb_delete_shadow

Указание удалить теневой файл БД который больше не нужен

1(Игнорируется)

0(Игнорируется)

isc_dpb_encrypt_key

Строка шифрования ключа, до 255 байт

Число байт в строке

Строка содержащая ключ

isc_dpb_force_write

Определяет синхронная или асинхронная запись будет в БД

0 = асинхронная

1 =синхронная

1

0 или 1

isc_dpb_lc_ctype

Строка опрделяющая набор символов для использования

Число байт в строке

Строка содержит название набора символов

isc_dpb_lc_messages

Строка определяющая язык файла сообщений

Число байт в строке

Строка содержащая название файла сообщеней

isc_dpb_license

Строка содержащая ключ к программной лицензии

Число байт в строке

Строка содержащая ключ

isc_dpb_no_reserve

Определяет резервировать или нет немного места на каждой странице БД для хранеия устаревших версий записей которые были модифицированы. keep backup versions on the same page as the primary record to optimize update activity

0(по умолчанию) = резервировать

1= не резервировать

1

0 или 1

isc_dpb_num_buffers

Число буферов кэша выделенных для использования с БД;

По умолчанию 75

1

Число выделенных буферов

isc_dpb_password

Строка пароля, до 255 символов

Число байт в строке

Строка содержащая пароль

isc_dpb_password_enc

Строка шифрованного пароля, до 255 символов

Число байт в строке

Строка содержащая пароль

isc_dpb_sys_user_name

Строка системного DBA, до 255 символов

Число байт в строке

Строка содержащая имя SYSDBA

isc_dpb_user_name

Строка имени пользователя, до 255 символов

Число байт в строке

Строка содержащая имя пользователя

3. Добавление параметров к DPB(раздел не отработан)

Иногда требуется добавить параметры в уже существующий DPB во время выполнения. К примеру, когда приложение запущено, и нужно определить имя пользователя и пароль и внести эти значения динамически. Функция isc_expand_dpb() может быть использована для передачи дополнительных параметров в созданный DPB  во isc_dpb_lc_ctypeвремя выполнения. Это параметры isc_dpb_user_name, isc_dpb_password, isc_dpb_lc_messages. Но isc_expand_dpb() автоматически не освобождает выделенное место в памяти при отключении от БД. Функция требует следующих параметров:

Параметр                тип                        Описание

dpb                        char**                        Указатель на DPB

dpb_size                unsigned short*        Указатель на текущий размер DPB в байтах

…                        char*        Указатели на тип элемента и элемент добавляемый к DPB

Третий параметр в таблице, « . . .», показывает переменное число вводимых параметров, с различными именами,  но каждый из которых указывает на char.

Следующий код демонстрирует как вызываемая функция isc_expand_dpb() добавляет имя пользователя и пароль  DPB после того как пользователь вводит их во время выполнения

char dpb_buffer[256], *dpb, *p;
char uname[256], upass[256];
short dpb_length;
/* Создание параметров буфера DPB. */
dpb = dpb_buffer;
*dpb++ = isc_dpb_version1;
*dpb++ = isc_dpb_num_buffers;
*dpb++ = 1;
*dpb++ = 90;
dpb_length = dpb - dpb_buffer;
/* Спрашиваем пользователя о имени и пароле */
prompt_user("Enter your user name: ");
gets(uname);
prompt_user("\nEnter your password: ");
gets(upass);
/* Добавляем введенное к DPB. */
dpb = dbp_buffer;
isc_expand_dpb(&dpb, &dpb_length,
isc_dpb_user_name, uname,
isc_dpb_password, upass,
NULL);

4. Подключение к БД

После создания и инициализации дескриптора БД, и небязательных установок в DPB, определяющих параметры соединения, используется функция  isc_attach_database() для подключения к существующей БД. Помимо распределения ресурсов системы для соединения с БД эта функцияя связывает определенную БД с дескриптором БД для последующего его использования в других API вызовах. 

isc_attach_database() требует 6 параметров       

·Указатель на вектор ошибки, откуда можно узнать ошибках если они были.
·Длина в байтах имени БД для открытия БД. Если имя БД включает имя и путь, указывается их общая длина.
·Строка содержащая имя БД для подключения. Имя может включать имя БД и путь к ней.
·Указатель на дескриптор БД .
·Длина в байтах DPB. Если DPB остался по умолчанию то, устанавливается в 0.
·Указателъ на DPB. Если DPB остался по умолчанию то, устанавливается в NULL.

Каждая БД требует отдельного вызова isc_attach_database().

Следующий код показывает подключение к демо базе xsample.gdb.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ibase.h>
 
isc_db_handle db;
ISC_STATUS status_vector[20];
short dpb_buf_len=20;
char dpb_buf[]={
                                isc_dpb_version1,                //версия буфера 
                                isc_dpb_user_name,                //начинается кластер параметра имя пользователя
                                6,                                 //длина этого параметра 6 байт
                                'S','Y','S','D','B','A',                //строка имени пользователя
                                isc_dpb_password,                //начинается кластер пароля пользователя
                                9,                                 //длина его 9 байт
                                'm','a','s','t','e','r','k','e','y'        //сам пароль
                };
 
int main()
{
 char str[]="c:\\Doc\\Ibgpre\ibmake\\xsample.gdb";
 
 isc_attach_database(status_vector, strlen(str), str, &db,dpb_buf_len,dpb_buf);
 if (status_vector[0] == 1 && status_vector[1])
 {
   isc_print_status(status_vector);        //печать информации по ошибке
 }
 if(db)
  isc_detach_database(status_vector, &db);
 
 return 0;
}

Запрос информации о подключении

После подключения к БД можно узнать информацию о соединении. Вызов функции isc_database_info() возвращает информацию о соединении, а также о версии дисковой структуры (ODS) используемой соединением, о числе выделенных буферов кэша, о числе страниц БД читаемых или записываемых, и т.д.

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

Приложение должно создать буфер результата достаточного размера, куда IB вернет информацию, и передать функции указатель на буфер и его размер в байтах. Если IB возвращает больше информации, чем буфер может принять, то в последний байт буфера пишется число соответствующее константе  isc_info_truncated.   

Элементы буфера запроса и значения буфера результата

Буфер запроса это символьный массив в каждый элемент которого заносятся значения определяющие какая информация будет возвращена. Каждый байт это параметр, информацию о котором мы хотим получить. Константы связанные с каждым таким параметром  определены в ibase.h.

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

1.Первый байт определяет информация о каком параметре содержится в этом кластере, он равен одной из констант определенных в ibase.h и уже описанных выше.
2.Далее идут два байта – содержащие число равное числу байт оставшихся до конца кластера, фактически размер значения параметра в байтах.
3.И наконец идет само значение занимающее столько байт сколько определено предыдущими двумя байтами. (это может быть число или строка )
 

Пример:

Вот так выглядит буфер результата после запроса  информации  о размере страницы БД

 

isc_info_page_size

Число байт до конца кластера (4 байта)

Число 1024

Символ конца буфера

1

2

3

4

5

6

7

8

0х0E

0x04

0x00

0x00

0x04

0x00

0x00

0x01

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

Характеристики БД

Следующая таблица перечисляет параметры БД информацию о которых можно получить вызвав  isc_database_info() , при добавлении в буфер запроса к параметрам добавляется префикс isc_info_

Элементы буфера запроса                        Содержание буфера результата

allocation                                        Число страниц зарезервированных БД

base_level                                        Версия БД        

- 1 байт содержит число 1

                                                                      - 1 байт содержит номер версии

db_idИмя файла базы данных и название местоположения
• 1 байт, содержит число 2 для локального подключения или 4 для удаленного подключения

• 1 байт, содержит длину  d, имени файла базы данных в байтах

• Строка d байтов, содержит имя файла базы данных

• 1 байт, содержит длину l, названия местоположения  в байтах

• строка l байтов, содержит название местопоожения

implementation                                Число выполнения базы данных:

• 1 байт, содержит 1

• 1 байт, содержит номер выполнения

• 1 байт, содержащий номер “класса”, или 1 или   12

no_reserve                                        0 или 1

• 0 указывает, что часть места зарезервировано на каждой странице базы данных для хранения резервных версий изменяемых записей [По умолчанию]

• 1 указывает, что не надо резервировать место для таких записей

ods_minor_versionМладший номер версии дисковой структуры (ODS); увеличение в младшем номере версии указывает на не-структурное изменение, оно все еще позволяет базе данных быть доступной ядрам  баз данных с тем же самым главным номером версии, но с различными младшими номерами версии ODS
ods_versionСтарший номер версии ODS ; базы данных с различными старшими номерами версий имеют различные физические  структуры,  ядро базы данных может обращаться только к базам данных с одинаковым старшим номером версии ODS; попытка присоединяться к базе данных с различными старшими ODS номерами приводит к ошибке
page_sizeРазмер страницы в  байтах в подключенной базе данных; используйте с isc_info_allocation, чтобы определить размер базы данных
version                                                Строка идентификации версии данной базы данных:?
• 1 байт, содержит число 1

• 1 байт, определяет длину n, следующей строки

• n байт, содержат строку идентификации версии

 

Параметры среды

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

Запрос буферного элемента                        Содержание буфера результата

current_memoryРазмер памяти  (в байтах) используемый сервером в настоящее время:
forced_writesЧисло, определяющее режим записи  данных на диск (0 для асинхронного, 1 для синхронного)
max_memoryМаксимальный объем памяти (в байтах) используемый сервером начиная с первого процесса подключенного базе данных
num_buffersЧисло буферов памяти выделенной в данный момент времени (в страницах)
sweep_intervalЧисло транзакций, которые завершились подтверждением между “sweeps”(чистками мусора) , удаляющего версии записей базы данных которые больше не нужны
user_namesИмена всех пользователей в настоящее время подключенных к базе данных; для каждого такого пользователя, буфер результатов содержит байт isc_info_user_names, следующий байт  определяет число байтов приходящихся на имя пользователя, и далее следует имя пользователя

Обратите внимание: Не все параметры среды  доступны на всех платформах.

Статистика работы сервера БД.

Существуют четыре параметра, которые характеризуют статистику работы базы данных. Эта статистика накапливается для базы данных с момента, первого подключения к БД любым процессом, и продолжается до тех пор пока последний  процесс не будет отключен от базы данных.

Например, значение возвращенное для isc_info_reads - это число чтений с момента первого подключения к данной базе данных, то есть он содержит все чтения выполняемые всеми подключенными процессами, а не число чтений выполненое вызвавшей  isc_database_info() программой, подключенной к данной базе данных.

Следующая таблица  содержит параметры статистики работы БД:

 

Элемент буфера запроса                                   Содержание буфера результата

fetches                                                                   Число чтений из памяти буфера кэша

marks                                                                   Число записей в память буфера кэша

reads                                                                   Число читаемых страниц

writes                                                                   Число записываемых страниц

Счетчики операций базы данных

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

Когда любой из этих информационных параметров затребован, IB возвращает буфер результата имеющий вид:

·1 байт определяет что за параметр возвратился (например, isc_info_insert_count).
·2 байта , сообщающие, сколько байт составляют следующая пара значений.
·Пара значений для каждой таблицы в базе данных, в которой произошел требуемый тип операции начиная с момента последнего подключения к БД.
Каждая пара состоит из:

·2 байта определяют ID таблицы
·4 байта показывают число операций (к примеру, вставки) произведенных над таблицей

Совет: чтобы определить настоящее имя таблицы из ID таблицы  выполните запрос к системной таблице, RDB$RELATION.

Следующая таблица описывает параметры, которые возвращают значения счетчиков операций над базой данных:

Элемент буфера запроса                                Содержание буфера результата

backout_count                                Число удалений версии записи(?)

delete_countЧисло операций удаления с момента последнего подключения к БД
expunge_countЧисло удалений записи и всех ее предыдущих версий, для записей чьи удаления  были потверждены
insert_countЧисло операций вставки в БД с момента последнего подключения к БД
purge_countЧисло удалений старых версий полностью готовых записей (записи, которые подтверждены, так чтобы более старые версии больше не были необходимы)
read_idx_countЧисло чтений выполненных по индексу, с момента последнего подключения
read_seq_countЧисло последовательных просмотров таблицы (чтение строк) сделанные для каждой таблицы, с момента последнего подключения к БД
update_countЧисло обновлений БД с момента последнего подключения к БД

Пример вызова isc_database_info()

Следующий код запрашивает размер страницы и число буферов кэша для подключенной БД, затем анализирует буфер результатов:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ibase.h>
 
isc_db_handle db;
ISC_STATUS status_vector[20];
short dpb_buf_len=20;
 
char dpb_buf[]={
                    isc_dpb_version1,        //версия буфера 
                    isc_dpb_user_name,        //начинается кластер параметра имя пользователя
                    6,                            //длина этого параметра 6 байт
                    'S','Y','S','D','B','A',//строка имени пользователя
                    isc_dpb_password,            //начинается кластер пароля пользователя
                    9,                                                 //длина его 9 байт
                   'm','a','s','t','e','r','k','e','y'        //сам пароль
                  };
 
int main()
{
 char str[]="c:\\Doc\\Ibgpre\\ibmake\\xsample.gdb";
 
isc_attach_database(status_vector, strlen(str), str,&db,dpb_buf_len,dpb_buf);
 if (status_vector[0] == 1 && status_vector[1])
 {
          isc_print_status(status_vector);
 }
 
 //обьявляем и заполняем буфер запроса о информации
 char db_items[] = {
                           isc_info_page_size,  //информация о размере страницы
                    isc_info_num_buffers,//информация о числе буферов кэша
                    isc_info_end                 //конец буфера
                   };
 char res_buffer[40], *p, item;
 int length;
 long page_size = 0L, num_buffers = 0L;
 
 isc_database_info(status_vector,&db, 
                   sizeof(db_items),
                   db_items,sizeof(res_buffer),
                   res_buffer);
 if (status_vector[0] == 1 && status_vector[1]) 
 {
  /* Для ошибок */
          isc_print_status(status_vector);
          return(1);
 }
        /* Выделяет значения возвращенные в буфере результатов. */
 for (p = res_buffer; *p != isc_info_end ; ) 
 {
          item = *p++;
          length = isc_vax_integer(p, 2);
          p += 2;
          switch (item)
          {
             case isc_info_page_size:
             page_size = isc_vax_integer(p, length);
             break;
                   case isc_info_num_buffers:
            num_buffers = isc_vax_integer(p, length);
            break;
             default:
            break;
          }
          p += length;
 }
 printf("page_size=%d\nnum_buffers=%d\n",page_size,num_buffers);
 
 if(db)
  isc_detach_database(status_vector, &db);
 
 return 0;
}

Отключение от БД

Просто вызываем isc_detach_database(status_vector, &db)(см.пример выше);                

И все ресурсы освободятся, а дескриптор станет 0L

Удаление БД

Пример говорит сам за себя

. . .
isc_db_handle db;
char *str = "xsample.gdb";
ISC_STATUS status_vector[20];
. . .
db = 0L;
/* Сначала соединяемся? чтобы получить дескриптор. */
isc_attach_database(status_vector, strlen(str), str, &db, 0, NULL);
if (status_vector[0] == 1 && status_vector[1])
{
  error_exit();
}
isc_drop_database(status_vector, &db1);/* вот тут удаляем БД */
if (status_vector[0] == 1 && status_vector[1])
{
  error_exit();
}
. . .

Использование транзакций

Эта глава описывает как формировать буфер параметров транзакции (TPB), как обьявлять и инициализировать дескриптор транзакций, и  как использовать API функции которые управляют транзакциями. Также объясняется как получать ID транзакции.

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

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

Функция                                        Описание

isc_start_transaction() Начинает новую транзакцию с одной или               несколькими БД; использует предварительно объявленный и заполненный TPB

isc_commit_retaining()                       Потверждает изменения произведенные транзакцией

и сохраняет контекст для дальнейшей обработки транзакции (то есть не заканчивает транзакцию. прим.пер)

isc_commit_transaction()                    Подтверждает изменения произведенные транзакцией

                                       , и заканчивает транзакцию.

isc_rollback_transaction()Производит откат изменений внесенных транзакцией, и заканчивает транзакцию
 

В дополнение к этим функциям, следующая таблица содержит редко используемые API

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

 

Функция                Описание
isc_start_multiple()             Стартует транзакцию, если использеутся фортран вместо isc_start_transaction()
isc_prepare_transaction()                        Подготавливает первую стадию двухфазного

   подтверждения, до первого вызова ф-ии isc_commit_transaction(); используется  когда необходимо отменить автоматичекое двухфазное

   подтверждение

isc_prepare_transaction2()               Подготавливает первую стадию двухфазного

                                          подтверждения, до первого вызова ф-ии

isc_commit_transaction();используется              когда необходимо отменить автоматичекое двухфазное     подтверждение

 

Старт транзакций

Старт транзакций происходит в три этапа:

1.Создание и инициализация дескриптора транзакции для каждой одновременно стартующей транзакции.
2.Необязательное создание и заполнение TPB для каждой транзакции.
3.Вызов ф-ии isc_start_transaction() для старта каждой транзакции

Все эти этапы описаны далее.

Примечание: Программисты пишущие приложения которые не разрешают использовать функции с переменным числом параметров должны использовать функцию isc_start_multiple() вместо isc_start_transaction().

Создание дескриптора транзакции

Каждая транзакция которая используется в приложении должна быть связана с владеющим ей дескриптором транзакции (transaction handle), указателем на адрес который используется всеми API ф-ями. Ibase.h содержит декларацию типа для дескриптора транзакции.

typedef void ISC_FAR *isc_tr_handle;

Объявление дескриптора транзакции

Для правильного объявления дескриптора используется тип isc_tr_handle.

#include <ibase.h>
. . .
isc_tr_handle tr1;
isc_tr_handle tr2;

Инициализация дескритора

#include <ibase.h>
. . .
isc_tr_handle tr1;
isc_tr_handle tr2;
. . .
/* Устанавливаем дескриптор в 0 перед использованием. */
tr1 = 0L;
tr2 = 0L;

Если он будет не 0, то isc_start_transaction() вернет код ошибки

Создание буфера параметров транзакции

Буфер параметров транзакции (TPB) – это необязательный байтовый массив, определяемый приложением, которое через этот буфер передает аргументы в isc_start_transaction(), которая, в свою очередь, устанавливает аттрибуты транзакции, ее операционные характеристики, режим чтения или записи для доступа к таблице, или только чтение, и могут или нет другие  активные транзакции иметь разделяемый доступ к таблице. Каждая транзакция должна иметь TPB, или использовать совместно с другими общий TPB.

Примечание: Если TPB не создан, то в isc_start_transaction() нужно передать вместо TPB – NULL.

И тогда установятся атрибуты транзакции по умолчанию.

TPB обьявляется в С программе как char массив однобайтовых элементов. Каждый элемент это параметр который описывает один аттрибут транзакции. Вот так выглядит обычная декларация этого буфера:

static char isc_tpb[] = {isc_tpb_version3,

isc_tpb_write,

isc_tpb_read_committed,

isc_tpb_no_rec_version,

isc_tpb_wait};

Этот пример использует параметрические константы определенные в заголовочном файле ibase.h . Первый элемент в каждом TPB должен быть константой isc_tpb_version3. Следующая таблица показывает доступные TPB константы, описывает их назначение, показывает которые из них назначаются по умолчанию если указатель на TPB установлен в NULL.

Параметр                        Описание

isc_tpb_version3                                Версия 3 буфера транзакций

isc_tpb_consistency                  Модель транзакции "блокировка таблицы"

isc_tpb_concurrency            Производительная, параллельная транзакция с приемлемой последовательностью; использование этого параметра дает все преимущества InterBase модели многорождаемых транзакций  [Значение по умолчанию]?????
isc_tpb_shared              Параллельный, разделяемый доступ к определенной таблице между всеми транзакциями; используется в сочетании с isc_tpb_lock_read и isc_tpb_lock_write, чтобы установить опцию блокировки [Значение по умолчанию]
isc_tpb_protected          Параллельный, ограниченный доступ к определенной таблице; используется в сочетании с isc_tpb_lock_read и isc_tpb_lock_write, чтобы установить опцию блокировки
isc_tpb_wait              Резолюция блокировки определяет, что транзакция должна ждать, пока блокированные ресурсы не будут освобождены перед повторением операции [Значение по умолчанию]    
isc_tpb_nowait                            Резолюция блокировки определяет, что транзакция не должна ждать при блокировке ресурсов, когда они будут освобождены, вместо этого должна быть возвращена немедленно ошибка конфликта блокировки.
isc_tpb_read                   Режим доступа только для чтения, который позволяет транзакции лишь производить выбор данных из таблиц
isc_tpb_write                     Режим  доступа чтения-записи позволяющий транзакции выбирать, вставлять и обновлять данные таблицы[по умолчанию]
isc_tpb_lock_read                      Доступ только для чтения к определенной таблице. Используется в сочетании с isc_tpb_shared, isc_tpb_protected, и isc_tpb_exclusive чтобы установить блокировку.
isc_tpb_lock_write                 Доступ чтение-запись для определенной таблицы. Используется в сочетании с isc_tpb_shared, isc_tpb_protected, и isc_tpb_exclusive чтобы установить блокировку[по умолчанию]
isc_tpb_read_committed                  Высокопроизводительная, высоко(?) параллельная транзакция, которая может читать изменения, совершенные другими параллельными транзакциями. Использование этого параметра дает полное преимущество модели InterBase многорождаемых транзакций.
isc_tpb_rec_version                    Позволяет isc_tpb_read_committed транзакции читать наиболее позднюю подтвержденную версию записи даже если есть другие, но неподтвержденные версии.    
isc_tpb_no_rec_version                        Позволяет isc_tpb_read_committed транзакции с читать только самую позднейшую подтвержденную версию записи. Если есть неподтвержденная версия записи и она задерживается, и isc_tpb_wait также определен, то транзакция ждет для задержанной записи подтверждения или отката назад. Иначе произойдет сразу ошибка конфликта блокировки. (То есть она не показывает старые версии записей если есть неподтвержденные новые. Прим. переводчика)

TPB параметры определяют следующие характеристики транзакции:

·Номер версии буфера параметров транзакции (Transaction version number), используется ядром InterBase.
·Режим доступа (Access mode) описывает действия, которые могут быть выполнены функциями связанными с транзакцией. Существуют следующие режимы доступа:

                                   isc_tpb_read

                                   isc_tpb_write    

·Уровень изоляции (Isolation level) описывает насколько изменения внесенные транзакцией будут отражены в других, одновременно с данной стартовавших транзакций. Существуют следующие уровни изоляции:
isc_tpb_concurrency                                                   isc_tpb_consistency                                               isc_tpb_read_committed, isc_tpb_rec_version                isc_tpb_read_committed, isc_tpb_no_rec_version

·Разрешение конфликтов блокировок (Lock resolution). Описывает как транзакция должна вести себя если происходит конфликт блокировок. Существуют два вида реакции.                                                                isc_tpb_wait                                                                isc_tpb_nowait
·Необязательное резервирование таблицы(Table reservation)  описывает метод доступа и тип блокировки для указанной таблицы с которой транзакция работает. Когда используется резервирование таблицы, таблицы резервируются с определенным типом доступа, когда транзакция стартует,  а не когда она получит фактически обратится к обратится к таблице. Существуют следующие виды резервирования:        
isc_tpb_shared, isc_tpb_lock_write                                        isc_tpb_shared, isc_tpb_lock_read                                isc_tpb_protected, isc_tpb_lock_write                                        isc_tpb_protected, isc_tpb_lock_read

Определение версии номера транзакции

Первый параметр в TPB должен всегда определять версию буфера. Он должен всегда быть установлен в isc_tpb_version3. Следующее объявление TPB иллюстрирует правильное использование этого параметра:

static char isc_tpb[] = {isc_tpb_version3, ...};

Определение режима доступа

Режим доступа описывает действия которые может выполнять транзакция. По умолчанию режим доступа, isc_tpb_write, разрешает транзакции читать данные из таблицы и писать данные в нее. Второй режим доступа, isc_tpb_read, ограничивает доступ только чтением. К примеру, следующее объявление TPB определяет транзакцию только для чтения:

static char isc_tpb[] = {isc_tpb_version3, isc_tpb_read};  

TPB разрешает определять только один параметр режима доступа. Более поздние объявления отменяют ранние.

Определение уровня изоляции

Уровень изоляции (Isolation level) описывает насколько изменения внесенные транзакцией будут отражены в других, одновременно с данной стартовавших транзакций

ISC_TPB_CONCURRENCY

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

static char isc_tpb[] = {isc_tpb_version3,

                                       isc_tpb_write,

                                       isc_tpb_concurrency};

ISC_TPB_READ_COMMITTED

Второй уровень изоляции,  isc_tpb_read_committed, имеет все преимущества isc_tpb_concurrency и дополнительно позволяет транзакции обращаться к подтвержденным изменениям совершенным другими параллельно работающими транзакциями. Два других параметра, isc_tpb_rec_version, и isc_tpb_no_rec_version, должны использоваться в комбинации с isc_tpb_read_committed. Они предоставляют более гибкое управление для доступа транзакции к подтвержденным изменениям.

·isc_tpb_no_rec_version, заданное по умолчанию, определяет что транзакция может видеть только самую последнюю версию записи. Если изменение записи продолжается, но не завершено подтверждением, запись не может быть прочитана.
·isc_tpb_rec_version определяет что транзакция может читать самую последнюю подтвержденную версию записи, даже если самая последняя не подтвержденная версия записи не завершилась и продолжает изменение.

Следующее объявление создает TPB с уровнем изоляции read_committed, и определяет транзакцию которая может читать самую последнюю версию записи.

static char isc_tpb[] = {isc_tpb_version3,

                                     isc_tpb_write,

                                     isc_tpb_read_committed,

                                     isc_tpb_rec_version};

ISC_TPB_CONSISTENCY

InterBase также поддерживает ограничительный уровень изоляции. isc_tpb_consistency не разрешает доступ транзакции к таблицам, если туда производят записи другие транзакции; и также не дает доступа другим транзакциям к таблице в которую производит запись эта транзакция. Этот уровень изоляции предназначен, чтобы гарантировать,  если производятся транзакцией записи в таблицу перед другими одновременно читающими и пишущими транзакциями, то только эта транзакция может изменять данные таблицы. Так как это существенно ограничивает одновременный доступ к таблице, то

isc_tpb_consistency надо использовать осторожно.

В TPB должен быть определен только  один параметр уровня изоляции (и один параметр уточнения, еслиуровень изоляции - isc_tpb_read_committed). Если определено больше одного параметра, то  более поздние объявления отменяют более ранние.

Если в TPB опущен параметр уровня изоляции то IB ставит транзакции уровень изоляции как  isc_tpb_concurrency.

Взаимодействия уровней изоляции

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

                                    isc_tpb_concurrency,

                                    isc_tpb_read_committed                isc_tpb_consistency


isc_tpb_write

isc_tpb_read

isc_tpb_write

isc_tpb_read

concurrency,

read_committed

isc_tpb_write

Некоторые одновременные обновления могут конфликтовать

___

Конфликты

Конфликты

isc_tpb_read

--

--

--

--

consistency

isc_tpb_write

Конфликты

--

Конфликты

Конфликты

isc_tpb_read

Конфликты

--

Конфликты

Эта таблица показывает, что транзакции isc_tpb_concurrency и isc_tpb_read_committed имеют наименьшее количество конфликтных ситуаций. К примеру, если t1 есть транзакция isc_tpb_concurrency с доступом isc_tpb_write, а t2 есть транзакция isc_tpb_read_committed с доступом isc_tpb_write, t1 и t2 тогда конфликтуют, когда они пытаются обновить одни и теже строки. Если t1 и t2 имеют доступ  isc_tpb_read, то они никогда не вступят в конфликт с другими транзакциями.

Разрешения конфликтов блокировок

Разрешение конфликтов блокировок (Lock resolution) описывает как транзакция должна вести себя если происходит конфликт блокировок. Существуют два вида реакции.

·isc_tpb_wait, по умолчанию определяет что транзакция будет ожидать пока блокированные ресурсы не освободятся. Как только ресурсы освободятся транзакция повторит операцию.
·isc_tpb_nowait, говорит что транзакция возвратит ошибку конфликта блокировок, без ожидания окончания блокировки.

К примеру, следующее объявление создает TPB с доступом для записи, и concurrency уровнем изоляции, и с разрешением блокировки isc_tpb_nowait:

static char isc_tpb[] = {isc_tpb_version3,

isc_tpb_write,

isc_tpb_concurrency,

isc_tpb_nowait};

TPB может иметь только один параметр разрешения конфликта блокировки. Если их больше одного, то поздняя декларация отменит раннюю. Если в TPB опущен этот параметр, то IB интерпретирует это как isc_tpb_concurrency.?

Определение резервирования таблицы

Обычно транзакции получают определенный доступ к таблицам только когда они непосредственно читают или пишут в таблицы. Параметр резервирования таблицы необязателен и может быть опущен в TPB. Резервирование таблицы (Table reservation) описывает режим доступа и разрешение конфликта блокировок для определенной таблицы которая доступна транзакции. Когда используется резервирование таблицы, таблицы резервируются для специального доступа когда транзакция стартует, а не когда транзакция фактически получит доступ к таблице.  Резервироание таблиц используется в среде, где одновременно существующие транзакции имеют совместный доступ к БД. Существуют следующие цели резервирования.

·Предотвращение возможных тупиковых ситуаций и конфликтов обновления, которые могут происходить, если блокировки принимаются только там  где они фактически необходимы (поведение по умолчанию).
·Предусмотрение  зависимой блокировки, блокировок таблиц, на которые можно воздействовать ограничениями целостности и триггерами. Если есть явная зависимость, блокировка не требуется, можно ручаться, что конфликты обновлений не произойдут из-за косвенных конфликтов таблиц.(!!!!!!!!!!!!!!!!!!!!!!!!!!!!)
·Изменение уровеня разделяемого доступа для одной или нескольких таблиц в транзакции. Например, isc_tpb_write транзакция с уровнем изоляции isc_tpb_concurrency может нуждаться в исключительных правах на изменения для отдельной таблицы, и могла бы использовать параметр резервирования, чтобы гарантировать себе единственный доступ для записи в таблицу.

Виды резервирования:

- Isc_tpb_shared, isc_tpb_lock_write,  разрешают любой транзакции с режимом доступа isc_tpb_write и уровнями изоляции isc_tpb_concurrency или isc_tpb_read_committed модифицировать данные, в то время как другие транзакции с этими уровнями изоляции и режимом доступа isc_tpb_read могут только читать данные.

- Isc_tpb_shared, isc_tpb_lock_read, разрешает любой транзакции читать данные, и любой транзакции с режимом доступа isc_tpb_write изменять данные. Это - наиболее либеральный режим резервирования.

- Isc_tpb_protected, isc_tpb_lock_write, не дает другим транзакциям изменять данные. Другие транзакции с уровнями изоляции isc_tpb_concurrency или isc_tpb_read_committed могут читать данные, но только эта транзакция может изменять их.

- Isc_tpb_protected, isc_tpb_lock_read, не дает всем транзакциям делать изменения данных, но разрешает всем транзакциям читать данные.

Имя таблицы, которую надо резервировать должно сразу следовать за параметрами резервирования. Например, следующее обьявление TPB резервирует таблицу, EMPLOYEE, для защищенного доступа  чтения:

static char isc_tpb[] = {isc_tpb_version3,

isc_tpb_write,

isc_tpb_concurrency,

isc_tpb_nowait,

isc_tpb_protected, isc_tpb_lock_read, "EMPLOYEE"};

Несколько таблиц могут быть зарезервированы одновременно. Следующее объявление иллюстрирует, как две таблицы зарезервированы, один для защищенного чтения, другой для защищенной записи:

static char isc_tpb[] = {isc_tpb_version3,

isc_tpb_write,

isc_tpb_concurrency,

isc_tpb_nowait,

isc_tpb_protected, isc_tpb_lock_read, "COUNTRY",

isc_tpb_protected, isc_tpb_lock_write, "EMPLOYEE"};

 

Использование TPB по умолчанию.

Для транзакции TPB необязателен. Если он не нужен, тогда в isc_start_transaction() в качестве  указателя на TPB надо передать NULL. В этом случае IB будет запускать транзакцию с параметрами как если бы TPB был обьявлен так:

static char isc_tpb[] = {isc_tpb_version3,

isc_tpb_write,

isc_tpb_concurrency,

isc_tpb_wait};

Вызов isc_start_transaction()

Как только дескриптор транзакции и TPB подготовлены, транзакция может стартовать посредством вызова isc_start_transaction(), использующей следующий синтаксис:

ISC_STATUS isc_start_transaction(ISC_STATUS *status vector,
isc_tr_handle *trans_handle,
short db_count,
isc_db_handle *&db_handle,
unsigned short tpb_length,
char *tpb_ad);

Для транзакции, которая выполняется для единственной базы данных, установите  db_count в 1. db_handle должен быть установлен уже ф-ей isc_attach_database (), tpb_length - размер TPB, и isc_tpb - адрес TPB. Пример:

#include <ibase.h>
. . .
ISC_STATUS status_vector[20];
isc_db_handle db1;
isc_tr_handle tr1;
static char isc_tbp[] = {isc_tpb_version3,
isc_tpb_write,
isc_tpb_concurrency,
isc_tpb_wait};
. . .
/* Здесь инициализируем дескрипторы БД и транзакции */
db1 = 0L;
tr1 = 0L;
. . .
/* Код для подключения к БД здесь опущен*/
isc_start_transaction(status_vector, &tr1, 1, &db1, (unsigned short) sizeof(isc_tpb), isc_tpb);

Транзакция может быть открыта для нескольких БД. Для этого установите db_count в число БД для которых будет запущена транзакция, где для каждой БД повторяется группа параметров db_handle, tpb_length, and isc_tpb. Пример:

isc_start_transaction(status_vector, &tr1 ,2, &db1,(unsigned short) sizeof(isc_tpb), &tpb,

&db2,(unsigned short) sizeof(tpb),&tpb);

Завершение транзакций

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

·isc_commit_transaction () делает изменения транзакции постоянными в базе данных. Для транзакций, которые охватывают больше одной базы данных, эта функция исполняет автоматический двухфазный commit, который гарантирует, что все изменения сделаны успешно.
·isc_rollback_transaction() отменяет все изменения произведенные транзакцией, и возвращает БД в состояние которое было перед стартом транзакции.

isc_commit_transaction () и isc_rollback_transaction () закрывают  потоки записи, связанные с транзакцией, повторно инициализируют дескриптор транзакции в 0L,  и освобождают ресурсы системы, выделенные для транзакции. Освобожденные ресурсы системы  вновь доступны для последующего использования любым приложением или программой.

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

Isc_start_multiple не будем рассматривать, так как фортран устарел.

Использование isc_commit_transaction( )

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

ISC_STATUS isc_commit_transaction(

ISC_STATUS *status_vector,

isc_tr_handle *trans_handle);

Пример, следующийвызов подтверждает транзакцию:

isc_commit_transaction(status_vector, &trans);

Транзакции стартовавшие с режима доступа isc_tpb_read должны также заканчиваться вызовомк isc_commit_transaction (), а не isc_rollback_transaction (). База данных не изменяется в этом случае, but the overhead required to start subsequent transactions is greatly reduced.????

Использование isc_commit_retaining()

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

ISC_STATUS isc_commit_retaining(

ISC_STATUS *status_vector,

isc_tr_handle *trans_handle);

Isc_commit_retaining () записывает все задержанные изменения в базу данных, заканчивает текущую транзакцию без  закрытия ее потока записи и курсоров, и без освобождения ее ресурсов системы, затем начинает новую транзакцию и назначает существующие потоки и ресурсы системы  новой транзакции.

Например, следующий вызлв подтверждает определенную транзакцию, сохраняя текущее состояние курсора и ресурсы системы:

isc_commit_retaining(status_vector, &trans);

Вызов isc_rollback_transaction ()  после isc_commit_retaining () откатывает назад обновления и записи, встречающиеся после вызова isc_commit_retaining ().

Использование isc_prepare_transaction()

Когда транзакция подтверждается для многих баз данных, использующих isc_commit_transaction (), InterBase автоматически исполняет двухфазное подтверждение. В течение первой фазы подтверждения, ядро InterBase опрашивает все участвующие в транзакции базы данных, чтобы удостовериться, что они все еще доступны, затем записывает сообщение, описывающее транзакцию в  поле RDB$TRANSACTION_DESCRIPTION RDB ситемной таблицы $TRANSACTION , затем помещает транзакцию в состояние неопределенности (limbo). Именно в течение второй фазы  изменения транзакции фактически подтверждаются  для базы данных.

Некоторые приложения могут иметь их собственные, дополнительные требования, чтобы делать двухфазное подтверждение. Эти приложения могут вызывать isc_prepare_transaction () чтобы выполнить  первую стадию двухфазного аодтверждения, затем исполнить их собственные, дополнительные задачи перед завершением подтверждения вызовом isc_commit_transaction ().

Синтаксис для isc_prepare_transaction ():

ISC_STATUS isc_prepare_transaction(

ISC_STATUS *status_vector,

isc_tr_handle *trans_handle);

Например, следующий  фрагмент кода иллюстрирует, как приложение могло бы вызывать isc_prepare_transaction (), потом после его собственными подпрограммами, перед завершением подтверждения по isc_commit_transaction ():

ISC_STATUS status_vector[20];
isc_db_handle db1;
isc_tr_handle trans;
. . .
/* Initialize handles. */
db1 = 0L;
trans = 0L;
. . .
/* Code assumes a database is attached here, */
/* and a transaction started. */
. . .
/* Выполнение первой фазы двух – фазного подтвеждения. */
isc_prepare_transaction(status_vector, &trans);
/* Приложение делает здесь свою какую-то работу */
my_app_function();
/* Сечас выполнится подтвержедние второй фазы */
isc_commit_transaction(status_vector, &trans);

Это вообще опасная практика,  задерживание второй фазы подтверждения после завершения первой, потому что задержки увеличивают шанс, что сетевые проблемы  или проблемы с сервером могут произойти между фазами

Использование isc_prepare_transaction2( )

Подобно isc_prepare_transaction (), isc_prepare_transaction2 () выполняет,  первую фазу двухфазного подтверждения, за исключением того, что isc_prepare_transaction2 () позволяет приложению применить свое  описание транзакции для вставки в поле RDB$TRANSACTION_DESCRIPTION системной таблицы RDB$TRANSACTION.

ВАЖНО Не используйте этот запрос без первичного исследования и понимания информации хранимой InterBase в RDB$TRANSACTION_DESCRIPTION в течение автоматического  двухфазного подтверждения. Хранение неподходящей или неполной информации может предотвратить восстановление базы данных, если двухфазное подтверждение даст сбой.

Использование isc_rollback_transaction( )

Используйте isc_rollback_transaction () чтобы восстановить базу данных к ее состоянию до начала транзакции. Isc_rollback_transaction () также закрывает потоки записи, связанные с транзакцией, сбрасывает имя транзакции в 0, и освобождает ресурсы системы, выделенные для транзакции. Isc_rollback_transaction () обычно появляется в подпрограммах обработки ошибок. Синтаксис для isc_rollback_transaction ():

ISC_STATUS isc_rollback_transaction(

ISC_STATUS *status_vector,

isc_tr_handle *trans_handle);

Пример отката транзакции:

isc_rollback_transaction(status_vector, &trans);

Работа с динамическим SQL (DSQL)

Эта глава описывает как использовать API  динамические ф-ии SQL (DSQL), чтобы обрабатывать динамически созданные инструкции для создания и обработки данных. Использование вызовов API нижнего уровня позволяет клиентским приложениям формировать SQL инструкции или опрашивать конечных пользователей во время выполнения, предоставлять конечным пользователям знакомый интерфейс с БД. А также предоставляет разработчикам приложений доступ нижнего уровня к особенностям InterBase, типа множественных БД, обычно не доступных на высоком уровне для встроенных DSQL инструкций. Например, утилита InterBase isql - это DSQL приложение построенное на вызовах API нижнего уровня.

Все API DSQL ф-ии начинаются с “isc_dsql” , чтобы их отличить от других функций.

Краткий обзор процесса программирования на DSQL

Построение и выполнение DSQL приложений с API включает следующие основные шаги:

·Встраивание DSQL API  ф-ий в приложение.
·Использование средств базового языка, таких как типы данных и макрокоманды, чтобы предоставить области ввода и вывода для проходящих инструкций и параметров во время выполнения.
·Программные методы, которые используют эти инструкции и средства, чтобы обработать инструкции SQL во время выполнения.

Эти шаги описываются детально в этой главе.

Ограничения для DSQL API

Хотя DSQL предлагает много преимуществ, но он также имеет следующие ограничения.

·Динамическая обработка транзакций не разрешается; все именованные транзакции должны быть объявлены во время компиляции.
·Динамический доступ к Blob и массивам данных не поддерживается; К Blob  и массивам данных можно обращаться, но только через стандартные, статически обработанные инструкции SQL, или через вызовы API нижнего уровня.
·Создание БД выполняется через инструкции выполнения CREATE DATABASE в пределах контекста  EXECUTE IMMEDIATE.

Доступ к базам данных

InterBase API разрешают приложениям подсоединяться одновременно к нескольким БД используя дескрипторы БД. Дескрипторы БД должны быть обьявлены и инициализированы когда приложение откомпилировано. Отдельные дескрипторы базы данных должны быть определены и инициализированы для каждой базы данных доступной параллельно.

Дескрипторы транзакций

InterBase требует чтобы все дескрипторы транзакций были объявлены в откомпилированном приложении. После компиляции, дескрипторы транзакции не могут быть изменены во время выполнения, и при этом новые дескрипторы не могут быть объявлены динамически во время выполнения. Большинство функций API, которые обрабатывают инструкции SQL во время выполнения, типа isc_dsql_describe (), isc_dsql_describe_bind (), isc_dsql_execute (), isc_dsql_execute2 (), isc_dsql_execute_immediate (), isc_dsql_exec_immed2 (), и isc_dsql_prepare (), поддерживает включение параметра дескриптора транзакции. Инструкции SQL, обработанные этими функциями не могут передавать дескрипторы транзакции, даже если синтаксис SQL для инструкции разрешает использование предложения TRANSACTION.

Прежде, чем  дескриптор транзакции будет использоваться, он должен быть объявлен и инициализирован в 0. Следующий код объявляет, инициализирует, и использует дескриптор транзакции в  API вызове , который размещает и готовит инструкцию SQL для выполнения:

#include <ibase.h>
. . .
isc_tr_handle trans; /* Обьявляется дескриптор транзакции. */
isc_stmt_handle stmt; /* Обьявляется дескриптор инструкции. */
char *sql_stmt = "SELECT * FROM EMPLOYEE";
isc_db_handle db1;
ISC_STATUS status_vector[20];
. . .
trans = 0L; /* Дескриптор транзакции инициализируется в 0. */
stmt = NULL; /* Дескриптор инструкции устанавливается в NULL перед выделенем. */
/* Здесь код для подключения, к БД и старта транзакции */
. . .
/* Выделяем место для дескриптора инструкции. */
isc_dsql_allocate_statement(status_vector, &db1, &stmt);
/* Подготавливаем инструкцию для выполнения. */
isc_dsql_prepare(status_vector, &trans, &stmt, 0, sql_stmt, 1, NULL);

Примечание

Инструкция SQL SET TRANSACTION не может быть подготовлена через isc_dsql_prepare (), но она может быть обработана с помощью isc_dsql_execute_immediate () если:

1.Предыдущие транзакции закончились подтверждением  или откачены назад.
2.Дескриптор транзакции установлен в NULL.

Создание базы данных

Создание новой базы данных в API приложении:

1.Отсоединитесь от всех БД с помощью isc_detach_database().
2.Сформируйте инструкцию  CREATE DATABASE для обработки.
3.Выполните инструкцию с помощью isc_dsql_execute_immediate () или isc_dsql_exec_immed2 ().

К примеру, следующие инструкции отсоединяют все подключенные БД и создают новую БД. Любой дескриптор существующей БД устанавливается в NULL, и может быть использован в будущем для подключения к новым БД.

char *str = "CREATE DATABASE \"new_emp.gdb\"";
. . .
isc_detach_database(status_vector, &db1);
isc_dsql_execute_immediate(status_vector, &db1, &trans, 0, str, 1,NULL);

Написание API приложения для обработки SQL инструкций

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

·Текст SQL инструкции
·Число базовых переменных
·Типы базовых переменных
·Ссылки на объекты БД

Создание приложения API DSQL гораздо сложнее, чем программирование  приложений со встроенным SQL и со статическим SQL, потому что для большинства операций DSQL, приложение должно явно выделить и обработать расширенный дескриптор области  SQL (XSQLDA) - структура данных, чтобы передать данные в БД  или из БД.

Использование API для обработки DSQL инструкций предполагает следующие основные шаги:

1.Определите, могут ли вызовы API обработать инструкцию SQL.
2.Представьте инструкцию SQL как строку символов в приложении.
3.В случае необходимости, выделите одну или больше структур XSQLDA для входных параметров и возвращаемых значений.
4.Используйте соответствующие API, и программные методы, чтобы обработать инструкцию SQL.
 

Определение API вызовов которые могут обработать SQL инструкцию.

Кроме упомянутого ранее в этой главе, функции DSQL могут обрабатывать большинство инструкций SQL. Например, DSQL может обрабатывать инструкции манипуляции данными, такими как DELETE и INSERT, инструкции опеределения данных таких как ALTER TABLE и CREATE INDEX, и инструкции SELECT. Следующая таблица показывает список SQL инструкций , которые не могут быть обработаны функциями DSQL:

Инструкция                                        Инструкция

CLOSE                                                DECLARE CURSOR

DESCRIBE                                        EXECUTE

EXECUTE IMMEDIATE                                 FETCH

OPEN                                                PREPARE

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

Представление инструкции SQL как строки символов

В рамках приложения DSQL, инструкция SQL может исходить из различных источников. Она может исходить  непосредственно от пользователя, кто вводит инструкцию при подсказке, как делает isql. Или она может быть сгенерирована приложением в ответ на действие пользователя. Не взирая на источник инструкции SQL, она должна быть представлена как  строка символов, которую передают к DSQL для обработки.

Строки SQL инструкций  не начинаются с префикса EXEC SQL  и не заканчиваются точкой с запятой (;) как это делается в типичных  приложениях встроенного SQL. Например, следующее объявление переменной базового языка – связывается со строкой инструкции SQL:

char *str = "DELETE FROM CUSTOMER WHERE CUST_NO = 256";

Примечание: Точка с запятой, которая появляется в конце этого объявления символа - терминатор строки в С, а не часть строки инструкции SQL.

Определение параметров в строках инструкции SQL

Строки инструкции SQL часто включают параметры, передаваемые по значению, или выражения  которые приводят к отдельному числовому или символьному значению. Значение параметра в  строке инструкции может быть передано как константа, или как метка - заполнитель во время выполнения. Например, следующая строка инструкции передает 256 как константу:

char *str = "DELETE FROM CUSTOMER WHERE CUST_NO = 256";

Также можно формировать строки во время выполнения c комбинацией констант. Этот метод полезен для инструкций, где переменная не истинная константа, или это - таблица или название столбца, и где инструкция выполняется только один раз в приложении.

Чтобы передавать параметр как метку - заполнитель, значение передают как вопросительный знак (?) вставленный внутрь инструкции строки:

char *str = "DELETE FROM CUSTOMER WHERE CUST_NO = ?";

Когда функция DSQL обрабатывает инструкцию, содержащую метку - заполнитель, то она заменяет вопросительный знак значением, находящимся в расширенном SQL дескрипторе (XSQLDA) предварительно объявленном и заполненном в приложении. Используйте метки - заполнители  в инструкциях, которые подготовливаются один раз, но выполняются много раз с различными значениями параметра.

Заменяемые  параметры, передаваемые по значению часто используются, чтобы передать значения SQL предложения  SELECT  инструкции WHERE  и в предложениях SET инструкции UPDATE.

Понятие XSQLDA

Все DSQL приложения должны содержать объявление одной или нескольких структур данных, называемых расширенными областями данных SQL - XSQLDA (eXtended SQL Descriptor Areal). Определение структуры XSQLDA может быть найдено в заголовочном файле ibase.h . Приложения объявляют элементы XSQLDA для использования.

XSQLDA это структура данных базового языка который использует DSQL для обмена данными с БД при обработке SQL инструкции. Есть два типа XSQLDA: для ввода и для вывода. Оба дескриптора реализованы с использованием структуры XSQLDA.

Одно поле в XSQLDA, sqlvar, является структурой XSQLVAR. Sqlvar особенно важен, потому что один XSQLVAR должен быть определен для каждого входного параметра или возвращаемого столбца. Подобно XSQLDA, XSQLVAR - структура, определенная в ibase.h .

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

Следующий рисунок иллюстрируют связи между XSQLDA и XSQLVAR:

Элемент XSQLDA               

short version

char sqldaid[8]

ISC_LONG sqldabc

short sqln

short sqld

XSQLVAR sqlvar[1]                                 |

|

|амассив n элементов XSQVAR

1-й элемент                                        n-й элемент

short sqltype                                        short sqltype

short sqlscale                                        short sqlscale

short sqlsubtype                                short sqlsubtype

short sqllen                                        short sqllen

char *sqldata                                        char *sqldata

short *sqlind                                        short *sqlind

short sqlname_length                                short sqlname_length

char sqlname[32]                                char sqlname[32]

short relname_length             . . .                        short relname_length

char relname[32]                                char relname[32]

short ownname_length                                short ownname_length

char ownname[32]                                char ownname[32]

short aliasname_length                                short aliasname_length

char aliasname[32]                                char aliasname[32]

XSQLDA для ввода состоит из одной структуры XSQLDA и одной структуры XSQLVAR для каждого входного параметра.  XSQLDA для вывода также состоит из одной структуры XSQLDA и одной структуры XSQLVAR для каждого элемента данных, возвращенного инструкцией. XSQLDA и его связанные структуры XSQLVAR выделены в отдельный блок смежной памяти.

Isc_dsql_prepare(), isc_dsql_describe(), и isc_dsql_describe_bind()  могут использоваться, чтобы определить нужное число структур XSQLVAR для выделения, и макрокоманда XSQLDA_LENGTH может использоваться, чтобы выделить нужное количество памяти.

Описание полей XSQLDA

Определение поля                Описание

short versionУказывает версию структуры XSQLDA. Устанавливается приложением. Текущая версия определена в ibase.h как SQLDA_VERSION1

char sqldaid[8]                Зарезервировано для будущего использования

ISC_LONG sqldabc                Зарезервировано для будущего использования

short sqlnУказывает число элементов в массиве sqlvar; приложение должно устанавливать это поле каждый раз, когда оно выделяет  память для дескриптора
short sqldУказывает число параметров XSQLDA  для ввода, или число элементов списка выбора  XSQLDA для вывода; устанавливается InterBase c помощью isc_dsql_describe (), isc_dsql_describe_bind (), или isc_dsql_prepare ()
Для дескриптора ввода, sqld= 0 указывает, что инструкция SQL не имеет никаких параметров; для дескриптора вывода, sqld= 0 указывает, что инструкция SQL - не инструкция SELECT

XSQLVAR sqlvarМассив структур XSQLVAR; число элементов в массиве определено в поле sqln
short sqltypeУказывает SQL тип данных параметров или элементов списка выбора; устанавливается InterBasec помощью isc_dsql_describe (), isc_dsql_describe_bind (), или Isc_dsql_prepare ()
short sqlscaleПредставляет масштаб, определенный как отрицательное число, для точного числового типа данных  ( DECIMAL, NUMERIC); устанавливается IB с помощью isc_dsql_describe (), isc_dsql_describe_bind (), или isc_dsql_prepare ()
short sqlsubtypeОпределяет подтип дляBLOB данных; устанавливается IB с помощью  isc_dsql_describe (), isc_dsql_describe_bind (), или isc_dsql_prepare ()
short sqllenУказывает максимальный размер, в байтах, данных в поле sqldata; устанавливается IB с помощью  isc_dsql_describe (), isc_dsql_describe_bind (), или isc_dsql_prepare ()
char *sqldataДля дескрипторов ввода, определяет  адрес элемента списка выбора или параметра; установливается приложением. Для дескрипторов  вывода, содержит значение  элемента из списка выбора; установливается IB (проще, указатель на данные)
short *sqlindДля ввода, определяет указатель на переменную- индикатор; устанавливается приложением; При выводе, определяет указатель столбца значения индикатора  для элемента списка выбора следующего FETCH . Значение 0 указывает, что столбец - не NULL ; значение -1 указывает, что  столбец NULL; устанавливается IB
short sqlname_lengthОпределяет длину, в байтах, данных в поле, sqlname; устанавливается IB с помощью isc_dsql_prepare () или isc_dsql_describe ()

char sqlname[32]                        Содержит имя столбца. Не NULL, заканчивается (\0);

установливается IB с помощью isc_dsql_prepare () или isc_dsql_describe ()

short relname_length                        Определяет длину, в байтах, данных в поле, relname;

устанавливается IB с помощью isc_dsql_prepare () или isc_dsql_describe ()

char relname[32]                        Содержит имя таблицы; не NULL заканчивается (\0) 

устанавливается IB с помощью isc_dsql_prepare () или isc_dsql_describe ()

short ownname_length                Определяет длину, в байтах, данных в поле, ownname;

устанавливается IB с помощью isc_dsql_prepare () или isc_dsql_describe ()

char ownname[32]Содержит имя владельца таблицы; не NULL , заканчивается (\0, устанавливается IB с помощью isc_dsql_prepare () или isc_dsql_describe ()

short aliasname_length                Определяет длину, в байтах, данных в поле, aliasname;

устанавливается IB с помощью isc_dsql_prepare () или isc_dsql_describe ()
char aliasname[32]Содержит имя псевдонима столбца. Если псевдонима нет, содержит имя столбца; не NULL заканчивается (\0), устанавливается IB с помощью isc_dsql_prepare () или isc_dsql_describe ()
 

Дескрипторы ввода

Дескрипторы ввода используются, для обработки строк инструкции SQL, которые содержат параметры. Прежде, чем приложение сможет выполнить инструкцию с параметрами, оно должно присвоить параметрам значения. Приложение указывает  число параметров переданных в поле XSQLDA sqld, затем описывает каждый параметр в отдельной структуре XSQLVAR. Например, следующая  строка - инструкция содержит два параметра, поэтому приложение должно установить sqld в 2, и описать каждый параметр:

char *str = "UPDATE DEPARTMENT SET BUDGET = ? WHERE LOCATION = ?";

Когда инструкция выполняется, в первом XSQLVAR находится информации относительно значения BUDGET, а во втором XSQLVAR информация о LOCATION.

Дескрипторы вывода

В дескрипторах вывода возвращаются результаты выполненного приложением запроса. Поле sqld XSQLDA указывает, сколько значений было возвращено. Каждое значение сохраняется в отдельной структуре XSQLVAR. Поле XSQLDA sqlvar указывает на первую  из XSQLVAR структур . Следующая строка - инструкция требует дескриптора вывода:

char *str = "SELECT * FROM CUSTOMER WHERE CUST_NO > 100";

Использование макроса XSQLDA_LENGTH

В заголовочном файле ibase.h содержится определение макроса, XSQLDA_LENGTH, подсчитывающего число байт которые нужно выделить для структур XSQLDA для ввода и вывода. XSQLDA_LENGTH определен следующим образом:

#define XSQLDA_LENGTH (n) (sizeof (XSQLDA) + (n – 1) * sizeof(XSQLVAR))

n - число параметров в cтроке-инструкции, или число элементов в списке - select, возвращенных запросом. Например, следующая инструкция C использует макрокоманду XSQLDA_LENGTH, чтобы определить сколько памяти надо выделить для XSQLDA с 5 параметрами или возвращаемыми элементами:

XSQLDA *my_xsqlda;

. . .

my_xsqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(5));

. . .

Константные макросы типов данных SQL

InterBase определяет набор макро констант для представления типов данных SQL и   NULL – состояния информации в XSQLVAR. Приложение должно использовать эти макро константы, для определения типов данных параметров и  типов данных элементов списка -select в инструкции SQL. Следующая таблица перечисляет каждый тип данных SQL, его соответствующее макро-константное выражение, тип данных C или тип данных определенный InterBase , и используется поле sqlind или нет, чтобы указать параметр или переменную, которая содержит NULL или неопределенные данные:

Тип данных        Макрос                        Тип данных С                использование sqlind

Array                SQL_ARRAY                        ISC_QUAD                                No

Array                SQL_ARRAY + 1                             ISC_QUAD                        Yes

Blob                SQL_BLOB                                ISC_QUAD                                No

BLOB                SQL_BLOB + 1                        ISC_QUAD                                Yes

CHAR        SQL_TEXT                                char[]                                        No

CHAR        SQL_TEXT + 1                        char[]                                        Yes

DATE                SQL_DATE                                ISC_DATE                                No

DATE                SQL_DATE + 1                        ISC_DATE                                Yes

DECIMAL        SQL_SHORT, SQL_LONG,

SQL_DOUBLE, or SQL_INT64        int, long, double, or ISC_INT64        No

DECIMAL        SQL_SHORT + 1,

SQL_LONG + 1,

SQL_DOUBLE + 1,

or SQL_INT64 + 1                              int, long, double, or ISC_INT64        Yes

DOUBLE

PRECISON        SQL_DOUBLE                        double                                No

DOUBLE

PRECISION        SQL_DOUBLE + 1                        double                                Yes

INTEGER        SQL_LONG                                long                                        No

INTEGER        SQL_LONG + 1                        ISC_LONG                                Yes

FLOAT        SQL_FLOAT                                float                                        No

FLOAT        SQL_FLOAT + 1                        float                                        Yes

NUMERIC        SQL_SHORT, SQL_LONG,

SQL_DOUBLE, or SQL_INT64        int, long, double, or ISC_INT64        No

NUMERIC         SQL_SHORT + 1,

SQL_LONG + 1,

SQL_DOUBLE + 1,

or SQL_INT64 + 1                            int, long, double, or ISC_INT64        Yes

SMALLINT        SQL_SHORT                        short                                        No

SMALLINT        SQL_SHORT + 1                        short                                        Yes

TIME                SQL_TIME                                ISC_TIME                                No

TIME                SQL_TIME + 1                        ISC_TIME                                Yes

TIMESTAMP        SQL_TIMESTAMP                ISC_TIMESTAMP                        No

TIMESTAMP        SQL_TIMESTAMP + 1        ISC_TIMESTAMP                        Yes

VARCHAR        SQL_VARYING                        Первые 2 байта: short содержащий

           длину символьной строки;

           хранит байты: char []                No

VARCHAR        SQL_VARYING + 1                           Первые 2 байта: short содержащий

          длину символьной строки;

          хранит байты: char []                        Yes

Примечание

DECIMAL и типы данных NUMERIC хранятся внутри БД как SMALLINT, INTEGER, DOUBLE PRECISION, или 64-разрядные целочисленные типы данных. Чтобы определить правильное макро выражение для  представления столбцов  DECIMAL или  NUMERIC, используйте isql, чтобы узнать определение столбца таблицы, и увидеть, как InterBase хранит данные столбца, а затем выбрать соответствующее макро выражение.

Информация о типе данных  параметра или элемента списка-select содержится в поле sqltype структуры XSQLVAR. Значение, содержащееся в sqltype предоставляет два вида информации:

·Тип данных параметра или select-списка.
·Используется ли sqlind для показа значения NULL. Если sqlind используется, его значение определяет, является ли параметр или элемент списка -select NULL (-1), или не NULL (0):

Например, если sqltype равняется SQL_TEXT, параметр или элемент списка -select  это CHAR, который не использует sqlind для проверки значения NULL (потому что  теоретически значения NULL не позволяются в этом случае). Если sqltype равняется SQL_TEXT + 1, то sqlind может быть проверен, чтобы увидеть, является ли параметр или элемент списка -select   NULL:

Выражение языка C, sqltype & 1, обеспечивает полезную проверку того, может ли параметр или элемент списка -select содержать NULL. Выражение равно 0, если параметр или элемент списка выбора не могут содержать NULL, и 1, если параметр или элемент списка выбора могут содержать NULL. Следующий фрагмент кода демонстрирует, как использовать это выражение:

if (sqltype & 1 == 0)
{
/* параметр не может содержать NULL */
}
else
{
/* параметр может содержать NULL*/
}

По умолчанию, и isc_dsql_prepare () и isc_dsql_describe () возвращают макро выражение type + 1, так что sqlind должен всегда проверяться на NULL  значения с этими инструкциями.

Обработка переменных строк данных

Типы данных VARCHAR, CHARACTER VARYING, и NCHAR VARYING  требуют осторожной обработки в DSQL. Первые два байта этих типов данных содержат  информацию о длине строки, а остаток от данных содержит саму строку данных, для обработки.

Чтобы избежать необходимости писать код, для извлечения и обработки строки переменной длины в приложении, можно привести эти типы данных к фиксированной длине, используя макрокоманды SQL.

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

Обработка типов данных NUMERIC и DECIMAL

Типы данных DECIMAL и NUMERIC внутри БД храняться как SMALLINT, INTEFER, DOUBLE

PRECISION, или 64-разрядное целый тип данных, в зависимости от точности и масштаба, определенного для столбца, который использует эти типы. Определите как значения  DECIMAL или значения NUMERIC фактически храняться в базе данных, используя isql, чтобы проверить определение столбца таблице. У NUMERIC данные фактически сохраняются как DOUBLE PRECISION.

Когда DECIMAL или значение NUMERIC храняться как SMALLINT, INTEGER, или 64-разрядное целое, значение хранится как целое число. Во время поиска в DSQL, поле sqlscale XSQLVAR устанавливается в отрицательное число, которое показывает коэффициент(фактор) 10  на который целое число (возвращенное в sqldata), должно быть разделено, чтобы получился правильный NUMERIC или значение DECIMAL с его дробной частью. Если sqlcale -1, то число должно быть разделено на 10, если это  -2, то номер должен быть разделен 100, -3 1000, и т.д.

Приведение типов данных

Иногда при обработке DSQL входных параметров и элементов списка выбора, желательно или необходимо привести один тип данных к другому. Этот процесс упомянут как приведение типов данных. Например, приведение типов часто используется, когда параметры или элементы списка выбора имеют тип VARCHAR.  Приведение данных из SQL_VARYING к SQL_TEXTможет быть упрощена. Приведение возможно только для совместимых типов данных. Например, SQL_VARYING к SQL_TEXT, или SQL_SHORT к SQL_LONG.

Приведение символьных типов данных

Чтобы привести SQL_VARYING  к SQL_TEXT , измените поле sqltype структуры XSQLVAR в параметре или  элементе списка выбора к желаемой макрокоманде типа данных SQL. Например, следующая инструкция предполагает, что var - указатель на структуру XSQLVAR, и что она содержит тип данных SQL_VARYING , нужно преобразовать) к SQL_TEXT:

var - > sqltype = SQL_TEXT;

После приведения символьного типа данных, обеспечьте надлежащее количество памяти для него. Поле sqllen структуры XSQLVAR содержит информацию о размере неприведенных данных. Установите поле sqldata структуры XSQLVAR равным адресу данных.

Приведение числовых типов данных

Чтобы привести один числовой тип данных к другому, измените поле sqltype поле  в XSQLVAR структуре  параметре или  элемента списка выбора к желательной макрокоманде типа данных SQL . Например, следующая инструкция предполагает, что var - указатель на структуру XSQLVAR, и что она содержит SQL_SHORT тип данных, преобразуем его к SQL_LONG:

var- > sqltype = SQL_LONG;

ВАЖНО Не приводите больший тип данных к меньшему. Данные могут быть потеряны при такой трансляции.

Установка NULL индикатора

Если параметр или элемент списка выбора содержат значение NULL, поле sqlind должно использоваться, чтобы индицировать состояние NULL. Соответствующая память должна быть выделена для sqlind заранее.

Перед вставкой установите sqlind к -1 показывая, что значения NULL являются правомерными. Иначе, установите sqlind к 0.

После выбора, sqlind -1 указывает, что поле содержит значение NULL. Другие значения указывают, что поле содержит не-NULL данные.

Выравнивание числовых данных(плохо переведен)

Обычно, когда переменная с числовым типом данных создана, компилятор будет гарантировать, что переменная сохранена в должным образом выровненном адресе, но когда числовые данные сохранены в динамически выделенном буфере,  программист должен делать предосторожности, чтобы гарантировать, что  память должным образом выровнена. Некоторые платформы, в особенности с процессорами RISC, требуют, чтобы числовые данные в динамически выделенных структурах памяти были выровнены должным образом в памяти. Выравнивание зависимо, и от типа данных  и от платформы. Например, short число на Sun SPARCstation должно быть расположено в адресе, делимом 2, в то время как long на той же самой платформе должен быть расположен в адресе, делимом 4. В большинстве случаев, элемент данных должным образом выровнен, если адрес его стартового байта делимый правильным номером выравнивания. Консультируйтесь с определенной системой и документацией компилятора для требований выравнивания.Полезное правило бегунка - то, что размер типа данных является всегда имеющим силу номером выравнивания для типа данных. Для данного типа T, если размер (T) равняется n, то адреса делимый n правильно выровнены для T. Следующее макро выражение может использоваться, чтобы выровнять данные:

#define ALIGN (ptr, n) ((ptr + n - 1) и ~ (n - 1))))

Где ptr - указатель на символ.Следующий код иллюстрирует, как макрокоманда ALIGN могла бы использоваться:

char *buffer_pointer, *next_aligned;

next_aligned = ALIGN(buffer_pointer, sizeof(T));

Методы программирования на DSQL

Существует четыре возможных метода программирования DSQL для обработки строк -инструкций SQL. Лучший метод для обработки строки зависит от типа инструкции SQL в строке, и содержит она или нет метки - заполнители для параметров. Следующая  таблица объясняет, как определить соответствующий метод обработки для SQL строки:

Это запрос?                Имеются ли метки-заполнители                Определения метода

Нет                        Нет                                                метод 1

Нет                        Да                                                метод 2

Да                        Нет                                                метод 3

Да                        Да                                                метод  4

Рассмотрим далее все четыре метода

Метод 1: Инструкция не является запросом и не содержит параметров

Cуществуют два способа обработки строки - инструкции SQL содержащей инструкции не являющиеся запросом и без параметров метки - заполнителя:

·Используйте isc_dsql_execute_immediate (), чтобы подготовить и выполнить строку сразу.
·Используйте isc_dsql_allocate_statement () чтобы выделить инструкцию для выполнения строки - инструкции, и isc_dsql_prepare (), чтобы пронализировать инструкцию для выполнения и назначить ей имя, затем используйте isc_dsql_execute () для выполнения инструкции как требуется в приложении.

Использование isc_dsql_execute_immediate( )

Для исполнения инструкции сразу, используется isc_dsql_execute_immediate():

1. Cоздайте строку содержащую инструкцию SQL. Например, следующая инструкция создает строку инструкции SQL:

 

char *str = "UPDATE DEPARTMENT SET BUDGET = BUDGET * 1.05";

  2. Проведите синтаксический анализ и выполните инструкцию, используя isc_dsql_execute_immediate ():

isc_dsql_execute_immediate(status_vector, &db1, &trans ,0, str, 1, NULL);

Примечание: isc_dsql_execute_immediate () можно использовать и так. Например:

isc_dsql_execute_immediate(status_vector, &db1, &trans, 0,

"UPDATE DEPARTMENT SET BUDGET = BUDGET * 1.05", 1, NULL);

Использование isc_dsql_prepare( ) и isc_dsql_execute( )

Исполнение инструкций по этапам используются isc_dsql_allocate_statement(),

isc_dsql_prepare(), и isc_dsql_execute():

1. Создаем строку инструкции SQL:

char *str = "UPDATE DEPARTMENT SET BUDGET = BUDGET * 1.05";

2. Объявляем и инициализируем дескриптор SQL инструкции, который выделяется с помощью isc_dsql_allocate_statement():

isc_stmt_handle stmt; /* Обьявление дескриптора инструкции. */

stmt = NULL; /* Установите дескриптор инструкции в NULL. */

. . .

isc_dsql_allocate_statement(status_vector, &db1, &stmt);

3. Анализируем строку ф-ей isc_dsql_prepare(). Она устанавливает дескриптор инструкции stmt к нужному формату. Этот дескриптор будет потом использован в вызове isc_dsql_execute():

isc_dsql_prepare(status_vector, &trans, &stmt, 0, str, 1, NULL);

Примечание: isc_dsql_prepare() также мо;жно вызывать и так.

isc_dsql_prepare(status_vector, &trans, &stmt, 0,

"UPDATE DEPARTMENT SET BUDGET = BUDGET * 1.05", 1, NULL);

4. Выполняем инструкцию используя isc_dsql_execute().

isc_dsql_execute(status_vector, &trans, &stmt, 1, NULL);

Метод 2: Инструкция не-запрос с параметрами

Существует два этапа  обработки строки -инструкции SQL содержащий инструкции не-запроса с параметрами метки - заполнителя:

1.Создание структуры ввода XSQLDA для обработки строки инструкции.
2.Подготовка и выполнения строки инструкции с параметрами.

Создание структуры ввода XSQLDA

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

1.Объявите переменную указывающую на XSQLDA с необходимыми для обработки параметрами. Например, следующее обьявление создает XSQLDA ввода на которую указывает in_sqlda:        XSQLDA *in_sqlda;
2.Объявите необязательную переменную для доступа к структуре XSQLVAR указатель на которую содержится в XSQLDA:
XSQLVAR *var;

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

3.Выделите память для XSQLDA используя макрос XSQLDA_LENGTH. Следующая инструкция выделяет память для XSQLDA и передает указатель на нее в переменную in_sqlda:

              in_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(10));

В этой инструкции выделяется место для 10 структур XSQLVAR, позволяя XSQLDA хранить до 10 параметров.

4.Установите поле версии XSQLDA в значение SQLDA_VERSION1, а полю sqln присвойте число зарезервированных структур XSQLVAR:

              in_sqlda->version = SQLDA_VERSION1;

          in_sqlda->sqln = 10;

Подготовка и выполнение инструкции с параметрами

После создания XSQLDA для передачи параметров в SQL инструкцию, SQL инструкция может быть создана и подготовлена. Локальные  переменные, соответствующие параметрам метки - заполнителя в строке должны быть присвоены соответствующим полям sqldata структурах XSQLVAR:

1.Создайте строку с параметрами метками-заполнителями, например:
 

           char *str = "UPDATE DEPARTMENT SET BUDGET = ?, LOCATION = ?";

           Эта инструкция содержит два параметра BUDGET и LOCATION.

2.Объявите и инициализируйте дескриптор SQL инструкции, место подкоторый выделяется с помощью isc_dsql_allocate():
 

          isc_stmt_handle stmt; /* Обьявите дескриптор инструкции. */

          stmt = NULL; /* Установите в NULL перед выделением памяти. */

          . . .

          isc_dsql_allocate_statement(status_vector, &db1, &stmt);

3.Проанализируйте строковую инструкцию с помощью isc_dsql_prepare (), она производит грамматический разбор SQL предложения. Она установит дескриптор инструкции (stmt) ко внутреннему представлению. Дескриптор инструкции используется в последующих запросах к isc_dsql_describe_bind () и isc_dsql_execute ():
 

            isc_dsql_prepare(status_vector, &trans, &stmt, 0, str, 1,in_sqlda);

4.Используйте isc_dsql_describe_bind () чтобы заполнить  XSQLDA ввода информацией о параметрах содержащихся в инструкции SQL:
 

           isc_dsql_describe_bind(status_vector, &stmt, 1, in_sqlda);

5.Сравните значение  поля sqln структуры XSQLDA со значением поля sqld, чтобы удостовериться, что выделено достаточно структур XSQLVAR для хранения информации о каждом параметре. Sqln должен быть по крайней мере таким же как sqld. Если не хватает памяти для предварительного размещения дескриптора вывода,  то перераспределте память заново таким образом, чтобы отразить число параметров, указанных в sqld, сбросьте sqln и version, установите и их снова, и выполните опять isc_dsql_describe_bind ():
               if (in_sqlda->sqld > in_sqlda->sqln)
              {
                  n = in_sqlda->sqld;
                 free(in_sqlda);
                 in_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(n));
                 in_sqlda->sqln = n
                 in_sqlda->version = SQLDA_VERSION1;
                 isc_dsql_describe_bind(status_vector, &stmt, 1, in_sqlda);
             }
6.Обработайте каждый XSQLVAR параметр в структуре XSQLDA. Это выглядит так:
a.Приведите типы данных  параметров (необязательно).
b.Выделите   память  для локальных данных, указанных полем sqldata XSQLVAR. Этот шаг только требуется, если память для локальных переменных выделено до времени выполнения. Следующий пример иллюстрирует динамическое выделение  памяти для локальных переменных:
c.Значение параметра должно быть совместимого типа данных(обязательно)
d.Определите  индикатор  NULL значения для параметра.
 

Следующий пример кода иллюстрирует эти шаги, выполненяет цикл для каждой структуры XSQLVAR принадлежащей in_sqlda XSQLDA:

for ( i=0, var = in_sqlda->sqlvar; i < in_sqlda->sqld; i++, var++ )
{
  /* Обрабатываем здесь каждую XSQLVAR структуру.
   Var указывает на структуру XSQLVAR. */
  dtype = (var->sqltype & ~1); /* drop NULL flag for now */
  switch(dtype)
  {
   case SQL_VARYING: /* приводит к SQL_TEXT*/
    var->sqltype = SQL_TEXT;
        /* выделяем память для хранения локальной переменной */
         var->sqldata = (char *)malloc(sizeof(char)*var->sqllen);
         . . .
         break;
       case SQL_TEXT:
          var->sqldata = (char *)malloc(sizeof(char)*var->sqllen);
    /* здесь присваивается значение параметру */
       . . .
     break;
       case SQL_LONG:
           var->sqldata = (char *)malloc(sizeof(long));
     /* присваивается значения параметру */
           *(long *)(var->sqldata) = 17;
     break;
     . . .
      } /* end of switch statement */
      if (var->sqltype & 1)
     {
          /* выделяется переменная для NULL индикатора */
           var->sqlind = (short *)malloc(sizeof(short));
     }
} /* конец цикла */
 

7.Выполните инструкцию. Пример:
 

      isc_dsql_execute(status_vector, &trans, &stmt, 1, in_sqlda);

 

Повторное выполнение инструкции

Как только строка инструкции не-запроса с параметрами подготовлена, она может быть выполнена столько раз сколько требуется в приложении. Перед каждым последующим выполнением, XSQLDA ввода может быть связан новым параметром и новым индикатором  NULL.

Метод 3: Инструкция запрос без параметров

Обработка инструкций запросов без параметров происходит за три шага:

1.Подготовьте XSQLDA вывода для обработки элементов списка выбора возвращенных при выполнении запроса.
2.Подготовьте строку инструкции.
3.Используйте курсор для выполнения инструкции и извлечения элементов списка выбора из XSQLDA вывода.

Подготовка XSQLDA для вывода данных.

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

1. Объявите  переменную типа XSQLDA которая будет хранить данные столбцов каждой строки, выбираемой из результатов запроса. Например, следующее объявление создает XSQLDA вывода, называемое out_sqlda:

XSQLDA *out_sqlda;

2. Объявите необязательную переменную для доступа к структуре XSQLVAR:

XSQLVAR *var;

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

3. Выделите память для XSQLDA используя макрос XSQLDA_LENGTH. Следующая инструкция выделяет память для хранения out_sqlda:

out_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(10));

В этой инструкции выделяется память для десяти структур XSQLVAR, позволяя разместить до 10 элементов списка выбора.

4. Установите поля  version в SQLDA_VERSION1 и поля sqln в число выделенных XSQLVAR:

out_sqlda->version = SQLDA_VERSION1;

out_sqlda->sqln = 10;

Подготовка инструкции запроса без параметров

После того, как XSQLDA создана для хранения элементов  возвращаемых инструкцией запроса,  строка инструкция может быть создана, подготовлена, и описана. Когда строка инструкция выполнена, InterBase создает список выбора  строк как результат запроса.

Подготовка строки запроса включает следующие шаги:

1. Создайте саму строку:

char *str = "SELECT * FROM CUSTOMER";

Инструкция, кажется,  имеет только один элемент списка выбора (*). Звездочка - символ подстановочных знаков, который замещает все столбцы в таблице, так что фактическое число возвращенных элементов равняется числу столбцов в таблице.

2. Объявите и инициализируйте дескриптор инструкции с помощью isc_dsql_allocate():

isc_stmt_handle stmt; /* Объявление дескриптора инструкции. */

stmt = NULL;

. . .

isc_dsql_allocate_statement(status_vector, &db1, &stmt);

3. Проанализируйте строку с помощью isc_dsql_prepare (), она выполняет грамматический разбор инструкции, и заполняет stmt. Дескриптор инструкции используется в последующих запросах к isc_dsql_describe_bind () и isc_dsql_execute ():

isc_dsql_prepare(status_vector, &trans, &stmt, 0, str, 1, NULL);

4. Используйте isc_dsql_describe () для заполения структуры XSQLDA вывода информацией об элементах списка выбора(столбцах), возвращаемых инструкцией:

isc_dsql_describe(status_vector, &stmt,1, out_sqlda);

5. Сравните значение  поля sqln структуры XSQLDA со значением поля sqld, чтобы удостовериться, что выделено достаточно структур XSQLVAR для хранения информации о каждом параметре. Sqln должен быть по крайней мере таким же как sqld. Если не хватает памяти для предварительного размещения дескриптора вывода,  то перераспределте память заново таким образом, чтобы отразить число параметров, указанных в sqld, сбросьте sqln и version, установите и их снова, и выполните опять isc_dsql_describe_bind ():

if (out_sqlda->sqld > out_sqlda->sqln)
{
   n = out_sqlda->sqld;
  free(out_sqlda);
  out_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(n));
  out_sqlda->sqln = n;
  out_sqlda->version = SQLDA_VERSION1;
  isc_dsql_describe(status_vector, &trans, 1, out_sqlda);
}

6. Обработайте каждый XSQLVAR параметр в структуре XSQLDA. Это выглядит так:

e.Приведите типы данных  параметров (необязательно).
f.Выделите   память  для локальных данных, указанных полем sqldata XSQLVAR. Этот шаг только требуется, если память для локальных переменных выделена до времени выполнения. Следующий пример иллюстрирует динамическое выделение  памяти для локальных переменных:
g.Значение параметра должно быть совместимого типа данных(обязательно)
h.Определите  индикатор  NULL - значения для параметра.

Следующий пример кода иллюстрирует эти шаги, и выполняет цикл для каждой структуры XSQLVAR принадлежащей out_sqlda XSQLDA:

for (i=0, var = out_sqlda->sqlvar; i < out_sqlda->sqld; i++, var++)
{
dtype = (var->sqltype & ~1); /* drop flag bit for now */
switch(dtype)
{
case SQL_VARYING:
var->sqltype = SQL_TEXT;
var->sqldata = (char *)malloc(sizeof(char)*var->sqllen + 2);
break;
case SQL_TEXT:
var->sqldata = (char *)malloc(sizeof(char)*var->sqllen);
break;
case SQL_LONG:
var->sqldata = (char *)malloc(sizeof(long));
break;
. . .
/* обработка других типов */
} /* конец инструкции switch */
if (var->sqltype & 1)
{
/* выделяем память для хранения NULL индикатора */
var->sqlind = (short *)malloc(sizeof(short));
}
} /* конец цикла */

Выполнение  строки инструкции в пределах контекста курсора

Чтобы осуществлять поиск элементов  списка выбора из подготовленной инструкции, строка должна быть выполнена в контексте курсора. Все объявления курсора в InterBase – это фиксированные инструкции, вставленные в приложение прежде, чем оно будет откомпилировано. Разработчики DSQL приложений  должны предупреждать потребность в курсорах при написании приложения и объявлять их заранее.

Курсор  необходим, чтобы обработать позиционированные инструкции UPDATE И DELETE, сделанные для строк, выбранных с помощью isc_dsql_fetch () для инструкций SELECT, которые определяют необязательное предложение FOR UPDATE OF.

Следующее описание рассматривает ситуации когда курсор необходим.

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

1. Выполните подготовленную инструкцию с помощью isc_dsql_execute():

isc_dsql_execute(status_vector, &trans, &stmt, 1, NULL);

2. Объявите и откройте курсор для инструкции с помощью isc_dsql_set_cursor_name(). К примеру, следующая инструкция объявляет курсор с именем «dyn_cursor», для SQL инструкции stmt:

isc_dsql_set_cursor_name(status_vector, &stmt,"dyn_cursor", NULL);

Открытие курсора выполняет инструкцию, и нужный набор строк будет найден

3. Выберите одну строку и сразу  обработайте элементы списка выбора (столбцы), которые она содержит с помощью isc_dsql_fetch (). Например, следующий цикл выбирает одну строку из dyn_cursor и сразу обрабатывает каждый элемент в выбранной строке  с помощью специфической для приложения функцией, называемой process_column ():

while ( ( fetch_stat =isc_dsql_fetch(status_vector, &stmt, 1, out_sqlda) ) == 0)
{
for (i = 0; i < out_sqlda->sqld; i++)
{
process_column(sqlda->sqlvar[i]);
}
}
if (fetch_stat != 100L)
{
/* isc_dsql_fetch возвращает 100 если нет больше строк для выбора*/
SQLCODE = isc_sqlcode(status_vector);
isc_print_sqlerror(SQLCODE, status_vector);
return(1);
}

Process_column () - функция упомянутая в этом примере обрабатывает каждый возвращенный элемент списка выбора. Следующий код иллюстрирует, как такая функция может быть написана:

void process_column(XSQLVAR *var)
{
/* проверка на NULL значение */
if ((var->sqltype & 1) && (*(var->sqlind) = -1))
{
/* Здесь определяется NULL значение */
}
else
{
/* обработка данных*/
}
. . .
}

4.  Когда все строки выбраны курсор закройте с помощью isc_dsql_free_statement():

isc_dsql_free_statement(status_vector, &stmt, DSQL_close);

Повторное выполнение инструкции запроса без параметров

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

Метод 4: Инструкция запроса с параметрами

По этим четырем шагам происходит обработка строки запроса с параметрами метками-заполнителями:

1. Подготовка структуры ввода XSQLDA для обработки строк параметров.

2. Подготовка структуры вывода XSQLDA для обработки элементов выборки возвращенных при выполнении запроса.

3. Подготовка строки инструкции и ее параметров.

4. Использование курсора выполняющего инструкцию использующую переменные входных параметров из структуры ввода XSQLDA, и  поиск элементов выборки из структуры вывода XSQLDA.

Подготовка структуры ввода XSQLDA

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

1.Объявите переменную указывающую на XSQLDA с необходимыми для обрабоки параметрами. Например, следующая декларация создает XSQLDA на который указывает in_sqlda:        XSQLDA *in_sqlda;
2.Объявите необязательную переменную для доступа к структуре XSQLVAR указатель на которую содержится в XSQLDA:
XSQLVAR *var;

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

3. Выделите память для XSQLDA используя макрос XSQLDA_LENGTH. Следующая инструкция выделяет память для XSQLDA и передает указатель на нее в переменную in_sqlda:

 

in_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(10));

 

В этой инструкции выделяется место для 10 структур XSQLVAR, позволяя XSQLDA хранить до 10 параметров.

 

4. Установите поле версии XSQLDA в значение SQLDA_VERSION1, а полю sqln присвойте число зарезервированных структур XSQLVAR:

     in_sqlda->version = SQLDA_VERSION1;

     in_sqlda->sqln = 10;

Подготовка структуры XSQLDA для вывода данных

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

1. Объявите  переменную типа XSQLDA которая будет хранить данные столбцов каждой строки, выбранной в запросе. Например, следующее объявление создает XSQLDA, называемое out_sqlda:

XSQLDA *out_sqlda;

2. Объявление необязательной переменной для доступа к структуре XSQLVAR:

XSQLVAR *var;

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

3. Выделите память для XSQLDA используя макрос XSQLDA_LENGTH. Следующая инструкция выделяет память для хранения out_sqlda:

out_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(10));

В этой инструкции выделяется память для десяти структур XSQLVAR, позволяя разместить до 10 элементов списка выбора.

4. Установите поля  version в SQLDA_VERSION1 и поля sqln в число выделенных XSQLVAR:

out_sqlda->version = SQLDA_VERSION1;

out_sqlda->sqln = 10;

Подготовка инструкции запроса с параметрами

После того как структуры XSQLDA ввода и вывода были созданы для передачи параметров строке инструкции, и принятия возвращенных элементов  списка выборки после исполнения инструкции, строка - инструкция может быть создана и подготовлена. Когда  строка инструкция подготовлена, InterBase заменяет параметры метки - заполнителя в строке информацией о фактических используемых параметрах. Информация о параметрах должна быть передана в структуру ввода XSQLDA (и возможно откорректирована) прежде, чем инструкция будет выполнена. Когда строка-инструкция выполнена, InterBase сохраняет элементы списка выбора в структуре вывода XSQLDA.

Подготовка SQL инструкции с метками-заполнителями идет следующими шагами:

1.  Создайте саму строку с SQL инструкцией.

char *str = "SELECT * FROM DEPARTMENT WHERE BUDGET = ?,LOCATION = ?";

Эта инструкция содержит два параметра: значение связанное со столбцом BUDGET и значение связанное со столбцом LOCATION.

2. Объявите и инициализируйте дескриптор SQL инструкции с помощью

isc_dsql_allocate():

isc_stmt_handle stmt;

stmt = NULL;

. . .

isc_dsql_allocate_statement(status_vector, &db1, &stmt);

3. Подготовьте строку  с помощью isc_dsql_prepare (). Она устанавливает дескриптор инструкции (stmt) в правильную синтаксическую форму. Дескриптор инструкции используется в последующих вызовах к isc_dsql_describe (), isc_dsql_describe_bind (), и isc_dsql_execute2 ():

isc_dsql_prepare(status_vector, &trans, &stmt, 0,str, 1, out_xsqlda);

4. Используйте isc_dsql_describe_bind () чтобы заполнить структуру ввода XSQLDA информацией о параметрах содержащихся в инструкции SQL:

isc_dsql_describe_bind(status_vector, &stmt, 1, in_xsqlda);

5. Сравните поле sqln структуры XSQLDA с полем sqld, чтобы определить может ли  дескриптор  ввода принять то число параметров содержащихся в инструкции. Если нет, освободите  предварительно выделеннeю память для  дескриптора ввода, и заново перераспределите память чтобы отразить число параметров указанных sqld, сбросьте поля sqln и version и выполните isc_dsql_describe_bind () снова:

if (in_sqlda->sqld > in_sqlda->sqln)
{
n = in_sqlda->sqld;
free(in_sqlda);
in_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(n));
in_sqlda->sqln = n;
in_sqlda->version = SQLDA_VERSION1;
isc_dsql_decribe_bind(status_vector, &stmt, 1, in_xsqlda);
}

6. Обработайте каждый XSQLVAR параметр структуры ввода XSQLDA. Обработка  параметров структуры идет в четыре шага:

a.Приведите типы данных  параметров (необязательно).
b.Выделите   память  для локальных данных, указанных полем sqldata XSQLVAR. Этот шаг только требуется, если память для локальных переменных выделена до времени выполнения. Следующий пример иллюстрирует динамическое выделение  памяти для локальных переменных:
c.Значение параметра должно быть совместимого типа данных(обязательно)
d.Определите    NULL значение индикатора для параметра

Эти шаги должны быть представлены в следующем порядке. Следующий пример кода иллюстрирует эти шаги, и выполняет цикл для каждой структуры XSQLVAR в in_sqlda XSQLDA:

for (i=0, var = in_sqlda->sqlvar; i < in_sqlda->sqld; i++, var++)
{
/* Обработайте каждый XSQLVAR параметр здесь. 
Var – указатель на структуру параметр*/
dtype = (var->sqltype & ~1); 
switch(dtype)
{
case SQL_VARYING: /* приведение к  SQL_TEXT */
var->sqltype = SQL_TEXT;
/* allocate proper storage */
var->sqldata = (char *)malloc(sizeof(char)*var->sqllen);
/* Предоставьте значение для параметра. See case SQL_LONG. */
. . .
break;
case SQL_TEXT:
var->sqldata = (char *)malloc(sizeof(char)*var->sqllen);
/* Provide a value for the parameter. See case SQL_LONG. */
. . .
break;
case SQL_LONG:
var->sqldata = (char *)malloc(sizeof(long));
/* Присваиваем занчение пармтру. */
*(long *)(var->sqldata) = 17;
break;
. . .
} /* конец  инструкции switch */
if (sqltype & 1)
{
/* Резервирует переменную для хранения индикатора NULL */
var->sqlind = (short *)malloc(sizeof(short));
}
} /* end of for loop */

7. Используйте isc_dsql_describe () чтобы заполнить структуру вывода XSQLDA информацией о элементах списка выбора, возвращаемых инструкцией:

isc_dsql_describe(status_vector, &trans, &stmt, out_xsqlda);

8. Сравните поле sqln структуры XSQLDA с полем sqld, чтобы определить может ли дескриптор вывода хранить число элементов списка выбора, указанных в инструкции. Если нет, освободите выделенную предварительно память  для дескриптору вывода, затем перераспределите так память чтобы отразить число элементов списка выбора, указанных sqld, сбросьте sqln и version, и выполните все для output  снова:

if (out_sqlda->sqld > out_sqlda->sqln)
{
n = out_sqlda->sqld;
free(out_sqlda);
out_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(n));
out_sqlda->sqln = n;
out_sqlda->version = SQLDA_VERSION1;
isc_dsql_describe(status_vector, &trans, &stmt, out_xsqlda);
}

9. Подберите структуру XSQLVAR для каждого возвращаемого  элемента. Следующие шаги описывают этот процесс:

e.Приведите типы данных  параметров (необязательно).
f.Выделите   память  для локальных данных, указанных полем sqldata XSQLVAR. Этот шаг только требуется, если память для локальных переменных выделена до времени выполнения. Следующий пример иллюстрирует динамическое выделение  памяти для локальных переменных:
g.Значение параметра должно быть совместимого типа данных(обязательно)
h.Определите  индикатор  NULL значения для параметра.

Следующий пример кода иллюстрирует эти шаги, выполняет цикл для каждой структуры XSQLVAR принадлежащей out_sqlda XSQLDA:

for (i=0, var = out_sqlda->sqlvar; i < out_sqlda->sqld; i++, var++)
{
dtype = (var->sqltype & ~1);/
switch(dtype)
{
case SQL_VARYING:
var->sqltype = SQL_TEXT;
break;
case SQL_TEXT:
var->sqldata = (char *)malloc(sizeof(char)*var->sqllen);
break;
case SQL_LONG:
var->sqldata = (char *)malloc(sizeof(long));
break;
/* обработка других типов */
} /* конец swicth */
if (var->sqltype & 1)
{
var->sqlind = (short *)malloc(sizeof(short));
}
} /* конец цикла */

Выполнение инструкции запроса в контексте курсора

Чтобы возвратить элементы списка выборки строки инструкции, строка должна быть выполнена в пределах контекста курсора. Все объявления курсора в InterBase фиксированы, встроенные инструкции вставлены  в приложение прежде, чем оно будет откомпилировано. Разработчики DSQL приложений должны предусмотреть потребность в курсорах при написании приложения и объявлять их заранее.

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

1. Выполните инструкцию с помощью isc_dsql_execute2():

isc_dsql_execute2(status_vector, &trans, &stmt, 1,in_xsqlda, out_xsqlda);

Примечание от автора:

    При работе с ф-ей isc_dsql_execute2 существует хитрость не указанная в справочнике и служащая иточником ошибок. Если возвращается набор строк и необходимо открытие курсора то вместо out_sqlda надо передавать NULL иначе вернется ошибка. Если заранее известно что вернется одна и только одна строка, то надо передавать out_sqlda как показано в примере, но не создавать курсор. А просто обработать после отработки функции структуру вывода. Так что в этом примере не верно произведен вызов ф-ии.

2. Объявлите и откройте курсор для инструкции строки с помощью

isc_dsql_set_cursor_name(). К примеру, следующая инструкция объявляет курсор dyn_cursor, и для подготовки строки SQL инструкции stmt:

isc_dsql_set_cursor_name(status_vector, &stmt, "dyn_cursor", NULL);

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

3. Выберите строки с помощью isc_dsql_fetch () и сразу обработайте элементы списка выбора (столбцы), которые они содержит. Например, следующие цикл делает выбор одной строк  из dyn_cursor и сразу обрабатывают каждый элемент в выбранной строке с помощью определенной для приложения функцией, называемой process_column ():

while ( ( fetch_stat =isc_dsql_fetch(status_vector, &stmt, 1, out_sqlda) ) == 0 )
{
for (i = 0; i < out_sqlda->sqld; i++)
{
process_column(sqlda->sqlvar[i]);
}
}
 
if (fetch_stat != 100L)
{
/* isc_dsql_fetch возвращает 100, если нет больше строк оставшихся для выбора */
SQLCODE = isc_sqlcode(status_vector);
isc_print_sqlerror(SQLCODE, status_vector);
return(1);
}

4. Когда все строки выбраны закройте курсор с помощью isc_dsql_free_statement():

isc_dsql_free_statement(status_vector, &stmt, DSQL_close);

Повторное выполнение инструкции

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

Определение неизвестного типа инструкции во время выполнения

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

Запрашиваемая информация может включать:

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

Чтобы использовать isc_dsql_sql_info (), выделите буфер списка элементов, который описывает тип требуемой информации, и выделите буфер результатов, куда функция может возвращать нужную информацию. Например, чтобы определить  неизвестный тип инструкции, но подготовленной инструкции, вы должны выделить буфер списка элементов с одним элементом, и заполнять его макро константой  isc_info_sql_stmt_type, определенной в ibase.h:

char type_item[];

type_item[] = {isc_info_sql_stmt_type};

Обратите внимание, что дополнительная информация о макросах для требуемых элементов может быть найдена в ibase.h по комментарию, “ SQL information items."

Буфер результатов должен быть достаточно большим, чтобы содержать любые данные, возвращенные запросом. Надлежащий размер для этого буфера зависит от требуемой информации. Если не достаточно выделенной памяти куда isc_dsql_sql_info () помещает предопределенное значение, то в последний байт буфера результатов помещается isc_info_truncated. Вообще, когда запрашивается информация о типе инструкции то 8 байт - достаточный размер буфера. Объявление размера больше чем необходимо буферу безопасно. Запрос  идентифицирующий тип инструкции возвращает следующую информацию в буфер результатов:

1. Первый байт содержит isc_info_sql_stmt_type.

2. Второй байт содержит число n говорящее о том сколько байт занимает следующее значение.

3. Один или два байта определяют  тип инструкции. Следующая таблица показывает возвращаемые типы инструкций:

 

Тип                                        Числовое значение

isc_info_sql_stmt_select                        1

isc_info_sql_stmt_insert                        2

isc_info_sql_stmt_update                        3

isc_info_sql_stmt_delete                        4

isc_info_sql_stmt_ddl                        5

isc_info_sql_stmt_get_segment                6

isc_info_sql_stmt_put_segment                7

isc_info_sql_stmt_exec_procedure                8

isc_info_sql_stmt_start_trans                9

isc_info_sql_stmt_commit                        10

isc_info_sql_stmt_rollback                        11

isc_info_sql_stmt_select_for_upd                12

4. Последний байт, содержит значение isc_info_end (0).

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

Обратите внимание, что вся информация относительно инструкции кроме ее типа может быть более легко определена,  вызывав другие функции кроме isc_dsql_sql_info (). Например, чтобы определить информацию, для заполнения структуры ввода XSQLDA, вызовите isc_dsql_describe_bind (). Чтобы заполнить структуру вывода XSQLDA, вызовите isc_dsql_prepare () или isc_dsql_describe ().

Работа с преобразованиями типов

InterBase использует свой формат для внутреннего хранения данных типов TIMESTAMP, TIME, и  DATE, но предоставляет следующие вызовы API ф-й для трансляции в эти форматы и обратно:

·isc_decode_sql_date() конвертирует внутренний формат даты IB  к формату С структуры date
·isc_encode_sql_date() делает тоже, но обратно
·isc_decode_sql_time() конвертирует внутренний формат времени IB к формату С структуры time
·isc_encode_sql_time() делает тоже, но обратно
·isc_decode_timestamp() конвертирует внутренний формат timestamp IB к формату С структуры timestamp ; Это прежде выполнялось вызовом isc_decode_date ();
·isc_encode_timestamp() делает тоже, но обратно; Это прежде выполнялось вызовом isc_encode_date ();

Эти вызовы просто транслируют данные типа datetime (DATE, TIME, и TIMESTAMP)  в другие форматы; они не делают непосредственно чтения или записи данных типа datetime . Данные типа datetime читаюся и пишутся в базу данных, при использовании стандартного   синтаксиса DSQL, обрабатываемого с помощью семейства вызовов API  isc_dsql.

Примечание

В InterBase 6 тип данных DATE содержит только информацию о дате в диалекте 3 и не доступен в диалекте 1, чтобы избежать неоднозначности. Когда старая база данных перенесеносится к версии 6 диалект 1, все столбцы, которые предварительно имели тип данных DATE,  автоматически преобразовываются в TIMESTAMP. Чтобы хранить перенесенные данные в столбце DATE в диалекте 3, Вы должны создать новый столбец в диалекте 3, который имеет тип данных DATE, и затем переместить данные в него. InterBase не позволяет Вам использовать ALTER COLUMN, чтобы изменить тип данных TIMESTAMP в тип данных DATE из-за возможной потери данных.

InterBase также требует, чтобы числа, введенные в базу данных и  буфера параметров транзакций были в универсальном формате, с самым последним знаковым байтом. Знаковые числа требуют, чтобы  знак был в последнем байте. Системы, которые представляют числа со старшим байтом последним, должны использовать isc_vax_integer (), чтобы полностью изменить порядок байт чисел, введенных в буфер параметров баз данных (DPBs) и  буфера параметров транзакций (TPBs).

Преобразование даты и времени из InterBase в формат C

Следующие шаги показывают, как преобразовать тип данных TIMESTAMP из InterBase формата в  формат C; те же самые шаги  используются, чтобы преобразовать типы данных TIME И DATE. Начиная с InterBase 6, тип данных TIMESTAMP заменяет старый тип данных DATE, используемый в более ранних версиях .

Для выборки timestamp из таблицы, и преобразовывания его к форме пригодной для использования в C программах, следуют этими шагами:

1. Создайте  переменную для C time структуры. Большинство C и систем разработки программ C++ обеспечивает тип, struct tm, для C time структур в time.h файле заголовка. Следующий код C включает этот файл заголовка, и объявляет переменную типа struct tm:

#include <time.h>

#include <ibase.h>

. . .

struct tm entry_time;

. . .

2. Создайте переменную типа ISC_TIMESTAMP.

ISC_TIMESTAMP entry_date;

ISC_TIMESTAMP это структура объявленная в ibase.h.

3. Получите данные из таблицы в переменную типа ISC_TIMESTAMP

4. Преобразуйте эту переменную в числовой формат C с помощью InterBase функции isc_sql_decode_timestamp(). Она также обьялена в ibase.h, и она требует двух параметров, адреса переменной типа ISC_TIMESTAMP, и адреса struct tm переменной. Например, следующий фрагмент кода преобразует entry_date к entry_time:

isc_decode_timestamp(&entry_date, &entry_time);

Преобразование даты из формата С в формат InterBase

1. Создайте переменную для С time структуры .

#include <time.h>;

. . .

struct tm entry_time;

. . .

2. Создайте переменную типа ISC_TIMESTAMP для использования InterBase.

ISC_TIMESTAMP mytime;

3. Запишите дату в entry_time.

4. Используйте функцию isc_encode_sql_date() для преобразования entry_time в  mytime.

isc_encode_timestamp(&entry_time, &entry_date);

5. И выполните вставку даты в таблицу

Обработка ошибок

Эта глава описывает, как устанавливать вектор состояния ошибки, где InterBase может сохранять информацию об ошибке во время выполнения, и как использовать функции API, чтобы обрабатывать ошибки и сообщать о них. Следующая таблица содержит функции API для обработки ошибок:

Функция

Что делает

isc_interprete()

Передает данные об ошибках IB в буфер

isc_print_sqlerror()

Выводит сообщение об SQL ошибке

isc_print_status()

Выводит сообщение об ошибке IB

isc_sqlcode()

Устанавливает значение SQLCODE

isc_sql_interprete()

Передает данные об ошибках SQL в буфер

Установка вектора состояния ошибки

Большинство функций API возвращает информацию о состоянии, которое показывает успех или неудачу. Возвращенная информация берется из второго элемента массива вектора состояния ошибки, где InterBase сообщает о состояниях ошибки. Вектор состояния ошибки объявлен в приложениях как массив 20 длинных целых чисел, используя следующий синтаксис:

#include <Ibase.h>

...

ISC_STATUS status_vector [20];

ISC_STATUS определен директивой #define в ibase.h, и предоставляет удобство программирования и независимость от платформы

Использование информации в векторе состояния

Происходит или нет ошибка в течение выполнения вызова API, InterBase загружает вектор состояния ошибки информацией о состоянии вызова. Информация состоит из одного или нескольких  кодов ошибки InterBase, и информации об ошибке  может использоваться, чтобы сформировать сообщение об ошибках.

Приложение может проверять вектор состояния после выполнения большинства вызовов API, чтобы определить их успех или неудачу. Если состояние ошибки возвращено, приложения могут:

- Отобразить сообщения об ошибках InterBase, используюя isc_print_status ().

- Установить значение SQLCODE, соответствующее ошибке InterBase, используя isc_sqlcode (), и отобразить SQLCODE и сообщение об ошибках SQL, используя isc_print_sqlerror ().

- Формировать индивидуальные сообщения об ошибках InterBase в буфере используя isc_interprete (). Буфер должно предварительно создать приложение. Использование буфера позволяет приложению исполнить дополнительную обработку сообщения (например, сохранение сообщений в файле регистрации ошибок). Эта способность особенно полезна на системах управления окнами, которые не разрешают прямые экранные записи.

- Фиксировать сообщение об ошибках SQL в буфере с isc_sql_interprete (). Буфер должно предварительно создать приложение.

- Производить синтаксический анализ и реагировать на определенные ошибки  InterBase в векторе состояния.

Проверка вектора состояния на ошибки

Функции API, которые возвращают информацию в векторе состояния,  объявлены в ibase.h как возвращающие указатели на ISC_STATUS. Например,  прототип функции для isc_prepare_transaction () объявлен как:

ISC_STATUS ISC_EXPORT isc_prepare_transaction(ISC_STATUS ISC_FAR *, isc_tr_handle ISC_FAR *);

Чтобы проверить вектор состояния на состояние ошибки после выполнения функции, исследуйте первый элемент вектора состояния, чтобы узнать, установлен он в 1, и если так, исследуйте второй элемент, чтобы узнать, что он не 0. Значение отличное от нуля во втором элементе указывает на состояние ошибки. Следующий фрагмент кода на C иллюстрирует, как проверить вектор состояния на состояния ошибки:

#include <ibase.h>
. . .
ISC_STATUS status_vector[20];
. . .
/* Здесь предполагаемый вызов API возвращающий  состояние ошибки. */
if (status_vector[0] == 1 && status_vector[1] > 0)
{
/* Здесь описание состояния ошибки */
;
}

Если ошибка обнаружена, Вы можете использовать функции API в подпрограмме обработки ошибок, чтобы отобразить сообщения об ошибках, фиксировать сообщения об ошибках в буфере, или анализировать вектор состояния для специфических кодов ошибки.

Вывод или сбор данных сообщений об ошибках - только одна часть подпрограммы обработки ошибок.

Обычно, эти подпрограммы делают откат транзакции, и иногда они могут повторять операцию.

Отображение сообщений об ошибках InterBase

Используйте isc_print_status () чтобы отобразить сообщения об ошибках InterBase на экране. Эта функция анализирует вектор состояния, чтобы формировать все доступные сообщения об ошибках, затем использует C printf () функцию, чтобы вывести сообщения на дисплей. Isc_print_status () требует одного параметра, указателя на вектор состояния содержащий информацию об ошибке. Например, следующий фрагмент кода вызывает isc_print_status () и делает откат транзакции при ошибке

#include <ibase.h>.
. . .
ISC_STATUS status_vector[20];
isc_tr_handle trans;
. . .
trans = 0L;
. . .
/* Предпорлагаемая транзакция стартует здесь. */
/* Здесь предполагаемый вызов API возвращающий  состояние ошибки. */
if (status_vector[0] == 1 && status_vector[1] > 0)
{
isc_print_status(status_vector);
isc_rollback_transaction(status_vector, &trans);
}

На системах управления окнами, которые не разрешают, прямые экранные записи с printf (), используют isc_interprete () чтобы фиксировать сообщения об ошибках в буфере.

Для приложений, которые используют,  динамические функции SQL (DSQL) API, ошибки должны быть отображены, используя соглашения SQL. Используйте isc_sqlcode () и isc_print_sqlerror () вместо isc_print_status ().

Фиксация сообщений об ошибках InterBase

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

- Работают под системами управления окнами, которые не разрешают прямые экранные записи.

- Требуют, чтобы большего контроля над выводом сообщения об ошибке чем это было возможно с isc_print_status ().

- Сохраняют отчет о всех об ошибках в журнале.

- Управляют сообщениями или форматируют сообщения об ошибках для вывода, или передают их  подпрограммам отображения системам работы с окнами.

Isc_interprete () возвращает и форматирует отдельное сообщение об ошибках, каждый раз когда она вызывается. Когда ошибка происходит, вектор состояния обычно содержит больше чем одно сообщение об ошибках. Чтобы отыскивать все уместные сообщения об ошибках, Вы должны выполнять повторные вызовы  isc_interprete ().

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

Никогда не передавайте массив векторов состояний непосредственно  isc_interprete (). Каждый раз при вызове, isc_interprete () продвигает указатель на вектор состояния к следующему элементу, содержащему новую информацию сообщения. Перед запросом isc_interprete (), убедитесь, что установили указатель на начальный адрес вектора состояния.

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

#include <ibase.h>
. . .
ISC_STATUS status_vector[20];
isc_tr_handle trans;
long *pvector;
char msg[512];
FILE *efile; /* Фрагмент кода предполагаемого открытия файла */
trans = 0L;
. . .
/*Здесь начинается подпрограмма обработки ошибок. */
/* Всегда настраивайте pvector на указатель на status_vector. */
pvector = status_vector;
/* Получаем первое сообщение */
isc_interprete(msg, &pvector);
/* Пишем первое сообщение из буфера в log файл. */
fprintf(efile, "%s\n", msg);
msg[0] = ’-’; /* Добавляем в конец дефис для второго сообщения */
/* Ищем другие сообщения в цикле */
while(isc_interprete(msg + 1, &pvector)) /* Дальше? */
fprintf(efile, "%s\n", msg); /* Если так то пишем в log файл. */
fclose(efile); /* Все закончили закрываем Log файл */
isc_rollback(status_vector, &trans);
return(1);
. . .

Заполнение SQLCODE значением ошибки

стр 176-178, (Не переведены так как SQLCODE не нужны и устарели)

Анализ вектора состояния ( статус-вектор )

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

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

Как анализируется статус вектор

Подпрограммы обработки ошибок InterBase, isc_print_status () и isc_interprete (), используют подпрограммы, которые автоматически анализируют информацию сообщений об ошибках в векторе состояния без требования знаний о структуре статус-вектора. Если Вы планируете писать ваши собственные подпрограммы, чтобы читать статус-вектор и реагировать на его содержание, то Вы должны знать, как интерпретировать это содержание.

The key to parsing the status vector is to decipher the meaning of the first long in each cluster, beginning with the first cluster in the vector.

Значение первого длинного целого в кластере

Первое длинное целое в любом кластере - числовой дескриптор. Проверяя числовой дескриптор для любого кластера Вы можете всегда определять:

- Число длинных целых в кластере.

- Вид информации, содержащейся в оставшейся части кластера.

- Начальное местоположение следующего кластера в векторе состояния.

Интерпретация первого длинного целого числа

Длинное целое в

Значение

кластер

Интерпетация

0

-

Конец информации об ошибке в статус-векторе

1

2

Второе long в коде ошибки

2

2

Второй long это адрес строки, используемой как переменный параметр в универсальном сообщении об ошибках InterBase

3

3

Второй long это длина, в байтах, строки переменной длины, предоставляемой операционной системой (наиболее часто эта строка - имя файла); третье long - адрес строки

4

2

Второй long это число используемое как переменный параметр в универсальном сообщении об ошибках

5

2

Второй long - адрес строки сообщения об ошибках не требующий никакой дальнейшей обработки перед выводом

6

2

Второе длинное есть код ошибки VAX/VMC

7

2

Второе длинное есть код ошибки UNIX

8

2

Второе длинное есть код ошибки Apollo Domain

9

2

Второе длинное есть код ошибки MS-DOS или OS/2

10

2

Второе длинное есть код ошибки HP MPE/XL

11

2

Второе длинное есть код ошибки HP MPE/XL IPC

12

2

Второе длинное есть код ошибки NeXT/Mach

Примечание: По мере адатации InterBase к другим платформам и другому оборудованию в этот список добавяться новые значения

Включив ibase.h в начало вашего исходного текста, Вы можете использовать серии #defines, чтобы заменить на жестко закодированные числовые дескрипторы в векторе состояния для подпрограмм синтаксического анализа которые Вы напишите. Преимущества использования этих #defines следующие:

- Ваш код будет более легким для чтения.

- Сопровождение кода будет проще, схема нумерации числовых дескрипторов изменится в будущем выпуске InterBase.

- Следующая таблица перечисляет #define эквиваленты каждого числового дескриптора:

Значение

#define

Значение

#define

0

isc_arg_end

8

isc_arg_domain

1

isc_arg_gds

9

isc_arg_dos

2

isc_arg_string

10

isc_arg_mpexl

3

isc_arg_cstring

11

isc_arg_mpexl_ipc

4

isc_arg_number

12

isc_arg_next_mach

5

isc_arg_interpreted

13

isc_arg_netware

6

isc_arg_vms

14

isc_arg_win32

7

isc_arg_unix

 

 

Значение второго long в кластере

В  второй long в кластере - всегда один из пяти элементов:

- Код ошибки InterBase (1-ый long = 1).

- Адрес строки(1-ый long = 2 или 5).

- Длина строки(1-ый long = 3).

- Числовое значение (1-ый long = 4).

- Код ошибки операционной системы (1-ый long > 5).

Ошибочные коды Interbase

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

Во-вторых, когда Вы пишете вашу собственную подпрограмму обработки ошибок, Вы можете проверять вектор состояния, напрямую, обрабатывая и реагируя на определенные коды ошибок InterBase.

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

Если Вы пишите ваши собственные подпрограммы синтаксического анализа, Вы могут были должны исследовать и использовать эти дополнительные кластеры информации ошибки.

СТРОКОВЫЕ АДРЕСА

Строка адресуется указателем текста сообщения об ошибках. Когда первый элемент в кластере равен 2 (isc_arg_string), адрес указывает часто на имя базы данных, таблицы, или столбца, на который воздействует ошибка. В этих случаях, функции InterBase, которые формируют строку сообщения об ошибках заменяют параметр в универсальном сообщении об ошибках InterBase строкой, указанной этим адресом. В другихслучаях адрес указывают на сообщение об ошибках, жестко закодированное в триггеах базы данных.

Когда первый элемент в кластере равен 5 (isc_arg_interpreted), то адрес указывает на текстовое сообщение, которое не требует никакой дальнейшей обработки перед выодом. Иногда это сообщение может быть жестко закодировано в InterBase непосредственно, и может быть сообщение об ошибках  системы.

В любом из этих случаев, функции InterBase типа isc_print_status () и isc_interprete () могут форматировать и отображать заканчивающееся сообщение об ошибках для Вас.

181-182 не переведена

Пример анализа статус-вектора

Следующий пример C иллюстрирует простой пример, анализ  "в лоб" вектора состояния. При возникновении ошибки блок обработки ошибок анализирует кластер массива вектора состояния, печатая содержание каждого кластера и интерпретируя его для Вас.

#include <ibase.h>
. . .
ISC_STATUS status_vector[20];
main()
{
 int done, v; /* если аргументов нет то 1, есть 0?, индекс статус-вектора */
 int c, extra; /* счетчик кластеров, 3 long cluster flag */
 static char *meaning[] = {"End of error information",
                           "n InterBase error code",
                           " string address",
                           " string length",
                           " numeric value",
                           " system code"};
/* здесь устанавливается соединение с БД и начинается транзакция */
 if (status_vector[0] == 1 && status_vector[1] > 0) error_exit();
  . . .
}
 
void error_exit(void)
{
 done = v = 0;
 c = 1;//анализ начинаем с первого кластера
 while (!done)
 {
  extra = 0;
  printf("Cluster %d:\n", c);//выводит номер кластера
    //выводит индекс и содержимое кластера по индексу
  printf("Status vector %d: %ld: ", v, status_vector[v]);
  if (status_vector[v] != gds_arg_end)//если не достигнут конец  аргументов, выводим сообщение
        printf("Next long is a");
  switch (status_vector[v++])
  {
   case gds_arg_end:
    printf("%s\n", meaning[0]);//если встретили конец аргументов, то покидаем цикл
    done = 1;
    break;
   case gds_arg_gds:
    printf("%s\n", meaning[1]);
    break;
   case gds_arg_string:
   case gds_arg_interpreted:
    break;
   case gds_arg_number:
    printf("%s\n", meaning[4]);
    break;
   case gds_arg_cstring:
    printf("%s\n", meaning[3]);
    extra = 1;
    break;
   default:
    printf("%s\n", meaning[5]);
    break;
  }
 
 if (!done)
 {
  printf("Status vector %d: %ld", v, status_vector[v]);
  v++;/* передвинуть указатель вектора */
  c++;/* увеличить счетчик кластера */
  if (extra)
  {
   printf(": Next long is a %s\n", meaning[2]);
   printf("Status vector: %d: %ld\n\n", v,
   status_vector[v]);
   v++;
  }
  else
   printf("\n\n");
 }
}
 
 isc_rollback_transaction(status_vector, &trans);
 isc_detach_database(&db1);
 return(1);
}

Здесь пример вывода результатов этой программы

Cluster 1:

Status vector 0: 1: Next long is an InterBase error code

Status vector 1: 335544342

Cluster 2:

Status vector 2: 4: Next long is a numeric value

Status vector 3: 1

Cluster 3:

Status vector 4: 1: Next long is an InterBase error code

Status vector 5: 335544382

Cluster 4:

Status vector 6: 2: Next long is a string address

Status vector 7: 156740

Cluster 5:

Status vector 8: 0: End of error informationРабота с BLOB данными

 

Эта глава описывает тип данных переменной длины, называемый BLOB (Binary Large Object, большой двоичный обьект) . Обычно этот тип данных применяется для хранения изображений, аудио- , видео- информации и вообще может применяться для любых типов данных. Если хочется как то преобразовывать BLOB, то для этого существуют специальные функции называемые BLOB фильтры, их можно даже писать самому.

Следующая таблица представляет в алфавитном порядке функции используемые для работы с BLOB.

Функция

Описание

1

isc_blob_default_desc()

Загружает BLOB дескриптор с информацией по умолчанию о BLOB, включающую инфо подтипах, кодовой табл, и размере сегмента BLOB.

2

isc_blob_gen_bpb()

Создает буфер параметров (BPB) для исходного и целевого BLOB дескрипторов и разрешает динамический доступ к BLOB подтипу и кодовой таблице символов.

3

isc_blob_info()

Возвращает информацию о открытом BLOBE.

4

isc_blob_lookup_desc()

Определяет подтип, набор символов, и размер сегмента BLOB, учитывая название таблицы и название столбца BLOB.

5

isc_blob_set_desc()

Инициализирует дескриптор BLOB из переданных ей параметров

6

isc_cancel_blob()

Отмена  BLOB

7

isc_close_blob()

Закрытие открытого BLOB

8

isc_create_blob2()

Создает и открывает BLOB для  записи, и при желании пользователя определяет фильтр, который нужно использовать, чтобы конвертировать BLOB из одного подтипа в другой

9

isc_get_segment()

Возвращает данные из BLOB столбца в строке, возвращенной выполнением инструкции SELECT

10

isc_open_blob2()

Открывает существующий  BLOB для  поиска, и при желании пользователя определяет фильтр, который нужно использовать, чтобы конвертировать BLOB из одного подтипа в другой

11

isc_put_segment()

Пишет данные в BLOB

 

Кроме управления BLOB данными обычными способами, подобными управлению другими типами данных, Interbase предоставляет  более гибкие правила  типов данных для данных BLOB. Поскольку существует много собственных типов данных разработчика, то вы можете определять их как данные BLOB, Interbase работает с ними как со своими и позволяет Вам определять ваш собственный тип данных, называемый подтип BLOB. Interbase также предоставляет два своих предопределенных подтипа: 0, неструктурированный подтип, вообще применимый к любым двоичным данным или данным неопределенного типа, и 1, применимый  к простому тексту.

 

Пользовательские данные должны быть всегда представлены как отрицательные числа от –1 до –32678. Подтип BLOB определяет как определен BLOB столбец.

Приложение ответственно за то, чтобы данные, хранимые в столбце BLOB согласовывались с его подтипом; Interbase не проверяет тип или формат данных BLOB.

Конечно чем  хранить данные Blob непосредственно в поле Blob записи таблицы, InterBase хранит там Blob ID. Blob ID является уникальным числовым значением которое ссылается на данные Blob. Данные Blob хранятся в другом месте в базы данных, в ряде сегментов Blob, по сегментам и осуществляется чтение и запись BLOB. Сегменты Blob могут иметь изменяющуюся длину. Длина индивидуального сегмента определяется при записи. Сегменты удобны при работе с данными, который является слишком большим для одного  буфера памяти приложения. Но не необходимо использовать множественные сегменты; Вы можете помещать все ваши данные Blob на единственном сегменте.

Операции над BLOB данными

Interbase поддерживает следующие операции над BLOB данными:

1.Чтение из BLOB.
2. Вставка новой строки включающей BLOB данные
3.Замена данных ссылающихся на  BLOB столбец.
4.Обновление данных ссылающихся на  BLOB столбец.
5.Удаление BLOB.

API Функции динамического SQL (DSQL) и  структура данных XSQLDA необходимы, чтобы выполнить SELECT, INSERT, и инструкции UPDATE, требующиеся, чтобы выбирать, вставлять, или модифицировать уместные данные Blob.

Чтение данных из BLOB.

Эти шесть шагов требуются для чтения данных из существующего BLOB:

1.  Создается обычная инструкция SELECT для выбора строки содержащей BLOB столбец.

2. Подготавливается структура для вывода данных XSQLDA.

3. Подготавливается SELECT инструкция.

4. Выполняется инструкция.

5. Выбираем строки одну за другой

6. Читаем и обрабатываем BLOB данные для каждой строки.

Опишем все это подробнее, для непонимающих.

Создание SELECT инструкции

char *str =
"SELECT PROJ_NAME, PROJ_DESC, PRODUCT FROM PROJECT WHERE \
PRODUCT IN ("software", "hardware", "other") ORDER BY PROJ_NAME";

Подготовка структуры вывода XSQLDA

1. Объявляем переменную содержащую XSQLDA

XSQLDA *out_sqlda;

2. Выделяем память

out_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(3));

3.Ставим версию и число выходных параметров

out_sqlda->version = SQLDA_VERSION1;

out_sqlda->sqln = 3;

Подготовка SELECT инструкции для выполнения

После создания XSQLDA для содержания данных столбцов каждой выбранной строки,

Строку инструкции нужно подготовить к выполнению.

1. Обьявляеи и инициализируем дескриптор SQL инструкции,

с помощью известной нам функции isc_dsql_allocate_statement():

isc_stmt_handle stmt; /* Обьявление дескриптора инструкции */

stmt = NULL; /* Установка дескриптора в NULL перед выполнением */

isc_dsql_allocate_statement(status_vector, &db_handle, &stmt);

3.  Подготавливаем строку для выполнения с помощью isc_dsql_prepare(), которая проверяет строку(str) на синтаксические ошибки, переводит строку в формат для эффективного выполнения, и устанавливает в дескриптор инструкции (stmt) ссылку на этот созданный формат (blr что ли). Дескриптор инструкции используется позднее в функции isc_dsql_execute().

Если isc_dsql_prepare() передан указатель на XSQLDA вывода, как в следующем примере, она будет заполнять большинство полей в XSQLDA и всех подструктур XSQLVAR информацией о типа данных, длине, и имени столбца  в инструкции.

Пример вызова isc_dsql_prepare():

isc_dsql_prepare(
status_vector,
&trans, /* Устанавливается предварительным вызовом isc_start_transaction()*/
&stmt, /*Дескриптор инструкции устанавливается в вызове этой функции. */
0, /* Определяет что инструкция - строка заканчиается 0*/
str, /* Инструкция - строкаа */
SQLDA_VERSION1,/* Номер версии XSQLDA */
out_sqlda /* XSQLDA для хранения данных столбцов после выполнения инструкции */
)

3. Устанавливаем XSQLVAR структуру для каждого столбца:

- Определяем тип столбца ( если он не был установлен isc_dsql_prepare())

- Связываем указатель структуры XSQLVAR  sqldata с соответствующей локальной переменной.

Для столбцов чьи типы неизветсны к этому моменту:

-Приводим элементы типо данных (необязательно), к примеру, из SQL_VARYING к SQL_TEXT.
-Динамически выделяем место для хранения данных на которые указывает sqldata

Для обоих:

- Определяем число байт данных передаваемых в sqldata

- Предоставляем значение NULL индикатора для параметров

Выбранные данные для Blob (и массивов) столбцов отличаются от других типов столбцов, так что поля XSQLVAR должны быть установлены по-другому. Для не -BLOB (и не-массивов) столбцов, isc_dsql_prepare () устанавливают для каждый sqltype к соответствующему  типу поля, и выбранные данные помещаются в область памяти на которую указывают соответствующие sqldata, при каждой операции fetch. Для столбцов Blob, тип должен быть установлен в SQL _Blob (или SQL _Blob + 1, если нужен индикатор NULL). InterBase сохраняет внутренний идентификатор Blob (Blob ID),  а не данные Blob, в памяти на которую кажет sqldata, когда  строки данных выбраны, так что  sqldata должна указывать на память с размером нужным для хранения Blob ID.

Следующий пример кода иллюстрирует назначения для Blob и столбцов не-Blob, чей тип известен ко времени компиляции.

#define PROJLEN 20
#define TYPELEN 12
ISC_QUAD blob_id;
char proj_name[PROJLEN + 1];
char prod_type[TYPELEN + 1];
short flag0, flag1, flag2;
out_sqlda->sqlvar[0].sqldata = proj_name;
out_sqlda->sqlvar[0].sqltype = SQL_TEXT + 1;
out_sqlda->sqlvar[0].sqllen = PROJLEN;
out_sqlda->sqlvar[0].sqlind = &flag0;
out_sqlda->sqlvar[1].sqldata = (char *) &blob_id;
out_sqlda->sqlvar[1].sqltype = SQL_Blob + 1;
out_sqlda->sqlvar[1].sqllen = sizeof(ISC_QUAD);
out_sqlda->sqlvar[1].sqlind = &flag1;
out_sqlda->sqlvar[2].sqldata = prod_type;
out_sqlda->sqlvar[2].sqltype = SQL_TEXT + 1;
out_sqlda->sqlvar[2].sqllen = TYPELEN;
out_sqlda->sqlvar[2].sqlind = &flag2;

Выполнение инструкции

После того как инструкция подготовлена можно ее выполнить.

isc_dsql_execute(
status_vector,
&trans, /* Устанавливается предвартельным вызовом isc_start_transaction()*/
&stmt, /* выделяется isc_dsql_allocate_statement() */
1, /* XSQLDA version number */
NULL /* NULL так как нет входных параметров */
);

Эта инструкция создает список выбора, это строки возвращаемые после выполения инструкции.

Извлечение выбранных строк

Конструкция  цикла извлечения используется, чтобы извлечь (в  XSQLDA вывода) данные столбцов для отдельной строки из списка выбора и обработать каждую строку прежде, чем следующая строка будет выбрана. Каждое выполнение isc_dsql_fetch () выбирает данные столбцов в соответствующие подструктруы XSQLVAR структуры out_sqlda. Для столбца Blob, выбирается Blob ID неявляющийся фактическими данными, а просто указатель на них.

ISC_STATUS fetch_stat;
long SQLCODE;
. . .
while ((fetch_stat =
isc_dsql_fetch(status_vector, &stmt, 1, out_sqlda))
== 0)
{
proj_name[PROJLEN] = ’\0’;
prod_type[TYPELEN] = ’\0’;
printf("\nPROJECT: %–20s TYPE: %–15s\n\n",
proj_name, prod_type);
/* Read and process the Blob data (see next section) */
}
if (fetch_stat != 100L)
{
/* isc_dsql_fetch returns 100 if no more rows remain to be
retrieved */
SQLCODE = isc_sqlcode(status_vector);
isc_print_sqlerror(SQLCODE, status_vector);
return(1);
}

Чтение и обработка BLOB данных

Для чтения и обработки BLOB данных

1.Объявите и инициализируйте BLOB дескриптор

     isc_blob_handle blob_handle; /* Обьявление BLOB дескритора. */

blob_handle = NULL; /* Устанвите его в NULL перед использованием*/

2.Создайте буфер для хранения каждого прочитанного BLOB сегмента. Его размер должен быть максимальным размером сегмента в вашей программе используемой для чтения BLOB.

           char blob_segment[80];

    3. Обьявите беззнаковую short переменную в которую IB будет хранить фактическую длину каждого прочитанного сегмента:

unsigned short actual_seg_len;

4. Открываем BLOB c извлеченным ранее blob_id

isc_open_blob2(
status_vector,
&db_handle,
&trans,
&blob_handle,/* Устанавливается этой функцией для ссылки на BLOB */
&blob_id, /* Blob ID полученный из out_sqlda которую заполнил isc_dsql_fetch() */
0, /* BPB length = 0; фильтр не будем использовать */
NULL /* NULL BPB, фильтр не будем использовать */
);

5.Читаем все BLOB данные вызывая повторно isc_get_segment(), берущую каждый Blob сегмент и его длину. Обрабатываем каждый прочитанны сегмент.

blob_stat = isc_get_segment(
status_vector,
&blob_handle, /* Устанавливается isc_open_blob2()*/
&actual_seg_len, /* Длина прочитанного сегмента */
sizeof(blob_segment), /* Длина буфера сегмента */
blob_segment /* буфер сегмента */
);
while (blob_stat == 0 || status_vector[1] == isc_segment)
{
/* isc_get_segment возвращает 0 если сегмент был полностью прочитан.
*/
/* status_vector[1] утанавливается в  isc_segment только часть */
/* сегмента была прочитана из-за буфера (blob_segment) не являющегося */
/* достаточно большим. В этом случае придется делать дополнительные вызовы */
/* isc_get_segment() для дочитывания. */
printf("%*.*s", actual_seg_len, actual_seg_len, blob_segment);
blob_stat = isc_get_segment(status_vector, &blob_handle,
&actual_seg_len, sizeof(blob_segment), blob_segment);
printf("\n");
};
printf("\n");

6. Закрываем BLOB

isc_close_blob(status_vector, &blob_handle);

Запись данных в BLOB

Перед тем как создать новый BLOB  и записать туда данные вы должны сделать следующее.

-Включить BLOB данные в строку втавляемую в таблицу
-Заменить данные ссылающиеся на BLOB столбец строки
-Обновить данные ссылающиеся на BLOB столбец строки

Вносимый  в столбец Blob фактически не содержит данных Blob. Он содержит Blob ID ссылающийся на данные, которыe сохранены в другом месте. Так, чтобы устанавить или изменить столбец Blob, Вы должны установить (или сбросить) Blob ID, сохраненный в нем. Если столбец Blob содержит Blob ID, и Вы изменяете столбцы относящиеся к различным  Blob (или содержащим NULL), Blob на который ссылается предварительно сохраненный Blob ID будет удален в течение следующей сборки "мусора".(????)

Все эти операции требуют следующих шагов:

1. Подготовьте соответствующую инструкцию DSQL. Это будет инструкция INSERT, если Вы вставляете новую строку в таблицу, или инструкция UPDATE для изменения строки. Каждая из этих инструкций будет нуждаться в соответствующей  структуре ввода XSQLDA для передачи параметров  инструкции во время выполнения. Blob ID нового Blob будет одним переданных значений

2. Создайте новый BLOB, и запишите в него данные.

3. Свяжите BLOB ID нового BLOB со столбцом таблицы строк над которой вы будете выполнять INSERT и UPDATE.

Примечание: вы не можете непосредственно обновлять BLOB данные. Если вы желаете модифицировать BLOB данные, вы должны:

-Создать новый BLOB
-Прочитать данные из старого BLOBA в буфер где вы сможете отредактировать и модифицировать их.
-Записать измененные данные в новый BLOB.
-Подготовить и выполнить инструкцию UPDATE которая модифицирует BLOB столбец содержащий BLOB ID нового BLOB, заменяющий старый BLOB ID.

Секция ниже описывает шаги требуемые для вставки, обновления, и замены BLOB данных.

Подготовка UPDATE или INSERT инструкции.

1. Создаем саму строку для обновления

 char *upd_str =
  "UPDATE PROJECT SET PROJ_DESC = ? WHERE PROJ_ID = ?";

или для вставки

char *in_str = "INSERT INTO PROJECT (PROJ_NAME, PROJ_DESC, PRODUCT,
         PROJ_ID) VALUES (?, ?, ?, ?)";

2. Обьявляем переменную содержащую структуру входных параметров

XSQLDA *in_sqlda;

3. in_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(2));

4. in_sqlda->version = SQLDA_VERSION1;

  in_sqlda->sqln = 2;

5. Установить XSQLVAR структуру в XSQLDA для каждого передаваемого параметра.

-Определяем типы данных элементов
-Для параметров типы которых известны во время компиляции: указатель sqldata  связываем с локальной перемнной содержаещей передаваемый данные.
-Для параметров типы которых неизвестны во время выполнения: выделяем память для хранения данных на которые указывает sqldata
-Определяем число байт данных (размер)

Следующий код иллюстрирует все это для столбца BLOB и одного столбца тип данных которого известен во время компиляции.

#define PROJLEN 5
char proj_id[PROJLEN + 1];
ISC_QUAD blob_id;
in_sqlda->sqlvar[0].sqldata = (char *) &blob_id;
in_sqlda->sqlvar[0].sqltype = SQL_Blob + 1;
in_sqlda->sqlvar[0].sqllen = sizeof(ISC_QUAD);
in_sqlda->sqlvar[1].sqldata = proj_id;
in_sqlda->sqlvar[1].sqltype = SQL_TEXT;
in_sqlda->sqlvar[1].sqllen = 5;

Proj_id  должна быть инициализирована во время выполнения (если значение не известно во времени компиляции). Blob_id должна быть установлена, чтобы обращаться к недавно созданному Blob, как описано в следующих секциях

Создание нового BLOB и хранения данных

1. Объявление и инициализация BLOB дескриптора:

isc_blob_handle blob_handle; /* Обьявления BLOB дескриптора */
blob_handle = NULL; /* Устанавливаем дескриптор в NULL перед использованием*/
2. Обьявление и инициализация BLOB ID:
ISC_QUAD blob_id; /* Объявление Blob ID. */
blob_id = NULL; /* Установка его в NULL перед использованеим*/

3. Создание нового BLOB вызовом isc_create_blob2():

isc_create_blob2(
status_vector,
&db_handle,
&trans,
&blob_handle, /* устанавливается этой функцией ссылка на новый Blob */
&blob_id, /* Blob ID устанавливается этой функцией*/
0, /* Blob Parameter Buffer length = 0; no filter will be used*/
NULL /* NULL Blob Parameter Buffer, since no filter will be used*/
);

Эта функция создает новый BLOB открывает его для записи, и устанавливает blob_handle к указателю на новый BLOB

isc_create_blob2() также связывает BLOB с BLOB ID, и устанавливает blob_id к указателю на BLOB ID.

4. Записываем все данные, которые будут записаны в Blob,  делая ряд вызовов isc_put_segment (). Следующий пример читает строки данных, и связывает каждый Blob с упомянутым blob_handle. (Get_line () читает следующую строку данных, которые будут написаны.)

char *line;
unsigned short len;
. . .
line = get_line();
while (line)
{
len = strlen(line);
isc_put_segment(
status_vector,
&blob_handle,/* set by previous isc_create_blob2() */
len, /* длина буфера содержащего ланные для записи */
line /* буфер содержащий данные для записи в BLOB */
);
if (status_vector[0] == 1 && status_vector[1])
{
isc_print_status(status_vector);
return(1);
};
line = get_line();
};

5. Закрываем BLOB

isc_close_blob(status_vector, &blob_handle);

Связывание нового BLOB с BLOB столбцом

Выполнение инструкции UPDATE связывает новый BLOB с BLOB столбцом в строке выбранной инструкции.

isc_dsql_execute_immediate(

status_vector,

&db_handle,

&trans,

0,

upd_str,

1,

in_sqlda

);

Удаление BLOB

Cуществуют четыре способа удаления BLOB.

-Удаляем строку содержащую BLOB. Вы можете использовать DSQL для выполнения DELETE инструкции.
-Заменяем различные BLOB. Если Blob столбец содержит Blob ID, и вы модифицируете столбец ссылающийся на разные BLOB, ранее сохраненный BLOB будет удален слудующей сборкой “мусора”.
-Сбрасываем в NULL столбец ссылающийся на BLOB, к примеру, используя DSQL инструкцию  как следующую:
UPDATE PROJECT SET PROJ_DESC = NULL WHERE PROJ_ID = "VBASE"

Blob на который указывал удаленный blob_id будет удален следующей сборкой «мусора»

- Отказываемся от BLOB, после того как он был создан но, не был связан еще с определенным столбцом в таблице, используя isc_cancel_blob() функцию.

isc_cancel_blob(status_vector, &blob_handle);

Запрос информации об открытом BLOB

После того, как приложение открывает Blob, оно может получить информацию о Blob.

Isc_blob_info ()  позволяет приложению сделать запрос для информации о Blob типа общего количества

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

Приложение должно также создать буфер результата, достаточно большой, чтобы хранить информацию, возвращенную InterBase. Оно передает указатель на буфер результата, и размер, в байтах, этого буфера в isc_blob_info (). Если InterBase пытается поместить, больше информации чем может вместить буфер результатов, она помещает значение, isc_info_truncated, определенное в ibase.h, в последний байт буфера результатов. 

Буфер списка элементов запрашиваемой информации и буфер результатов.

Буфер списка элементов это char массив содержащий запрашиваемые байты значений. Каждый байт есть пункт определяющий тип желаемой информации.

Соответствующие констаннты опеределены в ibase.h

#define isc_info_blob_num_segments 4

#define isc_info_blob_max_segment 5

#define isc_info_blob_total_length 6

#define isc_info_blob_type 7

Буфер результатов содержит серию кластеров информации по каждому запрошенному элементу.  Каждый кластер содержит три части.

1.Первый байт определяет тип возвращенной информации.
2.Второй байт – число определяющее число байт до конца кластера (длина инфо)
3.Значение хранимое в переменном числе байт, которое интерпретируется в зависимости от типа первого байта кластера

Следующая таблица показывает элементы информацию о которых можно получить

Элемент                                Возвращаемое занчение

Запрашиваемый и возвращаемый элемент

Возвращаемое значение

1

isc_info_blob_num_segments

Полное число сегментов

2

isc_info_blob_max_segment

Длина самого длинного сегмента

3

isc_info_blob_total_length

Полный размер в байтах BLOB

4

isc_info_blob_type

Тип BLOB(0:сегментированный, 1:поток)

В дополнение к этой информации IB возвращенной в ответ на запрос, IB  может также возвратить один или несколько сообщений состояния в буфер результата. Каждое сообщение состояния есть беззнаковый байт в длине

Элемент

Описание

1

isc_info_end

Конец сообщений

2

isc_info_truncated

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

3

isc_info_error

Запрашиваемая информация неопеределена. Проверьте status_vector  и сообщения.

 

Пример вызова isc_blob_info( )

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

char blob_items[] = {
isc_info_blob_max_segment, isc_info_blob_num_segments};
char res_buffer[20], *p, item;
short length;
SLONG max_size = 0L, num_segments = 0L;
ISC_STATUS status_vector[20];
isc_open_blob2(
status_vector,
&db_handle, /* database handle, set by isc_attach_database() */
&tr_handle, /* transaction handle, set by isc_start_transaction()
*/
&blob_handle, /* set by this function to refer to the Blob */
&blob_id, /* Blob ID of the Blob to open */
0, /* BPB length = 0; no filter will be used */
NULL /* NULL BPB, since no filter will be used */
);
if (status_vector[0] == 1 && status_vector[1])
{
isc_print_status(status_vector);
return(1);
}
isc_blob_info(
status_vector,
&blob_handle, /* Set in isc_open_blob2() call above. */
sizeof(blob_items),/* Length of item-list buffer. */
blob_items, /* Item-list buffer. */
sizeof(res_buffer),/* Length of result buffer. */
res_buffer /* Result buffer */
);
if (status_vector[0] == 1 && status_vector[1])
{
/* An error occurred. */
isc_print_status(status_vector);
isc_close_blob(status_vector, &blob_handle);
return(1);
};
/* Extract the values returned in the result buffer. */
for (p = res_buffer; *p != isc_info_end ;)
{
item = *p++
length = (short)isc_vax_integer(p, 2);
p += 2;
switch (item)
{
case isc_info_blob_max_segment:
max_size = isc_vax_integer(p, length);
break;
case isc_info_blob_num_segments:
num_segments = isc_vax_integer(p, length);
break;
case isc_info_truncated:
/* handle error */
break;
default:
break;
}
p += length;
};

Blob дескрипторы

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

Два дескриптора Blob необходимы всякий раз, когда фильтр используется при записи  или чтении  Blob: один описывать данные источника фильтра, и другой  цель.

BLOB дескриптор это структура определенная в заголовочном файле ibase.h как следующая:

typedef struct {

short blob_desc_subtype; /* type of Blob data */

short blob_desc_charset; /* character set */

short blob_desc_segment_size; /* segment size */

unsigned char blob_desc_field_name [32]; /* Blob column name */

unsigned char blob_desc_relation_name [32]; /* table name */

} ISC_Blob_DESC;

Размер сегмента BLOB есть максимальное число юайт в приложении

Размер сегмента Blob - максимальное число байтов, которые приложение, как ожидается,  запишет  или будет читать из Blob. Вы можете использовать этот размер, чтобы выделить ваши собственные буфера. Blob_desc_relation_name и blob_desc_field_name поля содержащие строки с нулевым символом в конце.

Заполнение blob дескриптора

Есть четыре варианта для заполнения blob дескриптора

-Вызываем isc_blob_default_desc().  И заполняем ей поля дескриптора значениями по умолчанию. Подтип по умолчанию есть 1 (TEXT), сегмент размером 80 байт, кодовая страница по умолчанию есть страница установленная для вашего процесса.
-Вызываем isc_blob_lookup_desc(). Она обращается к системным таблицам и берет оттуда информацию о BLOB и заполняет ею поля дескриптора.
-Вызываем isc_blob_set_desc(). Она инициализирует дескриптор из парметров вызова, быстрее нежели получать доступ к метаданным.
-Устанавливаем поля дескриптора напрямую.
 

Следующий пример вызывает isc_blob_lookup_desc () чтобы узнать текущий подтип и информацию о наборе символов для столбца Blob по имени PROJ_DESC в таблице  PROJECT. Функция сохраняет информацию в исходном описателе from_desс.

isc_blob_lookup_desc (

status_vector,

&db_handle; /* Set by previous isc_attach_database() call. */

&tr_handle, /* Set by previous isc_start_transaction() call. */

"PROJECT", /* имя таблицы */

"PROJ_DESC", /* название столбца */

&from_desc, /* Blob дескриптор заполняется этой функцией. */

&global /* глобальное название колонки возвращаемое этой функцией */

)

Фильтрация BLOB данных

Фильтр Blob это подпрограмма, которая транслирует данные Blob из одного подтипа в другой.

InterBase включает набор специальных внутренних фильтров Blob, которые преобразовывают подтип 0

( Неструктурные данные) в подтип 1 (ТЕКСТ), и из подтипа 1 к подтипу 0.

В дополнение к использованию этих стандартных фильтров, Вы можете создавать ваши собственные внешние фильтры, чтобы обеспечивать специальное конвертирование данных. Например, Вы могли бы разрабатывать фильтр, чтобы преобразовывать один формат изображения к другому, например отображать то же самое изображение на мониторах с различными разрешающими способностями. Или Вы могли бы конвертировать двоичный Blob к простому тексту , чтобы легко файл переносить от одной системы к другой. Если Вы определяете фильтры, Вы можете назначать их идентификаторы подтипа от -32,768 до -1. Следующие разделы дают краткий обзор того, как писать фильтры Blob, и сопровождаются подробностями , как написать приложение, которое требует фильтрации.

Обратите внимание, что фильтры Blob доступны для баз данных, находящихся на всех платформах сервера InterBase кроме Системы Netware, где фильтры Blob не могут быть созданы.

Использование ваших собственных фильтров

В отличие от стандарта фильтров InterBase, которые конвертируют подтипом 0 в подтип 1 и наоборот, внешний фильтр Blob - вообще часть библиотеки подпрограмм, которые Вы создаете и связываете с приложением. Вы можете писать, Blob на C или Паскаль (или любой язык, который может называться из C). Чтобы использовать ваши собственные фильтры, следуйте по этим шагами:

1. Решите, какой фильтр Вы, должны написать.

2. Напишите фильтры в базовом языке.

3. Сформируйте общедоступную библиотеку фильтров.

4. Сделайте библиотеку фильтров доступной.

5. Определить фильтры для базы данных.

6. Напишите приложение, которое требует фильтрацию.

Шаги 2, 5 и 6 будут лучше описаны в следующих разделах.

Объявление внешнего фильтра BLOB для БД

Для объявления внешнего фильтра для БД, используйте иснтрукции DECLARE FILTER. К примеру, следующая инструкция объявляет фильтр, SAMPLE:

DECLARE FILTER SAMPLE

INPUT TYPE –1 OUTPUT_TYPE –2

ENTRY POINT "FilterFunction"

MODULE_NAME "filter.dll";

В примере, входной подтип фильтра определен как -1 и его выходной подтип  как -2. Если подтип -1 определяет текст нижнего регистра, а подтип -2 текст верхнего регистра, то цель фильтра SAMPLE  состояла бы в том, чтобы перевести данные Blob из текста нижнего регистра в текст верхнего регистра.

Параметры ENTRY_POINT И MODULE_NAME определяют внешнюю подпрограмму, которую InterBase вызывает, когда вызывается фильтр. Параметр MODULE_NAME определяет filter.dll, динамически загружаемую библиотеку, содержащую выполнимый код фильтра. Параметр ENTRY_POINT определяет точку входа в DLL. Хотя пример показывает только простое имя файла, это - хорошая практика для определения полного квалифицированного пути, начиная с пользователей вашего приложения желающих загрузить файл.

Создание внешних Blob фильтров

Если Вы хотите создавать ваши собственные фильтры, Вы должны иметь детальное понимание типов данных , которые Вы планируете конвертировать. InterBase не делает строгой проверки  типа данных на данные Blob; это - ваша ответственность.

Определений функций фильтров

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

Вы объявляете имя функции фильтра и имя выполняемой программы - фильтра с параметрами ENTRY_POINT И MODULE_NAME в инструкции DECLARE FILTER.

Функция фильтра должна иметь следующее объявление, вызывающее последовательность:

filter_function_name(short action, isc_blob_ctl control);

Параметр, action, является одним из восьми возможных макроопределений действия, и параметр, control - элемент isc_blob_ctl, управляющей структуры Blob, определенной в файле заголовка InterBase, ibase.h. Эти параметры обсуждаются позже в этой главе.

Следующий листинг скелетного фильтра описывает функцию фильтра, jpeg_filter:

#include <ibase.h>
#define SUCCESS 0
#define FAILURE 1
ISC_STATUS jpeg_filter(short action, isc_blob_ctl control)
{
ISC_STATUS status = SUCCESS;
switch (action)
{
case isc_blob_filter_open:
. . .
break;
case isc_blob_filter_get_segment:
. . .
break;
case isc_blob_filter_create:
. . .
break;
case isc_blob_filter_put_segment:
. . .
break;
case isc_blob_filter_close:
. . .
break;
case isc_blob_filter_alloc:
. . .
break;
case isc_blob_filter_free:
. . .
break;
case isc_blob_filter_seek:
. . .
break;
default:
. . .
break;
}
return status;
}

InterBase передает одно из восьми возможных действий к функции фильтра, jpeg_filter, посредством параметра action, и также передает экземпляр управляющей структуры Blob, isc_blob_ctl, посредством параметра, control. ellipses (…) в предыдущем листинге представляют код, который выполняет некоторые операции, для каждого действии, или случая, который перечислен в инструкции case.

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

Управляющая структура BLOB предоставляет основные методы обмена данными между фильтром и Interbase.

Управляющая структура BLOB определена при помощи typedef, isc_blob_ctl. в ibase.h вот так:

typedef struct isc_blob_ctl {
ISC_STATUS (*ctl_source)();
/* Указатель на внутреннюю InterBase Blob подпрограмму доступа.(функтор) */
struct isc_blob_ctl *ctl_source_handle;
/* Экземпляр of isc_blob_ctl передаваемый во внутреннюю подпрограмму доступа IB*/
short ctl_to_sub_type;/* Целевой подтип */
short ctl_from_sub_type;/* Исходный подтип */
unsigned short ctl_buffer_length; /* Длина ctl_buffer. */
unsigned short ctl_segment_length; /* Длина текущего сегмента */
unsigned short ctl_bpb_length; /* Длина буфера параметров BLOB. */
char *ctl_bpb; /* Указатель на буфер параметров BLOB */
unsigned char *ctl_buffer; /* Указатель на сегментный буфер */
ISC_LONG ctl_max_segment; /* Длина самого длинного BLOB сегмента */
ISC_LONG ctl_number_segments; /* Полное число сегментов */
ISC_LONG ctl_total_length; /* Полная длина BLOB */
ISC_STATUS *ctl_status;/* Указатель на статус вектор */
long ctl_data[8];/* Данные определяемые приложением */
} *ISC_Blob_CTL;

Семантика некоторых isc_blob_ctl полей зависит от выполняемого действия.

Например, когда приложение вызывает isc_put_segment () функцию API, InterBase передает isc_blob_filter_put_segment - действие функции фильтра. Буфер, указатель на буфер, ctl_buffer - поле управляющей структуры, передаваемый функции фильтра, содержит сегмент данных, которые будут записано, как определено приложением в его запросе к isc_put_segment (). Поскольку буфер содержит информацию, передаваемую в функцию фильтра, это называется полем IN. Функция фильтра должна включить инструкции в инструкцию case под isc_blob_filter_put_segment для выполнения фильтрации и затем передачи данных для записи в базу данных. Это может быть сделано,  вызывом *ctl_source подпрограммы доступной внутри Interbase подпрограммы. Для подробной информации относительно ctl_source, см. Руководство Программиста.

С другой стороны, когда приложение вызывает isc_get_segment () функцию API,и буфер, на него указывает ctl_buffer в управляющей структуре переденной функции фильтра,  пуст. В этом случае, InterBase передает isc_blob_filter_get_segment  действие- функции фильтра. Обработка действия- функции isc_blob_filter_get_segment фильтра должна включить команды для заполнения ctl_buffer сегментом данных из базы данных, чтобы возвратить его приложению. Это может быть сделано,  вызывом *ctl_source подпрограммы доступной внутри IB. В этом случае, буфер используется для вывода информации функцией фильтра, и называется полем OUT.

Следующая таблица описывает каждое из полей в isc_blob_ctl управляющей структуре Blob,  используются ли они для ввода функции фильтра (IN), или вывода (OUT).

Название поля

Описание

1

(*ctl_source)()

Указатель на функцию к которой происходит обращение из IB BLOB

2

*ctl_source_handle

Указатель на экземпляр isc_blob_ctl передаваемый внутренней IB BLOB подпрограмме(IN)

3

ctl_to_sub_type

Целевой подтип: информационное поле, предоставляет поддержку многоцелевых фильтров, которые могут исполнять больше одного типа трансляции; это поле и следующее позволяют такому фильтру решить какую трансляцию выолнить (IN)

4

ctl_from_sub_type

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

5

ctl_buffer_length

Для isc_blob_filter_put_segment, поле - поле IN, которое содержит длину сегмента данных , содержащихся в ctl_buffer.

Для isc_blob_filter_get_segment, поле -  поле IN, устанавливающее размер буфера на который указывает ctl_buffer, который используется, чтобы сохранить полученные данные Blob

6

ctl_segment_length

Длина текущего сегмента. Для isc_blob_filter_put_segment, поле не используется

Для isc_blob_filter_get_segment, поле - поле OUT устанавливающее размер полученного сегмента (или части сегмент, в случае, когда буферная длина ctl_buffer_length - меньше чем фактическая длина сегмента)

7

ctl_bpb_length

Длина буфера параметров BLOB

8

*ctl_bpb

Указатель на буфер параметров BLOB

9

*ctl_buffer

Указатель на буфер сегмента. Для isc_blob_filter_put_segment, поле есть поле IN которое содержит сегмент данных.

Для isc_blob_filter_get_segment, поле есть поле OUT заполняемое функцией фильтра сегментом данных возвращаемым приложению

10

ctl_max_segment

Длина в байтах, самого длинного сегмента BLOB. Инициализируется в 0. Функция фильтра устанавливает это поле. Это информационное поле.

11

ctl_number_segments

Полное число сегментов в BLOB. Инициализирующее значение 0. Устанавливается функцией фильтра, информационное.

12

ctl_total_length

Полная длина BLOB, в байтах. Устанавливается функцией фильтра, информационное.

13

*ctl_status

Указатель на IB статус вектор (OUT)

14

ctl_data [8]

8 – элементный массив данных определяемых приложением. Используйте это поле для хранения указателей, таких как указатели на область памяти и дескрипторы файлов созданные isc_blob_filter_open дескриптором, к примеру. Тогда, в следующий раз, в который функция фильтра вызывается указатели, будут доступны для использования.(IN, OUT)

 

Программирование действий функции фильтра

Когда приложение вызывает функцию API Blob для Blob, который будет отфильтрован, InterBase передает соответствующее сообщение  о действии в функцию фильтра посредством параметра действия. Существуют восемь возможных действий. Следующие макроопределения действий объявлены в ibase.h file:

#define isc_blob_filter_open 0

#define isc_blob_filter_get_segment 1

#define isc_blob_filter_close 2

#define isc_blob_filter_create 3

#define isc_blob_filter_put_segment 4

#define isc_blob_filter_alloc 5

#define isc_blob_filter_free 6

#define isc_blob_filter_seek 7

Следующая таблица перечисляет действия, и определяет, когда функция фильтра вызвается для каждого специфического действия. Большинство действий - результат событий, которые происходят, когда приложение вызывает функцию API Blob.

Действие

Когда вызывается фильтр с соответствующим действием

1

isc_blob_filter_open

Вызывается когда приложение вызывает isc_open_blob2()

2

isc_blob_filter_get_segment

Вызывается когда приложение вызывает isc_get_segment()

3

isc_blob_filter_close

Вызывается когда приложение вызывает isc_close_blob()

4

isc_blob_filter_create

Вызывается когда приложение вызывает isc_create_blob2()

5

isc_blob_filter_put_segment

Вызывается когда приложение вызывает isc_put_segment()

6

isc_blob_filter_alloc

Вызывается когда InterBase инициализирует обработку фильтром; это не результат действия приложения

7

isc_blob_filter_free

Вызывается когда InterBase заканчивает обработку фильтром; это не результат действия приложения

8

isc_blob_filter_seek

Зарезервирован для внутреннего использования фильтра; не используется внешними фильтрами

Написание приложений которые требуют фильтрации

Требование фильтрации BLOB данных, читыемых или пишущихся в BLOB, происходи по следующим шагам.

1. Создается буфер параметров BLOB (BPB) определяющий целевой или исходный подтип, и необязательный кодовый набор символов (для текстовых подтипов)

2. Вызовите или isc_open_blob2 () или isc_create_blob2 () чтобы открыть Blob для чтения или для записи, соответственно. В запросе, передайте BPB, чью информацию, InterBase будет использовать, чтобы определить который фильтр должен быть вызван.

Понятие буфера параметров BLOB

Буфер параметров Blob (BPB) необходим всякий раз, когда фильтр будет использоваться при записи  или чтении  Blob. BPB - переменная типа массив символов, объявленная в приложении, которое содержит исходные и целевые подтипы. Когда данные читаются  или записываются в  Blob, связанный с BPB, InterBase автоматически вызовет соответствующий фильтр, основанный на исходных и целевых подтипах, указанных в BPB.

Если исходные и целевые подтипы являются 1 (ТЕКСТОМ), и BPB также определяет различные исходные и целевые наборы символов, то, когда данные читаются от или записываются в Blob, связанный с BPB, InterBase автоматически преобразуе) каждый символ  источника в символ целевого набора символов.

Буфер параметров Blob может быть сгенерирован двумя способами:

1. Косвенно, через ВЫЗОВЫ API, создают исходные и целевые дескрипторы, а затем генерируют BPB исходя из информации находящейся в описателях.

2. Напрямую,  заполняя BPB массив соответствующими значениями.

Если Вы генерируете BPB через ВЫЗОВЫ API, Вы не надо знать формат  BPB. Но если Вы желаете непосредственно генерировать BPB, тогда Вы должны знать формат. Оба подхода описаны в следующих секциях. Формат BPB документирован в секции о прямом заполнении BPB.

Генерация буфера BLOB параметров используя API вызовы

Чтобы генерировать BPB косвенно, используйте ВЫЗОВЫ API, чтобы создать исходный и целевой дескрипторы Blob, и затем вызывовите isc_blob_gen_bpb () чтобы генерировать BPB из то информации, что в описателях. Следуйте  этими шагами:

1. Объявите два дескриптора Blob, один для источника, а другой для цели.

Например,

#include "Ibase.h"

ISC_Blob_DESC from_desc, to_desc;

2. Сохраните соответствующую информацию в дескрипторах Blob, вызывая одну из функций isc_blob_default_desc (), isc_blob_lookup_desc (), или isc_blob_set_desc (), или,  заполните дескрипторные поля напрямую. Следующий пример просматривает текущий подтип и информацию о наборе символов для столбца Blob по имени GUIDEBOOK в таблице по имени TOURISM, и сохраняет это в исходном дескрипторе, from_desc. Он устанавливает целевой дескриптор , to_desc к заданному по умолчанию подтипу (ТЕКСТ) и набору символов, так, чтобы исходные данные были преобразованы к простому тексту

140 INTERBASE 5
isc_blob_lookup_desc 
status_vector,
&db_handle; /* set in previous isc_attach_database() call */
&tr_handle, /* set in previous isc_start_transaction() call */
"TOURISM", /* table name */
"GUIDEBOOK", /* column name */
&from_desc, /* Blob descriptor filled in by this function call */
&global);
if (status_vector[0] == 1 && status_vector[1])
{
/* process error */
isc_print_status(status_vector);
return(1);
};
isc_blob_default_desc (
&to_desc, /* Blob descriptor filled in by this function call */
"", /* NULL table name; it's not needed in this case */
""); /* NULL column name; it's not needed in this case */

3. Обьявите  массив символов который будет использоваться как BPB. Удостоверьтесь чтобы он был достаточного размера.

char bpb[20];

4. Объявите  переменную unsigned short в которою IB будет сохранять фактическую длину данных BPB:

unsigned short actual_bpb_length;

5. Вызовите isc_blob_gen_bpb() для инициализации BPB информацией из исходных и целевых BLOB дескрипторов. К примеру:

isc_blob_gen_bpb(
status_vector,
&to_desc, /* target Blob descriptor */
&from_desc, /* source Blob descriptor */
sizeof(bpb), /* length of BPB buffer */
bpb, /* buffer into which the generated BPB will be stored*/
&actual_bpb_length /* actual length of generated BPB */
);

Создание буфера параметров BLOB напрямую

BPB можно создавать напрямую.

BPB состоит из следующих частей:

1. Байт определяющий версию BPB, это константа isc_bpb_version1.

2. Ряд кластеров из одного или нескольких байт

Каждый кластер состоит из следующих частей:

1. Первый байт это тип параметра. Это одна из онстант определенная для всех типов параметров (например, isc_bpb_target_type).

2. Второй байт определяет число байт оставшихся до конца кластера (это и есть длина самого параметра)

3. Переменное число байт интерпертирующееся в зависимости от типа параметра.

Примечание: Все числа в BPB представляются в универсальном формате сначала идет младший байт потом более старший.

Следующая таблица показывает список типов параметров

Параметр типа

Описание

1

isc_bpb_target_type

Целевой подтип

2

isc_bpb_source_type

Исходный подтип

3

isc_bpb_target_interp

Целевой набор символов

4

isc_bpb_source_interp

Исходный набор символов

BPB должен содержать isc_bpb_version1 в первом байте, и должен содержать кластеры, определяющие исходные и целевые подтипы. Кластеры кодовой таблицы символов необязательны. Если исходные и целевые подтипы являются 1 (ТЕКСТ), и BPB также определяет различные исходный и целевой наборы символов, то, когда данные читаются или пишутся в  Blob, связанный с BPB, InterBase автоматически преобразует каждый символ источника в символ целевого набора символов.

В следущем примере напрямую создается BPB для фильтра который исходный подтип –4 переводит в целевой подтип 1(ТЕКСТ)

char bpb[] = {
isc_bpb_version1,
isc_bpb_target_type,
1, /* # bytes that follow which specify target subtype */
1, /* target subtype (TEXT) */
isc_bpb_source_type,
1, /* # bytes that follow which specify source subtype */
–4, /* source subtype*/
};

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

Запрос на использование фильтра

Вы запрашиваете использование фильтра при открытии или создании Blob для чтения или  для записи. При вызове isc_open_blob2 () или isc_create_blob2 (), передайте BPB, чью информация, InterBase будет использовать, чтобы определить который фильтр должен быть вызван.

Следующий пример показывает создание и открытие BLOB для записи.

isc_blob_handle blob_handle; /* declare at beginning */
ISC_QUAD blob_id; /* declare at beginning */
. . .
isc_create_blob2(
status_vector,
&db_handle,
&tr_handle,
&blob_handle, /* to be filled in by this function */
&blob_id, /* to be filled in by this function */
actual_bpb_length, /* length of BPB data */
&bpb /* Blob parameter buffer */
)
if (status_vector[0] == 1 && status_vector[1])
{
isc_print_status(status_vector);
return(1);
}

Работа  с массивом данных

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

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

Функция

Описание

1

isc_array_lookup_desc()

Ищет и сохраняет в дескрипторе массива тип данных, длину, масштаб, и размеры для всех элементов в указанного столбца массива указанной таблицы

2

isc_array_lookup_bounds()

Делает тоже что и функция isc_array_lookup_desc (), а также ищет и сохраняет верхние и нижние границы каждой величины

3

isc_array_set_desc()

Инициализируетдескриптор массива параметрами переданными ей

4

isc_array_get_slice()

Ищет данные в массиве

5

isc_array_put_slice()

Пишет данные в массив

Введение в массивы

InterBase поддерживает массивы большинства типов данных. Использование массива позволяет множественным элементам данных быть сохраненным в одном столбце. InterBase может обращаться с массивом как отдельным модулем, или как рядом отдельных модулей, называемых секторами. Использование массива уместно когда:

Элементы данных естественно образуют набор того же самого типа данных.

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

Каждый элемент должен также быть идентифицирован и иметь индивидуальный доступ.

Элементы данных в массиве называются элементами массива. Массив может содержать элементы любого типа данных InterBase кроме Blob, а также  не может быть массив массивов. Все элементы определенного массива имеют тот же самый тип данных.

InterBase поддерживает многомерные массивы, массивы с от 1 до 16 размерностей. Многомерные массивы хранятся в строке - в основном порядок).

Размерности Массива имеют определенный диапазон верхних и нижних границ, называемых нижними индексами. Нижние индексы массива определяются, когда столбец массива создается.

Хранение массивов БД

IB не хранит непосредственно массив данных в поле записи таблицы. Там она хранит ID массива. ID массива есть уникальное числовое занчение которое ссылаетс на массив данных, который хранится в другом месте БД.

Дескрипторы массива

Дескрипор массива описывает как массив или подмножество массива выбираться илизаписываться в ISC_ARRAY_DESC структуру. ISC_ARRAY_DESC определена в заголовочном файле ibase.h  в таком виде:

typedef struct {
unsigned char array_desc_dtype; /* Datatype */
char array_desc_scale; /* Scale for numeric datatypes */
unsigned short array_desc_length;
/* Length in bytes of each array element */
char array_desc_field_name [32]; /* Column name */
char array_desc_relation_name [32]; /* Table name */
short array_desc_dimensions; /* Number of array dimensions */
short array_desc_flags;
/* Specifies whether array is to be accessed in row-major or
column-major order */
ISC_ARRAY_BOUND array_desc_bounds [16];
/* Lower and upper bounds for each dimension */
} ISC_ARRAY_DESC;
ISC_ARRAY_BOUND определен как:
typedef struct {
short array_bound_lower; /* lower bound */
short array_bound_upper; /* upper bound */
} ISC_ARRAY_BOUND;

Дескриптор массива содержит 16 ISC_ARRAY_BOUND структур, одну для каждой возможной размерности. Массив с n размерностями устанавливает верхние и нижние границы для первых n ISC_ARRAY_BOUND структур. Число фактических размерностей массива определено в array_desc_dimensions поле дескриптора массива.

Когда Вы выбираете данные из массива, Вы применяете Дескриптор массива определяющий сектор массива (полный массив или подмножество смежных элементов массива) для выборки. Точно так же, когда Вы записываете данные в массив, Вы применяете Дескриптор массива определяющий сектор массива который будет записан .

Инициализация дескриптора массива

Существуют 4 способа для инициализации дескриптора массива:

- Вызовите isc_array_lookup_desc(), который найдет ( в системной таблице метаданных) и сохранит в дескрипторе массива тип данных, длину, масштаб и размерности для определенного для массива столбца в определенной таблице. Эта функция также сохранит название таблицы и столбца в дескрипторе, и инициализирует его array_desc_flags поле показывающее какой массив доступен в первой строке. К примеру:

isc_array_lookup_desc(

status_vector,

&db_handle, /* Set by isc_attach_database() */

&tr_handle, /* Set by isc_start_transaction() */

"PROJ_DEPT_BUDGET",/* table name */

"QUART_HEAD_CNT",/* array column name */

&desc /* Инициализируемый дескриптор */

);

- Вызовите isc_array_lookup_bounds (), который найдет и выполниттот же самый вызов isc_array_lookup_desc (), за исключением того, что функция isc_array_lookup_bounds () также выбирает и сохраняет в Дескрипторе массива верхние и нижние границы каждой размерности

- Вызовите isc_array_set_desc(), которая инициализирует дескриптор параметрами, быстрее нежели она стала бы обращаться к метаданным для инициализиции. Например:

short dtype = SQL_TEXT;

short len = 8;

short numdims = 2;

isc_array_set_desc(

status_vector,

"TABLE1", /* table name */

"CHAR_ARRAY", /* array column name */

&dtype, /* datatype of elements */

&len, /* length of each element */

&numdims, /* number of array dimensions */

&desc /* descriptor to be filled in */

);

- Установите дескриптор полей напрямую. Например, установка поля array_desc_dimensions в дескрипторе desc происходит так:

desc.array_desc_dimensions = 2;

Доступ к массиву данных

Interbase поддерживает следующие операции над массивом данных:

- Чтение из массива или сектора массива

- Запись в масссив:

Включение нового массива в строку вставляемую в таблицу.

Замена массива ссылающегося на столбец массива строкой с новым массивом

Обновление массива ссылающегося на столбец массива строкой с модифицированными данными и сектора с данными

- Удаление массива

DSQL API функции и структуры данных XSQLDA

DSQL функции API  и структуры данных XSQLDA необходимы, чтобы выполнить SELECT, INSERT, и инструкции UPDATE. Следующие секции включают описания DSQL, программные методы, нужные для выполнения этих инструкций

Примечание:

Следующие операции над массивом не поддерживаются:

-Ссылка на размеры массива динамичекси в DSQL
-Установка элемента массива в NULL
-Использование аггрегативных функций, таких как min(), max(), с массивами.
-Ссылка на массивыв GROUP BY
-Создание представлений которые производят выборку из секторов массива

Чтение данных из массива

Есть 7 шагов для чтения данных из массива или сектора массива:

1.Создание инструкции SELECT, которая выбирает соответствущий столбец
2.Подготовка структуры вывода XSQLDA для хранения данных столбца для каждой выбраной строки
3.Подготовка инструкции SELECT для выполнения
4.Выполнение инструкции
5.Заполнение дескриптора массива информацией о описывающей массив или сетор массива для выборки
6.Выбираем отобранные строки по одной
7.Читаем и обрабатываем массив данных из каждой строки

Создание строки SELECT

char *sel_str =

"SELECT DEPT_NO, QUART_HEAD_CNT FROM PROJ_DEPT_BUDGET \

WHERE year = 1994 AND PROJ_ID = ’VBASE’";

Подготовка XSQLDA ля вывода

XSQLDA *out_sqlda;

out_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(2));

out_sqlda->version = SQLDA_VERSION1;

out_sqlda->sqln = 2;

Подготовка SELECT инструкции к выполнению

isc_stmt_handle stmt; /* Declare a statement handle. */

stmt = NULL; /* Set handle to NULL before allocation. */

isc_dsql_allocate_statement(status_vector, &db_handle, &stmt);

isc_dsql_prepare(

status_vector,

&trans, /* Set by previous isc_start_transaction() call. */

&stmt, /* Statement handle set by this function call. */

0, /* Specifies statement string is null-terminated. */

sel_str, /* Statement string. */

1, /* XSQLDA version number. */

out_sqlda /* XSQLDA for storing column data. */

);

Устанавливаем XSQLVAR структуру дл я каждого столбца.

Для столбцов чьи типы известны во время компиляции

-Определяем тип столбца (если он не был установлен isc_dsql_prepare())
-Уквзатель sqldata  связывает с локальными переменными

Для столбцов чьи типы неизвестны вр время компиляции

- Приводим типы элементов (необязательно). Например, SQL_VARYING к SQL_TEXT.

- Динамичаски выделяем память для локального хранения данных, и указатель на нее присваиваем sqldata

Не забываем кому нужно про NULL индикатор

Поиск данных для массивов (и Blob) столбцов отличаются от других типов столбцов, так что поля XSQLVAR должны быть установлены по-другому. Для не-массива (и не-BLOB) столбцов, isc_dsql_prepare () устанавливают каждый XSQLVAR sqltype поле к соответствующему полевому типу найденных данных, когда отобранные данные строки списка выбраны. Для столбцов массива, тип установливается в SQL _ARRAY (или SQL _ARRAY + 1, если столбцу массива позволяют быть NULL). InterBase сохраняет внутренний идентификатор массива (array ID), не данные массива, в sqldata когда  строки данных выбраны, так что Вы должны направить sqldata на область размером ID array.

Пример внизу:

ISC_QUAD array_id = 0L;

char dept_no[6];

short flag0, flag1;

out_sqlda->sqlvar[0].sqldata = (char *) dept_no;

out_sqlda->sqlvar[0].sqltype = SQL_TEXT + 1;

out_sqlda->sqlvar[0].sqlind = &flag0;

out_sqlda->sqlvar[1].sqldata = (char *) &array_id;

out_sqlda->sqlvar[1].sqltype = SQL_ARRAY + 1;

out_sqlda->sqlvar[1].sqlind = &flag1;

Выполнение инструкции

isc_dsql_execute(

status_vector,

&trans, /* set by previous isc_start_transaction() call */

&stmt, /* set above by isc_dsql_prepare() */

1, /* XSQLDA version number */

NULL /* NULL since stmt doesn’t have input values */

);

Заполнение дескриптора массива

1. Создаем дескриптор массива

ISC_ARRAY_DESC desc;

2. И заполняем дескриптор массива как было описано выше

isc_array_lookup_bounds(

status_vector,

&db_handle,

&trans,

"PROJ_DEPT_BUDGET",/* table name */

"QUART_HEAD_CNT",/* array column name */

&desc);

Предположим столбец массива, QUART_HEAD_CNT, - одномерный массив, состоящий из четырех элементов, и он был объявлен нижний индекс 1 и верхним  4, когда это был создан. Тогда после вышеупомянутого запроса к isc_array_lookup_bounds (), поля Дескриптора массива для границ будут содержать следующую информацию:

desc.array_desc_bounds[0].array_bound_lower == 1

desc.array_desc_bounds[0].array_bound_upper == 4

Если Вы хотите читать только сектор массива, то измените верхние или нижние границы соответственно. Например, если Вы только хотите читать первые два элемента массива, измените верхнюю границу к значению 2, как в:

desc.array_desc_bounds[0].array_bound_upper = 2

Извлечение выбранных строк

ISC_STATUS fetch_stat;
long SQLCODE;
. . .
while ((fetch_stat = j
isc_dsql_fetch(status_vector, &stmt, 1, out_sqlda))
== 0)
{
/* Read and process the array data */
}
if (fetch_stat != 100L)
{
/* isc_dsql_fetch returns 100 if no more rows remain to be
retrieved */
SQLCODE = isc_sqlcode(status_vector);
isc_print_sqlerror(SQLCODE, status_vector);
return(1);
}

Чтение и обработка массива данных

Чтение и обработка массива или массива сектора данных:

1. Создаем буфер для хранения читаемых данных. Сделайте его достаточного размера для хранения всех элементов читаемого сектора. Например, следующий код делает объявление буфера массива для хранения 4 long элементов.

long hcnt[4];

2. Обьявляем short переменную для определения размера буфера массива

short len;

3. Устанавливаем переменную в длину буфера:

len = sizeof(hcnt);

4. Читаем массив или массив сектора данных в буфер вызвав isc_array_get_slice(). Обрабатываем прочитанные данные. В следующем примере массив читается в hcnt буфер массива, и обрабатывается.

isc_array_get_slice(

status_vector,

&db_handle,/* set by isc_attach_database()*/

&trans, /* set by isc_start_transaction() */

&array_id, /* array ID put into out_sqlda by isc_dsql_fetch()*/

&desc, /* array descriptor specifying slice to be read */

(void *) hcnt,/* buffer into which data will be read */

(long *) &len/* length of buffer */

);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector);

return(1);

}

/* Make dept_no a null-terminated string */

dept_no[out_sqlda->sqlvar[0].sqllen] = ’\0’;

printf("Department #: %s\n\n", dept_no);

printf("\tCurrent head counts: %ld %ld %ld %ld\n",

hcnt[0], hcnt[1], hcnt[2], hcnt[3]);

Запись данных в массив

Isc_array_put_slice() вызывается для записи данных в массив или массив секторов

Следующие шаги требуются для вставки, замены, или обновления массива данных

1.Подготовка дескриптора с информацией описывающей массив (или сектор) для записи
2.Подготовка буфера массива с данными для записи.
3.Подготовка соответствующей DSQL инструкции.
4.Вызовите isc_array_put_slice() для создание нового массива, и для записи данных из буфера массива в массив или сектор массива
5.Свяжите новый массив со столбцом массива, установив его значение к ID массива

Подготовка дескриптора массива

1. Создайте дескриптор массива:

ISC_ARRAY_DESC desc;

2. Заполните дескриптор информацией

Заполните Дескриптор  информацией относительно столбца массива, куда данные будут записаны. Делайте это или,  вызывая одну из функций isc_array_lookup_bounds (), isc_array_lookup_desc (), или isc_array_set_desc (), или,  непосредственно заполняя Дескриптор.

isc_array_lookup_bounds(

status_vector,

db_handle,

&trans,

"PROJ_DEPT_BUDGET",/* table name */

"QUART_HEAD_CNT",/* array column name */

&desc);

Подготовка буфера с данными

long hcnt[4];

short len;

len = sizeof(hcnt);

заполняем буфер данными

hcnt[0] = 4;

hcnt[1] = 5;

hcnt[2] = 6;

hcnt[3] = 6;

Подготовка инструкции вставки и обновления

char *upd_str =

"UPDATE PROJ_DEPT_BUDGET SET QUART_HEAD_CNT = ? WHERE \

YEAR = 1994 AND PROJ_ID = "MKTPR" AND DEPT_NO = ?";

XSQLDA *in_sqlda;

in_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(2));

in_sqlda->version = SQLDA_VERSION1;

in_sqlda->sqln = 2;

#define NUMLEN 4

char dept_no[NUMLEN + 1];

ISC_QUAD array_id;

in_sqlda->sqlvar[0].sqldata = &array_id;

in_sqlda->sqlvar[0].sqltype = SQL_ARRAY + 1;

in_sqlda->sqlvar[0].sqllen = sizeof(ISC_QUAD);

in_sqlda->sqlvar[1].sqldata = dept_no;

in_sqlda->sqlvar[1].sqltype = SQL_TEXT;

in_sqlda->sqlvar[1].sqllen = 4;

Вызов isc_array_put_slice()

1. Обьявляем ID массива

ISC_QUAD array_id; /* Declare an array ID. */

2. Инициализируем его

array_id = NULL;/* Set handle to NULL before using it */

3. Вызываем isc_array_put_slice().

isc_array_put_slice(

status_vector,

&db_handle,

&trans,

&array_id,/* array ID (NULL, or existing array’s array ID) */

&desc, /* array descriptor describing where to write data */

hcnt, /* array buffer containing data to write to array */

&len /* length of array buffer */

);

Связываем новый массив с массивом столбцом

isc_dsql_execute_immediate(

status_vector,

&db_handle,

&trans,

0, /* indicates string to execute is null-terminated */

upd_str, /* UPDATE statement string to be executed */

1, /* XSQLDA version number */

in_sqlda /* XSQLDA supplying parameters to UPDATE statement */

);

Удаление массива

Существует три способа удаления

1.Удалите строку содержащую массив обычной инструкцией DELETE
2.Замените массив други, как описано выше. Если массив столбец содержит ID массива, и вы модифицируете столбец  ссылкой на другой массив, то массив со старым ID будет удален во время следующей сборки “мусора”.
3.Сьросьте в NULL столбец ссылающийся на массив. Например, используя DSQL инструкцию:

"UPDATE JOB SET LANGUAGE_REQ = NULL \

WHERE JOB_CODE = "SA12" AND JOB_GRADE = 10"

И массив, ссылка на который стала NULL, будет удален при следующей сборке “мусора”

Работа с событиями

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

Функция

Описание

1

isc_event_block()

Создать буфер параметров событий

2

isc_wait_for_event()

Ожидание синхронного события которое будет зарегистрировано

3

isc_que_events()

Установите асинхронное событие и возвратите управление приложению

4

isc_event_counts()

Определите исзменение значения счетчика событий в буфере параметров событий

5

isc_cancel_events()

Отказаться от интересующего события

Для асинхронных событий, эта глава описывает, как создать асинхронное прерывание (СИНХРОННОЕ СИСТЕМНОЕ ПРЕРЫВАНИЕ), - функцию , которая отвечает на зарегистрированные события.

Понятие механизма событий

Механизм событий в InterBase состоит из  четырех частей.

-Ядра InterBase, которое обслуживает очередь событий и уведомляет приложения, когда событие происходит.
-Буфера параметров событий, созданного приложением, где оно может получать уведомление о событиях .
- Приложение, которое регистрирует   одно или несколько интересующих определенных, именованных событиямй и  ждет уведомление, когда произойдет синхронное событие, или передает указатель на функцию AST, которая обрабатывает уведомления так, чтобы работа приложения могла продолжаться (асинхронное событие).
-Триггера или хранимой процедуры, которая уведомляет ядро что определенное, именованное событие  произошло. Уведомление вызывается POSTING.

Механизм событий InterBase позволяет приложениям отвечать на действия и изменения базы данных, сделанные другими, одновременно выполняющимися  программами без потребности других приложений, для связи непосредственно друг с другом, и без траты ПРОЦЕССОРНОГО ВРЕМЕНИ, требуемого для периодического опроса, чтобы определить произошло событие или нет.

Буфера параметров событий

Если приложение должно получать уведомление о событиях, оно должно установить два буфера параметров событий одинакового размера (EPBs) используя  isc_event_block (). Первый буфер, event_buffer, используется, чтобы хранить счетчик возникновений события до того как приложение зарегистрирует заинтересованность в событии. Второй буфер, result_buffer, впоследствии заполняется изменениями счетчика возникновений события, когда наступает событие, представляющее интерес для приложения. Вторая функция API, isc_event_counts (), определяет различия между счетчиками в этих буферах, чтобы определить, которое событие или события произошело.

Уведомление о синхронном событии

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

Isc_wait_for_event () функция обеспечивает синхронную обработку события для приложения.

Уведомление об асинхронном событии

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

Isc_que_events () предоставляет асинхронную обработку событий

Управление событиями в транзакции

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

Транзакция может регистрировать то же самое событие не один раз перед подтверждением, но независимо от того сколько раз событие было зарегистрировано, это расценивается как единственное возникновение события при  уведомлении о  событии.

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

В C, каждый EPB объявлен как указатель на char следующим образом:

char *event_buffer, *result_buffer;

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

Isc_event_block () также требует по крайней мере двух дополнительных параметров: число событий, к которым приложение регистрирует свой интерес, и для каждого события, строка названия события. Один вызов isc_event_block () может передать 15 строк названий событий. Названия события должны соответствовать названиям событий, зарегистрированных хранимыми процедурами или триггерами. Isc_event_block () выделяет одинаковое количество памяти для каждого EPB, достаточного, чтобы обработать каждое именованное событие. Она возвращает единственное значение, указывая размер в байтах каждого буфера.

Синтаксис для isc_event_block ():

ISC_LONG isc_event_block(

char **event_buffer,

char **result_buffer,

unsigned short id_count,

. . . );

Например, следующий код устанавливает EPB для трех событий:

#include <ibase.h>;

. . .

char *event_buffer, *result_buffer;

long blength;

. . .

blength = isc_event_block(&event_buffer, &result_buffer, 3, "BORL",

"INTEL", "SUN");

. . .

Этот код предполагает, что имеются триггера или хранимые процедуры, определенные для базы данных, которые устанавливают события по имени “BORL”, “INTEL”, и “SUN”.

Приложения, которые должны ответить на больше чем 15 событий, могут делать множественные запросы к isc_event_block (), определяя различные EPB и списки событий для каждого запроса.

После установки EPB и определения событий, представляющих интерес с помощью isc_event_block (), приложение может использовать isc_wait_for_event () чтобы зарегистрировать интерес в этих событиях и приостанавливать свое выполнение, пока одно из событий не произойдет.

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

Синтаксис для isc_wait_for_event():

ISC_STATUS isc_wait_for_event(

ISC_STATUS *status_vector,

isc_db_handle *db_handle,

short length,

char *event_buffer,

char *result_buffer);

Например следующий код устанавливает EPB для трех событий, потом вызывает isc_wait_for_event() чтобы остановить выполнение программыц пока одно их событий не произойдет.

#include <ibase.h>;

. . .

char *event_buffer, *result_buffer;

long blength;

ISC_STATUS status_vector[20];

isc_db_handle db1;

. . .

/* Assume database db1 is attached here and a transaction started. */

blength = isc_event_block(&event_buffer, &result_buffer, 3, "BORL",

"INTEL", "SUN");

isc_wait_for_event(status_vector, &db1, (short)blength,

event_buffer, result_buffer);

/* Application processing is suspended here until an event occurs. */

. . .

Как isc_wait_for_event () вызвана, так  приложение останавливает обработку, пока одно из требуемых событий не произойдет. Когда событие происходит, приложение обрабатывает результат в следующей исполняемой инструкции после запроса к isc_wait_for_event (). Если приложение ожидает  больше чем одно событие, оно должно использовать isc_event_counts () чтобы определить, какое событие произошло

Один вызов isc_wait_for_event() может ожидать максимум 15 событий.

Приложения которым нужно ожидать более чем 15 событий должны в другом вызове продолжать ожидание.

Продолжение обработки с помощью isc_que_event()

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

Когда требуемое событие произошло, InterBase вызывает асинхронную функцию ловушку(AST) , также переданную как параметр в isc_que_events (), для обработки проишедшего события. СИНХРОННОЕ СИСТЕМНОЕ ПРЕРЫВАНИЕ (AST) - функция или подпрограмма в приложении запроса, единственная цель которого состоит в том, чтобы обработать наступление события для приложения.

Синтаксис для isc_que_events ()

ISC_STATUS isc_que_events(

ISC_STATUS *status_vector,

isc_db_handle *db_handle,

ISC_LONG *event_id,

short length,

char *event_buffer,

isc_callback event_function,

void *event_function_arg);

Event_id - длинный указатель, который используется как дескриптор в последующих запросах к isc_cancel_events () чтобы закончить уведомление о событии. Он должен бть проинициализирован когда передается. Параметр length - это размер event_buffer, который содержит текущий счетчик событий, которые нужно ждать. Event_function - указатель на функцию AST, которую InterBase должна вызывать, когда событие, представляющее интерес произошло.

It is up to the AST function to notify the application that it has been called, perhaps by setting a global flag of some kind. Event_function_arg - указатель на первый параметр, передаваемый AST

Создание AST

Функция события, event_function, должна быть написана с тремя входными параметрами:

1. Event_function_arg, указывается в вызове isc_que_events (). Обычно это указатель на буфер параметров событий, который должен быть заполнен измененными счетчиками события.

2. Длина  буфера events_list.

3.Указатель на буфер events_list, временный буфер параметров события совершенно сходен с переданным isc_que_events (), исключение для модификаций счетчиков события. Буфер результатов автоматически не модифицируется возникновением события;

Это происходит до event_function, чтобы скопировать временный буфер events_list в  постоянный буфер, который  использует приложение.

Буфер результатов автоматически не обновляется при возникновении события; это - до event_function, чтобы копировать временный буфер events_list на более постоянный буфер, который приложение использует.

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

Образец event_function представлен в следующем примере:

isc_callback event_function

(char *result, short length, char *updated)

{

/* Set the global event flag. */

event_flag++

/* Copy the temporary updated buffer to the result buffer. */

while (length--)

*result++ = *updated++;

return(0);

};

Полный пример с isc_que_events( )

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

#include <ibase.h>

#define number_of_stocks 3;

#define MAX_LOOP 10

char *event_names[] = {"DEC", "HP", "SUN"};

char *event_buffer, *result_buffer;

ISC_STATUS status_vector[20];

short length;

ISC_LONG event_id;

int i, counter;

int event_flag = 0;

length = (short)isc_event_block(

&event_buffer,

&result_buffer,

number_of_stocks,

"DEC", "HP", "SUN");

isc_que_events(

status_vector,

&database_handle, /* Set in previous isc_attach_database(). */

&event_id,

length, /* Returned from isc_event_block(). */

event_buffer,

(isc_callback)event_function,

result_buffer);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector); /* Display error message. */

return(1);

};

counter = 0;

while (counter < MAX_LOOP)

{

counter++;

if (!event_flag)

{

/* Do whatever other processing you want. */

;

}

else

{ event_flag = 0;

isc_event_counts(

status_vector,

length,

event_buffer,

result_buffer);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector); /*Display error message.*/

return(1);

};

for (i=0; i<number_of_stocks; i++)

if (status_vector[i])

{

/* The event has been posted. Do whatever is appropriate,

such as initiating a buy or sell order.

Note: event_names[i] tells the name of the event

corresponding to status_vector[i]. */

;

}

isc_que_events(

status_vector,

&database_handle,

&event_id,

length,

event_buffer,

(isc_callback)event_function,

result_buffer);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector); /*Display error message.*/

return(1);

}

} /* End of else. */

} /* End of while. */

/* Let InterBase know you no longer want to wait asynchronously. */

isc_cancel_events(

status_vector,

&database_handle,

&event_id);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector); /* Display error message. */

return(1);

}

Определение какое событие произошло с помощью isc_event_counts( )

Когда приложение регистрирует заинтересованность в множественных событиях и получает уведомление, что событие произошло, дальше оно должно использовать isc_event_counts () чтобы определить, каккое событие или события произошли. isc_event_counts () вычитает значения массива event_buffer из значений  массива result_buffer, чтобы определить сколько раз, каждый событие произошло начиная с того момента как приложения зарегистрировали интерес в наборе событий. Event_buffer и result_buffer - переменные, объявленные в  приложении, и созданные и инициализированные isc_event_block ().

Разность для каждого элемента возвращается в массиве состояний ошибок, который передан в isc_event_counts (). Чтобы определить, какие события произошли, приложение должно проверить каждый элемент массива на значения отличные от нуля. Счетчик отличный от нуля указывает число событий зарегистрированых между моментом вызова isc_event_block ()  и моментом времени когда событие было зарегистрировано, после вызовов isc_wait_for_event () или isc_que_events () . Где много приложений обращаются к той же самой базе данных, там определенный счетчик события может быть 1 или больше, и больше чем один элемент счетчика события может быть отличен от нуля.

Обратите внимание Когда первый раз устанавливают AST, чтобы поймать в ловушку события с помощью isc_que_events (), InterBase инициализирует все значения счетчика в статус вкеторе в 1, а не в 0. Чтобы убрать значения, вызовите isc_event_counts ().

В дополнение к определению которое событие произошело, isc_event_counts () повторно инициализирует массив event_buffer в ожидании другого вызова isc_wait_for_event () или isc_que_events (). Значения в event_buffer установлены в те же самые значения как переданные значения в result_buffer.

Полный синтаксис для isc_event_counts ():

void isc_event_counts(

ISC_STATUS status_vector,

short buffer_length,

char *event_buffer,

char *result_buffer);

Например, следующий код обьявляет интерес в трех событиях, ожидает их, затем использует isc_event_counts() чтобы определить какое событие произошло.

#include <ibase.h>;

. . .

char *event_buffer, *result_buffer;

long blength;

ISC_STATUS status_vector[20];

isc_db_handle db1;

long count_array[3];

int i;

. . .

/* Assume database db1 is attached here and a transaction started. */

blength = isc_event_block(&event_buffer, &result_buffer, 3, "BORL",

"INTEL", "SUN");

isc_wait_for_event(status_vector, &db1, (short)blength,

event_buffer, result_buffer);

/* Application processing is suspended here until an event occurs. */

isc_event_counts(status_vector, (short)blength, event_buffer,

result_buffer);

for (i = 0; i < 3; i++)

{

if (status_vector[i])

{

/* Process the event here. */

;

}

}

Отказ от интереса в асинхронном событии с помощью isc_cancel_events( )

Приложение, которое требовало асинхронное уведомление о событии с помощью isc_que_events () может впоследствии отменять требование уведомления в любое время с помощью  isc_cancel_events () используя следующий синтаксис:

ISC_STATUS isc_cancel_events(

ISC_STATUS *status_vector,

isc_db_handle *db_handle,

ISC_LONG *event_id);

Event_id - набор дескрипторов событий в предыдущем вызове isc_que_events (). Например, следующий код отменяет интерес в событии или событиях, идентифицированных event_id:

include <ibase.h>;

. . .

/* For example code leading up to this call, see the code example

in "Continuous Processing with isc_event_que(), earlier in this

chapter. */

isc_cancel_events(status_vector, &db_handle, &event_id);

 

Работа с сервисами

Эта глава охватывает InterBase API функции сервисов. Это средство позволяет Вам писать приложения, которые контролируют и управляют серверами InterBase и базами данных. Задачи, которые Вы можете выполнять с этими API, включают:

- Выполнение задачи обслуживания базы данных типа резервного копирования и восстановление, остановка и перезапуск, сборка "мусора", и сканирование на предмет испорченных структур данных

- Создание, изменение, и удаление пользователе в базе данных защиты

- Управление программными сертификатами

- Запросы информации о конфигурации баз данных и сервера

Обзор сервисных API

Этот раздел описывает основные коцепции сервисных API, использование буфера параметров сервисов, и методы для подключение и отключение к Service Manager

Общая информация

Сервисные API это группа функций в клиентской библиотеке (gds32.dll  в Windows, libgds.a  в UNIX/Linux). Сервисные API включены в утилиты gbak, gfix, gsec, gstat и iblicense, но естественно не все.

Все серверы InterBase включают средство называемое Менеджером Сервисов(Services Manager). API Сервисы позволяют клиентским приложениям делать запросы к Менеджеру Сервисов сервера InterBase, и Менеджер Сервисов исполняет задачи. Сервер может быть локальный (на том же самом главном компьютере что и ваше приложение), или удаленный (на другом главном компьютере на сети). API Сервисы предлагают те же самые особенности когда подлючены к локальному или удаленному серверу InterBase.

Семейство API Сервисов состоит из  четырех функции:

Isc_service_attach () инициализирует связь с указанным Services Manager

Isc_service_start () вызывает сервисную задачу

Isc_service_query () запрашивает информацию, или задачу из Services Manager

Isc_service_detach () отключает от Services Manager

Использование буфера параметров сервисов

Вы можете настраивать ваше приложение для подключения к Services Manager,  создавая буфер параметров сервисов (SPB), заполняя его нужными свойствами, и передать адрес SPB в isc_service_attach () или в другие функции в группе API Сервисов. Например, SPB может содержать имя пользователя и пароль для подключения к удаленному серверу.

SPB это символьный массив обьявленный в вашем приложении. Он содержит следующие элементы:

1. Первый байт включающий информацию о версии фрмата  SPB, это константа isc_spb_version.

2. Второй байт определяет число версии котороя определена как макрос, и как рекомендуемая версия SPB для каждого релиза InterBase.

3. Далее идут один или несколько кластеров байт, каждый кластер описывает один параметр.

Кластера в свою очередь состоят из следующих частей

1.Первый байт определяет что за параметр будет описан в кластере.
2.Второй байт определяет длину описфываемого параметра
3.Дальше идет столько байт сколько определил второй байт кластера.

Пример заполнения буфера для имени пользователя

1 char spb_buffer[128], *spb = spb_buffer;

2 *spb++ = isc_spb_version;

3 *spb++ = isc_spb_current_version;

4 *spb++ = isc_spb_user_name;

5 *spb++ = strlen("SYSDBA");

6 strcpy(spb, "SYSDBA");

7 spb += strlen("SYSDBA");

Все числа представлены в нашем любимом универсальном формате.

Подключение к Services Manager с помощью isc_service_attach()

Используйте isc_service_attach() для подключения к удаленному IB Services Manager

Вы можете применять локальное или удаленное имя сервиса для опеределения к какому хосту будем подключаться.

Пример:

char *user = "SYSDBA",

*password = "masterkey", /* see security tip below */

*service_name = "jupiter:service_mgr";

ISC_STATUS status[20];

isc_svc_handle *service_handle = NULL;

spb_buffer[128], *spb = spb_buffer;

unsigned short spb_length;

*spb++ = isc_spb_version;

*spb++ = isc_spb_current_version;

*spb++ = isc_spb_user_name;

*spb++ = strlen(user);

strcpy(spb, user);

spb += strlen(user);

*spb++ = isc_spb_password;

*spb++ = strlen(password)

strcpy(spb, password);

spb += strlen(password);

spb_length = spb - spb_buffer;

if (isc_service_attach(status, 0, service_name,

&service_handle, spb_length, spb_buffer))

{

isc_print_status(status);

exit(1);

}

Отключение от Service Manager c помощью isc_service_detach()

isc_service_detach(status, &service_handle);

Вызов сервисных задач с помощью isc_service_start()

 

 

 

Справочник по API функциям

 

isc_vax_integer()

 

Изменяет порядок байт целого числа.

Синтакс ISC_LONG isc_vax_integer(char *buffer,short length);

 

Параметр                Тип                Описание

buffer                        char *                Указатель на целое число для конвертации

length                        short                Длина в байтах целого числа для конвертации

                               Может быть 1,2 или 4

Описание isc_vax_integer ()  меняет порядок байт целого числа находящегося в буфере и возвращает новое  значение.

Обычное применение этой функции  это преобразование целочисленных значений, переданных  в буфер параметров базы данных к формату, где самый младший байт должен быть первым и cамый старший  байт последним. В InterBase целочисленные значения должны быть представлены во буферах параметров  ввода(например, DPB) и возвращены в буферах результатов в универсальном формате, где самый младший байт первый, и cамый cтарший байт последний. Isc_vax_integer () используется, чтобы преобразовать целые числа к универсальному формату и обратно.

Пример

Следующий фрагмент кода преобразовывает 2-байтовое значение, хранимое в символьном буфере, который является буфером результатов, возвращенным функцией типа isc_database_info ():

#include <ibase.h>

char *p;

. . .

for(p = res_buffer; *p != isc_info_end;)

{

/* Чтение типа элемента следующего кластера в буфере результатов */

item = *p++;

/* Читает длину следующего значения в буфере результатов и преобразовует его. */

len = isc_vax_integer(p, 2);

p += len;

/* Теперь обработайте настоящее значение размером len байт. */

. . .

}

Возвращаемое значение isc_vax_integer () всегда возвращает полностью измененное побайтно длинное целочисленное значение.

Комментарий переводчика:

Если в буфере расположено число 0x00 0x04 0x00 x00, длиной 4 байта, p – указатель на символьный буфер этого числа, то соответствующий вызов isc_vax_integer(p, 4) даст ответ 1024.

 

 

 

 

 

 

 

 

 

Примеры программ

 

/*==============================================================================================

       

                                                               применения функции isc_dsql_execute2

Функция позволяет выполнять запрос с параметрами возвращающий данные

Ошибки:

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

т.е для нее не надо даже курсор открывать в этом случае,

но если возвращается несколько строк пишет SQLCODE=811, isc_print_sqlcode дает

multiple singleton rows. В документации написано что вроде бы функция может возвращать

несколько строк, но даже пример с ней приведен как возвращающий одну строку

Замечания:

Если вы желаете чтоб функция возвратила только одну группу данных 

  или не возвращала никаких данных, то лучше воспользоваться isc_dsql_exec_immed2()

  вместо isc_dsql_prepare () и isc_dsql_execute2 ().

Инструкции CREATE DATABASE и SET TRANSACTION не выполняются с помощью isc_dsql_execute2,

а с помощью isc_dsql_execute_immediate().

 

 

Описание примера:

 

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

  employee.gdb поставляемой с дистрибутивом ib6 и выполняет запрос следующего типа

  "select currency from country where country=?", где вместо маркера ? можно ставить

   любое название страны, в данном случае выбрана Australia.

       После выполнения будет результат 'Currency=ADollar'

*============================================================================================*/

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <ibase.h>

 

isc_db_handle db;

isc_tr_handle tr;

ISC_STATUS status_vector[20];

short dpb_buf_len=20;

char dpb_buf[]={

                               isc_dpb_version1,

                               isc_dpb_user_name,6, 'S','Y','S','D','B','A',

                               isc_dpb_password,9, 'm','a','s','t','e','r','k','e','y'

                               };

char tpb_buf[]  =        {

                                       isc_tpb_version3,

                                       isc_tpb_write,

                                       isc_tpb_read_committed,

                                       isc_tpb_no_rec_version,

                                       isc_tpb_wait

                                       };

 

 

int main()

{

int i;

long fetch_stat,SQLCODE;

short dtype;

char str[]="d:\\Interbase\\Examples\\V5\\Employee.gdb";

char *query = "SELECT CURRENCY FROM COUNTRY WHERE COUNTRY=?";

char currency[20];

isc_stmt_handle stmt;

XSQLDA *in_sqlda,*out_sqlda;

XSQLVAR *in_var,*out_var;

 

 

in_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(1));

out_sqlda= (XSQLDA *)malloc(XSQLDA_LENGTH(1));

in_sqlda->version =out_sqlda->version = SQLDA_VERSION1;

in_sqlda->sqln = out_sqlda->sqln = 1;

stmt = NULL;

db=NULL;

tr=NULL;

 

isc_attach_database(status_vector, strlen(str), str, &db,dpb_buf_len,dpb_buf);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector);goto ex;

}

isc_start_transaction(status_vector,&tr,1,&db,(unsigned short) sizeof(tpb_buf),tpb_buf);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector);goto ex;

}

 

isc_dsql_allocate_statement(status_vector, &db, &stmt);

isc_dsql_prepare(status_vector, &tr, &stmt, 0,query, 1, out_sqlda);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector);goto ex;

}

isc_dsql_describe_bind(status_vector, &stmt, 1, in_sqlda);

 

 

for (i=0, in_var = in_sqlda->sqlvar; i < in_sqlda->sqld; i++, in_var++)

{

dtype = (in_var->sqltype & ~1);

switch(dtype)

{

  case SQL_VARYING:

  in_var->sqltype = SQL_TEXT;

  in_var->sqldata = (char *)malloc(sizeof(char)*in_var->sqllen);

  strcpy(in_var->sqldata,"Australia");

  in_var->sqllen=9;

  break;

}

if (in_var->sqltype & 1)

{

 

  in_var->sqlind = (short *)malloc(sizeof(short));

}

}

 

isc_dsql_describe(status_vector, &stmt,1, out_sqlda);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector);goto ex;

}

 

for (i=0, out_var = out_sqlda->sqlvar; i < out_sqlda->sqld; i++, out_var++)

{

dtype = (out_var->sqltype & ~1);

switch(dtype)

{

  case SQL_VARYING:

  out_var->sqltype = SQL_TEXT;

  out_var->sqldata = (char *)malloc(sizeof(char)*out_var->sqllen);

  break;

}

if (out_var->sqltype & 1)

{

 

  out_var->sqlind = (short *)malloc(sizeof(short));

}

}

 

isc_dsql_execute2(status_vector, &tr, &stmt, 1,in_sqlda, out_sqlda);

if (status_vector[0] == 1 && status_vector[1])

{

SQLCODE = isc_sqlcode(status_vector);

isc_print_sqlerror(SQLCODE, status_vector);

isc_print_status(status_vector);goto ex;

}

 

for(i=0;i<out_sqlda->sqlvar->sqllen;++i )currency[i]=out_sqlda->sqlvar->sqldata[i];

currency[i]=0;

printf("\nCurrency=%s\n",currency);

isc_commit_transaction(status_vector,&tr);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector);goto ex;

}

 

ex:

 

if(db)

isc_detach_database(status_vector, &db);

free(in_sqlda);

free(out_sqlda);

return 0;

}

 

 

 

/*

Пример с возвратом нескольких строк

int main()

{

int i,j;

long fetch_stat,SQLCODE;

short dtype;

char str[]="d:\\Interbase\\Examples\\V5\\Employee.gdb";

//char *query = "SELECT CURRENCY FROM COUNTRY";

char *query = "select currency from country where country ='USA' or country = 'Australia'";

char currency[20];

isc_stmt_handle stmt;

XSQLDA *out_sqlda;

XSQLVAR *out_var;

 

out_sqlda= (XSQLDA *)malloc(XSQLDA_LENGTH(1));

out_sqlda->version = SQLDA_VERSION1;

out_sqlda->sqln = 1;

stmt = NULL;

db=NULL;

tr=NULL;

 

isc_attach_database(status_vector, strlen(str), str, &db,dpb_buf_len,dpb_buf);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector);goto ex;

}

isc_start_transaction(status_vector,&tr,1,&db,(unsigned short) sizeof(tpb_buf),tpb_buf);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector);goto ex;

}

 

isc_dsql_allocate_statement(status_vector, &db, &stmt);

isc_dsql_prepare(status_vector, &tr, &stmt, 0,query, 1, out_sqlda);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector);goto ex;

}

 

isc_dsql_describe(status_vector, &stmt,1, out_sqlda);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector);goto ex;

}

 

for (i=0, out_var = out_sqlda->sqlvar; i < out_sqlda->sqld; i++, out_var++)

{

 

dtype = (out_var->sqltype & ~1);

switch(dtype)

{

  case SQL_VARYING:

  out_var->sqltype = SQL_TEXT;

  out_var->sqldata = (char *)malloc(sizeof(char)*out_var->sqllen);

  break;

}

if (out_var->sqltype & 1)

{

  out_var->sqlind = (short *)malloc(sizeof(short));

}

}

 

isc_dsql_execute2(status_vector, &tr, &stmt, 1,NULL, out_sqlda);

if (status_vector[0] == 1 && status_vector[1])

{

SQLCODE = isc_sqlcode(status_vector);

isc_print_sqlerror(SQLCODE, status_vector);

isc_print_status(status_vector);goto ex;

}

 

isc_dsql_set_cursor_name(status_vector, &stmt, "dyn_cursor", NULL);

 

while ((fetch_stat =isc_dsql_fetch(status_vector, &stmt, 1, out_sqlda)) == 0)

{

for (i = 0; i < out_sqlda->sqld; i++)

{

  for(j=0;j<out_sqlda->sqlvar->sqllen;++j )currency[j]=out_sqlda->sqlvar->sqldata[j];

  currency[j]=0;

  printf("Currency=%s\n",currency);

}

}

if (fetch_stat != 100L)

{

 

SQLCODE = isc_sqlcode(status_vector);

isc_print_sqlerror(SQLCODE, status_vector);

}

isc_dsql_free_statement(status_vector, &stmt, DSQL_close);

 

isc_commit_transaction(status_vector,&tr);

if (status_vector[0] == 1 && status_vector[1])

{

isc_print_status(status_vector);goto ex;

}

 

ex:

 

if(db)

isc_detach_database(status_vector, &db);

free(out_sqlda);

return 0;

}*/