Рассмотрим подробнее работу с данными BLOB (большой двоичный объект), подтипами BLOB, особенностями чтения и записи BLOB, обращениям к BLOB с помощью SQL, DSQL и вызовами API.

ЧтопредставляетсобойBLOB?

BLOB предназначен для хранения данных произвольного формата переменной длины и, как правило, значительного размера.

BLOB можно использовать для хранения объектов разной природы и назначения, включая:

• Растровые изображения.

• Векторные рисунки.

• Звуки, видео и другую информацию мультимедиа.

• Текст и данные, включая документы большого объема.

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

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

Хранение данных BLOB

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

• TIFF, PICT, BMP, WMF, GEM, TARGA или другие растровые или векторно-графические файлы. :

• MIDI или WAV звуковые файлы. ,

• Интерактивные аудио-видео файлы AVI (Audio Video Interleaved) или видео файлы формата QuickTime.

• ASCII, MIF, DOC, RTF, WPx или другие текстовые файлы.

• Файлы CAD.

Затем эти файлы программно загружаются в базу данных. Доступ к данным также осуществляется программно. По сути, здесь можно провести следующую аналогию. Таблица с BLOB - директория, дескриптор BLOB - имя файла, данные BLOB - файл. В соответствии с этим и осуществляется доступ к объектам BLOB.

Подтипы BLOB

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

Таблица 6.4. Подтипы BLOB

Подтип Описание
0 Неструктурированный тип, обычно применимый к двоичным данным или данным неопределенного типа
1 Текст
2 Двоичное представление языка (BLR)
3 Список контроля доступа (Access control list)
4 (Резерв для будущего использования)
5 Закодированное описание метаданных таблиц
6 Описание неуспешных транзакции, работающих с несколькими базами данных

Пользовательский подтип можно определить, как отрицательное число в интервале от -1 до -32 678.

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

Столбцы BLOB определяются стандартным образов в командах CREATE TABLE, ALTER TABLE. Например, следующая команда определяет три столбца BLOB:

BLOB1 с подтипом 0 (принят по умолчанию), BLOB2 с подтипом 1 (текст) и BLOB3 с пользовательским подтипом -1.

Пример 6.4

CREATE TABLE TABLEBLOB (

BLOB1 BLOB,

BLOB2 BLOB SUB_TYPE 1,

BLOB3 BLOB SUB_TYPE -1 ' '

) ;

Чтобы определить и заданный по умолчанию размер сегмента, и подтип при создании столбца BLOB, используется опция SEGMENT SIZE, записываемая после опции SUB_TYPE. В приведенном ниже примере описывается создание в нашей тестовой базе описание таблицы книг, со держащей столбец REFERAT для хранения текста реферата книги, представляющий собой BLOB типа 0 (произвольный объект) с 80-байтовым сегментом.

Пример 6.5

CREATE TABLE TBOOK (

UNIKEY PRMKEY,

MATHERKEY INTEGER,

BOOKNM VARCHAR(250) ,

REFERAT BLOB sub_type 0 segment size 80,

NUM_ALL SMALLINT DEFAULT 0 NOT NULL,

NUM_PRESENCE SMALLINT DEFAULT 0 NOT NULL) ;

Единственное требование, которое InterBase предъявляет для определяемых пользователем подтипов - совместимость при преобразовании BLOB от одного подтипа к другому.

Использование памятидля BLOB

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

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

Работа с BLOB осуществляется на базовом языке. Сам доступ реализуется, как правило, одним из трех способов.

С использованием специально включаемого в программу SQL текста. В этом случае программа перед ее компиляцией обрабатывается специальным препроцессором. Для InterBase таковым является утилита GPRE. В результате ее работы этот текст транслируется в последовательность вызовов функций API. Поскольку аналогичные методы используются и другими СУБД, то это создает возможность с минимальными потерями переходить от работы с одной базы на другую.

Второй способ предполагает прямой вызов функций API. В этом случае программа оказывается явно привязанной к платформе InterBase, зато не требует предварительной препроцессорной обработки. Этот метод наиболее выгоден при написании стандартных функций, например, при создании библиотеки UDF (User Defined Functions).

Третий способ представляет собой использования средств доступа высокого уровня. В самом деле, поскольку работа с объектами BLOB первыми двумя способами достаточно трудоемка и при этом по своей сути стандартна, то было бы странно, если бы не было подобных высокоуровневых средств доступа к ним. В частности в системах C++ Builder и Delphi имеются специальные объекты для работы с BLOB.

Рассмотрим на примерах работу с BLOB различными методами.

В этом разделе будем использовать первый методами.

Использование API будет проиллюстрировано в разделе о создании UDF.

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

Выборка BLOB ID с использованием внедренного SQL показана в следующем примере (тексты этих примеров должны, естественно, перед их выполнением быть обработаны препроцессором). Предварительно создается курсор BLOB, представляющий отдельную строку выборки, содержащей объект BLOB (к курсорам вернемся несколько позже, а пока этот пример следует рассматривать, как иллюстрацию синтаксиса).

Пример 6.6 EXEC SQL

DECLARE BLOBDESC CURSOR FOR SELECT REFERAT FROM TBOOK

WHERE UNIKEY = 23;

Столбцы BLOB определяются так же, как обычные при создании таблиц (см. пример 6.5).

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

Связь между столбцом BLOB и данными BLOB

Рис. 6.1. Связь между столбцом BLOB и данными BLOB.

Вместо хранения данных BLOB непосредственно в таблице, InterBase хранит в ней только идентификатор. В базе данных ID указывает на первый сегмент данных BLOB, который хранится в базе данных в другом месте - в списке сегментов. Когда приложение создает BLOB в базе данных, оно записывает его содержимое посегментно. Точно так же приложение читает BLOB. Поскольку большинство данных BLOB - большие объекты, доступ к BLOB в большинстве случаев реализуется циклами в прикладном коде.

Длина сегмента BLOB

При определении BLOB в таблице в команде определения BLOB задается ожидаемый размер сегментов BLOB, которые должны быть записаны в столбец. Длина сегмента, определяемая для столбца сегмента, задает максимальное число байтов, которые приложение, как ожидается, запишет или будет читать из любого BLOB в столбце. Заданная по умолчанию длина сегмента - 80. Например, следующее объявление столбца создает BLOB с длиной сегмента 120:

Пример 6.7

EXEC SQL

CREATE TABLE TABLEBLOB (

BLOB1 BLOB SEGMENT SIZE 12 0;

);

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

Указание длины сегмента п гарантирует, что не более чем п байтов будет прочитано или записано за одну операцию с BLOB.

С некоторыми типами операций, например SELECT, INSERT или UPDATE, можно читать или записывать сегменты BLOB переменной длины.

В следующем примере команды INSERT CURSOR указывается длина сегмента в переменной базового языка segment_length.

Пример 6.8 EXEC SQL

INSERT CURSOR BLOBINS VALUES (:write_segment_buffer INDICATOR :segment_length);

Отмена длины сегмента Можно отменять установку дойны сегмента включением опции MAXIMUM_SEGMENT в инструкции DECLARE CURSOR. Так, следующее объявление курсора BLOB INSERT отменяет длину сегмента, которая была определена для поля BLOB1, увеличивая ее до 1024:

Пример 6.9 ,

EXEC SQL

DECLARE BLOBINS CURSOR FOR INSERT BLOB BLOB1 INTO TABLEBLOB MAXIMUM_SEGMENT 10 24;

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

Установка длины не затрагивает системное представление InterBase. При выборе длины сегмента следует руководствоваться удобством для конкретного приложения. Максимальная возможная длина сегмента -65 535 байт (64К).

ДОСТУП К BLOB С ИСПОЛЬЗОВАНИЕМ SQL (ИЗ ПРОГРАММЫ НА ЯЗЫКЕ С)

InterBase поддерживает команды SELECT, INSERT, UPDATE и DELETE для BLOB. Ниже приводятся примеры соответствующих программ, иллюстрирующих применение стандартного SQL для работы с BLOB. (Текст должен быть перед выполнением обработан утилитой Gpre).

Выборка BLOB

Следующая программа выбирает данные BLOB из столбца REFERAT таблицы ТВООК. Для реализации выборки нужно выполнить последовательно ряд действий:

• Объявить переменные базового языка для записи BLOB ID, данных сегментов BLOB и длины сегмента данных.

• Объявить курсор таблицы для выборки требуемого столбца BLOB.

• Объявить курсор для чтения BLOB, необходимый для чтения его сегментов.

• Открыть курсор таблицы и выбрать строку данных, содержащих BLOB.

• Открыть курсор чтения BLOB и выбрать первый сегмент данных.

• Выбрать в цикле остающиеся сегменты.

• Закрыть курсор чтения BLOB.

• Закрывает курсор для таблицы.

Пример 6.10

1. Объявляются переменные базового языка для записи BLOB ID, данных сегментов BLOB и длины сегмента данных:

EXEC SQL

BEGIN DECLARE SECTION;

BASED ON TBOOK.REFERAT blob_id;

BASED ON TBOOK.REFERAT.SEGMENT blob_segment_buf;

BASED ON TBOOK.UNIKEY key;

unsigned short blob_seg_len;

EXEC SQL

END DECLARE SECTION;

Конструкция BASED ON ... SEGMENT объявляет переменную базового языка blob_segment_buf, которая должна иметь размер, достаточный для размещения сегмента BLOB во время выполнения команды FETCH.

2. Объявляется курсор таблицы для выборки требуемого столбца BLOB, в данном примере - столбец REFERAT:

EXEC SQL

DECLARE TABCURSOR CURSOR FOR SELECT UNIKEY, REFERAT FROM TBOOK WHERE REFERAT = 123;

3. Объявляется курсор для чтения BLOB. Курсор для чтения BLOB -специальный тип курсора, используемый для чтения сегментов BLOB:

EXEC SQL

DECLARE BLOBCURSOR CURSOR FOR READ BLOB REFERAT FROM TBOOK;

Длина сегмента столбца BLOB REFERAT определена как 80, курсор BLOB BLOBCURSOR читает максимум 60 байт одновременно.

Чтобы переопределить длину сегмента, указанную в схеме базы данных для REFERAT, используется опция MAXIMUMSEGMENT. Например, следующий код ограничивает каждую операцию чтения BLOB максимумом в 60 байт, и SQLCODE устанавливается в 101, чтобы указать, когда прочитана только часть сегмента (признак конца данных):

EXEC SQL

DECLARE BLOBCURSOR CURSOR FOR READ BLOB REFERAT FROM TBOOK MAXIMUMJSEGMENT 60;

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

4. Открывается курсор таблицы и выбирается строка данных, содержащая BLOB:

EXEC SQL OPEN TABCURSOR;

EXEC SQL

FETCH TABCURSOR INTO :key, :blob_id;

Команда FETCH выбирает столбцы UNIKEY и REFERAT в host-переменные key и blob_id соответственно.

5. Открывается курсор чтения BLOB (путем использования BLOB ID), находящийся в переменной blob_id, и выбирается первый сегмент данных BLOB:

EXEC SQL

OPEN BLOBCURSOR USING :blob_id;

EXEC SQL

FETCH BLOBCURSOR INTO :blob_segment_buf:blob_seg_len;

Когда операция FETCH завершается, blob_segment_buf содержит первый сегмент данных BLOB, blob_seg_len содержит длину сегмента (число байтов, скопированных в blob_segment_buf).

6. Выбираются остающиеся сегменты в цикле на базовом языке, используя объявленные ранее переменные. После каждой выборки проверяется SQLCODE. Код 100 указывает, что все данные BLOB были выбраны. Код 101 указывает, что в сегменте BLOB еще остались данные. В приведенном примере полученные данные печатаются. Здесь их просто некуда деть, в реальных задачах данные либо сохраняются на диске, либо помещаются в какой-либо объект для последующей визуализации:

while (SQLCODE != 100 | | SQLCODE == 101)

{

printf("%*.*s", blob_seg_len, blob_seg_len, blob_segment_buf);

EXEC SQL

FETCH BC INTO :blob_segment_buf:blob_seg_len;

}

InterBase устанавливает код ошибки 101, когда длина буфера сегмента меньше, чем его специфицированная длина.

Например, как это происходит в нашем случае, если длина буфера сегмента 60, а длина специфицированного сегмента 80, то первый FETCH устанавливает код ошибки 101, указывающий, что в сегменте еще остаются данные. Второй FETCH читает оставшиеся 20 байт данных и устанавливает SQLCODE 0, указывающий, что следующий сегмент готов к чтению, или 100, если это был последний сегмент в BLOB.

7. Закрывает курсор чтения BLOB:

EXEC SQL

CLOSE BLOBCURSOR; i

8. Закрывает курсор для таблицы:

EXEC SQL CLOSE TABCURSOR;

Вставка данныхв BLOB

Следующая программа полностью аналогична предыдущей, но предназначена для вставки данных в BLOB-столбец REFERAT таблицы ТВООК. Для реализации вставки, также как и выборки нужно выполнить последовательно ряд действий:

• Объявить переменные базового языка для BLOB ID, данных сегмента BLOB и длины сегмента BLOB.

• Объявить BLOB-курсор для вставки.

• Открыть BLOB-курсор для вставки и задать host-переменную для размещения BLOB ID.

• Записать данные сегмента в буфере сегментов.

• Закрыть BLOB-курсор для вставки.

• Выполнить вставку новой строки, содержащей BLOB, в таблицу.

• Зафиксировать изменения в базе.

Пример 6.11

1. Объявляются переменные базового языка для BLOB ID, данных сегмента BLOB и длины сегмента BLOB:

EXEC SQL

BEGIN DECLARE SECTION;

BASED ON TBOOK.REFERAT blob_id;

BASED ON TBOOK.REFERAT.SEGMENT blob_segment_buf;

BASED ON TBOOK.UNIKEY key;

unsigned short blob_seg_len;

EXEC SQL

'END DECLARE SECTION;

Конструкция BASED ON ... SEGMENT объявляет переменную базового языка blob_segment_buf, которая должна иметь размер, достаточный для размещения сегмента BLOB во время выполнения команды FETCH.

2. Объявляется BLOB-курсор для вставки:

EXEC SQL

DECLARE BLOBCURSOR CURSOR FOR INSERT INTO TBOOK;

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

3. Открывается BLOB-курсор для вставки и задается host-переменная для размещения BLOB ID:

EXEC SQL

OPEN BLOBCURSOR INTO :blob_id;

4. Записываются данные сегмента в буфере сегментов blob seg-mentjbuf, вычисляется длина сегмента данных и используется команда INSERT CURSOR для записи сегмента. Эти действия повторяются в цикле, пока не будут записаны все сегменты BLOB:

char **s_referat; // Массив указателей на строки реферата int n_strings; // Количество строк в реферате

for(int i = 0; i<n_strings;i + +)

{

Sprintf (blob_segment_buf, s_referat[i]); blob_segment_len = strlen(blob_segment_buf);

EXEC SQL

INSERT CURSOR BLOBCURSOR

VALUES(:blob_segment_buf:blob_segment_len);

}

5. Закрывается BLOB-курсор для вставки:

EXEC SQL

CLOSE BLOBCURSOR;

6. Команда INSERT используется для вставки новых строк, содержащих BLOB, в таблицу ТВООК:

EXEC SQL '

INSERT INTO ТВООК (UNIKEY, MATHERKEY, BOOKNM, REFERAT)

VALUES (188, 44, 'Неведомая книга', :blob_id);

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

7. Фиксируются изменения в базе:

EXEC SQL

COMMIT;

Обновление данныхБи OB

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

• Объявить BLOB-курсор для вставки.

• Открыть BLOB-курсор для вставки и задать host-переменную для размещения BLOB ID.

• Считать сегменты данных старого BLOB, модифицировать их и записать в базу.

• Закрывает BLOB-курсор для вставки.

• Выполнить команду UPDATE для замены BLOB.

1. Объявляется BLOB-курсор для вставки:

EXEC SQL

DECLARE BLOBCURSOR CURSOR FOR INSERT BLOB REFERAT INTO TBOOK;

2. Открывается BLOB-курсор для вставки и задается host-переменная для размещения BLOB ID:

EXEC SQL

OPEN BLOBCURSOR INTO :blob_id;

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

3. Записывается сегмент данных BLOB в буфер сегментов blob_segment_buf, вычисляется длина сегмента данных, выполняются действия по модификации данных, и используется команда INSERT CURSOR для записи сегмента:

EXEC SQL

INSERT CURSOR BLOBCURSOR VALUES (:blob_segment_buf:blob„segment_len) ;

Эти действия повторяются в цикле, пока не будут записаны все сегменты BLOB.

4. Закрывает BLOB-курсор для вставки:

EXEC SQL

CLOSE BLOBCURSOR;

5. Когда процесс создания нового BLOB завершен, выполняется команда UPDATE, чтобы заменить старый BLOB в таблице новым, как показано ниже:

EXEC SQL UPDATE TBOOK SET

REFERAT = :blob_id;

WHERE CURRENT OF TABCURSOR;

Курсор таблицы TABCURSOR указывает на строку, установленную при объявлении курсора, а затем выбирает ее для обновления.

Удаление данныхБШБ Существует два метода для удаления BLOB. Во-первых, можно удалить строку, содержащую BLOB. Во-вторых, можно модифицировать строку и установить столбец BLOB в NULL или в BLOB ID другого BLOB (например, нового BLOB, созданного для модификации данных существующего).

Следующая команда удаляет текущие данные BLOB в столбце REFERAT таблицы ТВООК, устанавливая его в NULL:

Пример 6.13

EXEC SQL UPDATE ТВООК SET

REFERAT = NULL;

WHERE CURRENT OF TABCURSOR;

To же самое можно сделать и используя интерактивный SQL, поскольку содержимое BLOB здесь не используется.

Пример 6.14

UPDATE ТВООК SET

REFERAT = NULL;

WHEREUNIKEY = 12 3;

Удаление целиком строки с BLOB полностью аналогично удалению любой другой строки таблицы.

DELETE FROM ТВООК WHEREUNIKEY = 12 3 ;

Данные BLOB не удаляются немедленно при выполнении команды удаления. Фактическое удаление происходит, когда InterBase выполняет очистку версии. Подробнее работа с версиями описана в гл. 9. Следующий фрагмент кода иллюстрирует, как освободить память после удаления BLOB:

Пример 6.15

EXEC SQL

UPDATE TABLE SET BLOB_COLUMN = NULL WHERE ROW = :myrow;

EXEC SQL COMMIT;

/* Выполняем текущие действия */

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

/* Запускаем чистку базы */

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

Доступ к данным BLOB через вызовы API

В дополнение к доступу к данным BLOB, использующим SQL, InterBase API обеспечивает подпрограммы для доступа к данным BLOB. Следующие вызовы API обеспечивают доступ и управления данными BLOB.

Таблица 6.5. Функции API для работы с BLOB

Функция Описание
isc_blob_default_desc() Загружает структуру дескриптора BLOB заданной по умолчанию информацией о BLOB
isc_blob_gen_bpb() Генерирует буфер параметров BLOB (ВРВ) исходного и целевого дескрипторов BLOB, чтобы обеспечить . динамический доступ к подтипу BLOB . и используемой . кодовой таблице (набору) ■ символов
isc_blob_info() i Возвращает ■ информацию' об открытом BLOB
isc_blob_lookup_desc(} Просматривает' и записывает в дескриптор BLOB подтип, набор символов и размер сегмента BLOB'
isc_blob_set_desc(), Устанавливает в ■ полях дескриптора BLOB ■ значения, указанные в параметрах isc_blob_set_desc ■ ()
isc_cancel_blob() i Отказывается. от BLOB ■ и освобождает ■ оперативную, память,
isc_close_blob() i Закрывает ■ открытый BLOB.
isc_create_bl0b2()1 Создает ■ контекст- для^ сохранения; BLOB, открывает BLOB ■ для ■ записи и определяет фильтр^ (необязательная ■ опция), который нужно использовать, что-' бы транслировать данные: BLOB ■ из одного■ подтипа, в^ другой
Функция Описание
isc_get_segment() Читает сегмент из открытого BLOB
isc_open_blob2() Открывает существующий BLOB для выборки и необязательной фильтрации
isc_put_segment() Записывает сегмент BLOB

Примеры использования API приведены в разделе, описывающем работу с UDF (фильтры BLOB).

ДОСТУП К BLOB ИЗ ПРОГРАММ НА C++ BUILDER И DELPHI

Программирование доступа к BLOB из прикладных программ, используя механизм BLOB-курсоров с предварительной обработкой препроцессором GPRE, требует значительных усилий и чревато большим количеством ошибок. Для прикладных программистов одним из лучших решений при реализации приложений, работающих с базами данных в среде Windows, является выбор в качестве средства разработки программ систем C++ Builder и Delphi фирмы Inprise (Borland).

Рассмотрим порядок работы с BLOB в C++ Builder.

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

При работе с любыми базами данных, включая и локальные файлы, необходимо указать источник данных и описать свойства этих данных и порядок их обработки. Для такого рода работ в C++ Builder (Delphi) предусмотрены специальные объекты. Прежде всего, это такие объекты, как ТТаЫе (таблица) или TQuery (запрос).

При работе с ними необходимо описать некоторые из их свойств. Прежде всего, нужно указать откуда берутся данные. Для этого используется свойство объекта DatabaseName, задающее имя базы данных. Далее нужно указать либо имя таблицы базы данных при работе с таблицами в свойстве TableName объекта ТТаЫе, либо записать команду SQL (в данном случае Select) в свойстве SQL объекта TQuery.

Указанные объекты используют механизм BDE (Borland Database Engine), позволяющий одинаково работать с различными базами данных. Поскольку BDE может работать с разными СУБД, то предварительно нужно провести настройку BDE, это достаточно простая и к тому же разовая процедура. О ней мы поговорим несколько позже. Если используются последние версии C++ Builder, то в них есть средства прямого доступа к InterBase. В этом случае можно воспользоваться объектами ТШТаЫе или TIBQuery. Чтобы не привязываться к конкретным версиям продуктов, остановимся на доступе к данным через BDE.

ВизуализацияикорректировкаданныхБЬОБ Прежде всего, рассмотрим выдачу данных BLOB в визуальный объект. При этом допустима и корректировка этого объекта, которая отражается в базе данных.

Пример 6.16

Итак, создаем форму и помещаем на нее 4 объекта:

• Table 1 - TTable, в котором описываем привязку к базе данных. Пусть в BDE имя нашей тестовой базы Test.gdb - TESTLIBR, тогда свойство DatabaseName будет TESTLIBR, свойство TableName (имя таблицы) - ТВООК.

• DataSourcel - TTDataSource - объект необходимый для связи таблицы с визуальными объектами. Его свойство DataSet установим в Table 1, связав TDataSourcel с нужной нам таблицей.

• DBGridl - TDBGrid - объект для просмотра и редактирования данных в табличной форме. Свойство объекта DataSource установим в DataSourcel. Теперь мы через DataSourcel связали наш визуальный объект с таблицей ТВООК нашей базы.

• DBMemol - TDBMemo - объект для просмотра редактирования текстов, хранящихся в базе данных. Свойство объекта DataSource установим в DataSourcel. Теперь через DataSourcel мы связываем DBMemol с таблицей ТВООК, а, указав в свойстве DataField значение REFERAT, указали, что в нем будет отображаться содержимое поля REFERAT, представляющее собой текстовый BLOB.

Установим теперь свойство Active объекта Table 1 в true (это эквивалентно открытию таблицы).

Результат работы представлен на рис. 6.2.

Представление данных базы с BLOB на экране

Рис. 6.2. Представление данных базы с BLOB на экране.

На этом можно и закончить нашу работу. При перемещении по таблицы в окне DBMemol будет высвечиваться содержимое очередного реферата. То же самое можно проделать и с BLOB полями, хранящими графические данные, только вместо объекта TDBMemo нужно будет поместить объект TDBImage.

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

То же самое можно сделать и использую вместо объекта TTable объекта TQuery.

В этом случае вместо указания имени таблицы нужно указать порядок выборки данных. Для этого нужно указать соответствующую команду SELECT. Для ее задания используется свойство SQL объекта TQuery. В нашем случае это может быть конструкция вида SELECT * FROM ТВООК Для того чтобы автоматически генерировались команды по обновлению данных, свойство RequestLive должно быть установлено в true. Естественно не по любому запросу можно просто сгенерировать необходимые команды изменений, например, если выбираются данные из пяти таблиц с вычисляемыми значениями, то такая генерация просто невозможна, поэтому свойство RequestLive=true применимо только к выборкам из одной таблицы. Подробное описание этих ограничений можно найти в документации по C++Builder и Delphi, но это выходит за рамки данной книги.

Если выбираемые данные не нужно корректировать, то ограничений на вид команды SELECT нет, кроме того процедуры корректировки данных по результатам выборки можно описать и явно, предусматривая соответствующие команды INSERT, UPDATE и DELETE, тогда также команда SELECT может быть любой, например: select * FROM PBOOKAUTHOR

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

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

Пример 6.17

... // Находим нужную строку

TBLOBField * tt=

dynamic_cast<TBLOBField *>

(Tablel->FieldByName( "REFERAT") ) ;

// Настраиваемся на работу с BLOB tt->SaveToFile("AAA.AAA");

// Записываем данные BLOB в файл При использовании для записи данных из BLOB объекта TQuery код будет совершенно аналогичным:

. . . // Находим нужную строку

TBLOBField * tt= dynamic_cast<TBLOBField *>

(Queryl->FieldByName(“REFERAT"));

// Настраиваемся на работу с BLOB tt->SaveToFile("AAA.AAA");

// Записываем данные BLOB в файл Записьиз файлавBLOB

Запись данных в BLOB из файла с использованием объекта TTable иллюстрируется следующим примером:

Пример 6.18

. . . // Находим нужную строку

TBLOBField * tt= dynamic_cast<TBLOBField *>

(Tablel->FieldByName("REFERAT”));

// Настраиваемся на работу с BLOB Tablel->Edit();

// Переводим таблицу в режим редактирования tt->LoadFromFile("AAA.AAA");

// Записываем данные из файла в BLOB

При использовании для записи данных в BLOB объекта TQuery код примет следующий вид:

// Запрос должен быть обновляемым ! !

// то есть свойство RequestLive=true

• • . // Находим нужную строку

TBLOBField * tt= dynamic_cast<TBLOBField *>

(Queryl->FieldByName("REFERAT"));

// Настраиваемся на работу с BLOB Queryl->Edit();

// Переводим запрос в режим редактирования tt->LoadFromFile("AAA.AAA");

// Записываем данные из файла в BLOB

Если запрос не является обновляемым, то необходимо выдать явно команду UPDATE. Это можно сделать многими способами. Например, можно поступить так.

Установить свойство TQuery CashedUpdate=true. В этом случае вносимые в поля запроса изменения не посылаются в базу, а просто запоминаются на клиенте. Для внесения изменений необходимо выполнить явным образом команду обновления.

Рассмотрим подобное обновление на следующем примере.

Пример 6.19

Для реализации дополнения необходимо выполнить ряд действий.

• Добавить в форму объект TUpdateSQL, в котором хранятся тексты SQL для внесения изменений. Для них предусмотрены 3 свойства: DeleteSQL, ModifySQL и InsertSQL.

• Связать объект TQuery с TUpdateSQL, задав в свойстве объекта TQuery UpdateObject имя объекта TUpdateSQL. В нашем случае UpdateSQLl.

• В свойства объекта UpdateObject 1 записать запросы на изменение данных:

DeleteSQL

delete from ТВООК where UNIKEY = :OLD_UNIKEY

ModifySQL

update TBOOK set BOOKNM = :BOOKNM,

REFERAT = -.REFERAT

where UNIKEY = :OLD_UNIKEY

InsertSQL

insert into TBOOK (MATHERKEY, BOOKNM, REFERAT) values (MATHERKEY, :BOOKNM, :REFERAT)

Для нашего примера достаточно только свойства ModifySQL, остальные приведены чисто справочно. Значения параметров запросов на изменение данных автоматически берутся на основе значений текущей строки родительского запроса Query 1.

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

TBLOBField * tt= dynamic_cast<TBLOBField *>

(Queryl->FieldByName(“REFERAT"));

/ / Настраиваемся на работу с BLOB Queryl->Edit();

// Переводим запрос в режим редактирования

tt->LoadFromFile("AAA.AAA");

// Записываем данные из файла в BLOB // (пока только на клиенте)

UpdateSQLl->Apply(ukModify);

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

Пример 6.20

. . . // Находим нужную строку

TBLOBField * tt= dynamic_cast<TBLOBField *>

(Queryl->FieldByName("JOB_REQUIREMENT"));

// Настраиваемся на работу с BLOB TBLOBStream *bs = new TBLOBStream(tt, bmRead);

// Создаем поток для чтения из выбранного BLOB Memol->Lines->LoadFromStream(bs);

// Читаем данные из потока в поле приложения delete bs; // удаляем поток Чтениеданныхв BLOB из потока Пример 6.21

Queryl->Edit();

// Переводим запрос в режим редактирования TBLOBField * tt=dynamic_cast<TBLOBField *> (Queryl->FieldByName("JOB_REQUIREMENT"));

// Настраиваемся на работу с BLOB TMemoryStream *bs = new TMemoryStream(); Memol->Lines->SaveToStream(bs);

// Заполняем поток данными из приложения tt->LoadFromStream(bs);

// Записываем данными в BLOB . delete bs; // удаляем поток

// Выдаем команду на обновление базы (см. пример 6.19)

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

6.1. обзоры | Введение в InterBase | 6.3. функции пользователя (udf)


Введение в InterBase



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

  • Февраль
    2019
  • Пн
  • Вт
  • Ср
  • Чт
  • Пт
  • Сб
  • Вс