|
|
Эта глава описывает тип данных переменной длины, называемый BLOB (Binary Large Object, большой двоичный объект) . Обычно этот тип данных применяется для хранения изображений, аудио- , видео- информации и вообще может применяться для любых типов данных. Если хочется как то преобразовывать BLOB, то для этого существуют специальные функции называемые BLOB фильтры, их можно даже писать самому. Следующая таблица представляет в алфавитном порядке функции используемые для работы с BLOB.
Кроме управления данными обычными способами, подобными управлению другими типами данных, Interbase предоставляет более гибкие правила типов данных для данных BLOB. Поскольку существует много собственных типов данных разработчика, то вы можете определять их как данные BLOB, Interbase работает с ними как со своими и позволяет Вам определять ваш собственный тип данных, называемый подтип BLOB. Interbase также предоставляет два своих предопределенных подтипа: 0, неструктурированный подтип, вообще применимый к любым двоичным данным или данным неопределенного типа, и 1,применимый к простому тексту. Пользовательские данные должны быть всегда представлены как отрицательные числа от –1 до –32678. Подтип BLOB определяет как определен BLOB столбец. Приложение ответственно за то, чтобы данные, хранимые в столбце BLOB согласовывались с его подтипом; Interbase не проверяет тип или формат данных BLOB. Конечно чем хранить данные Blob непосредственно в поле Blob записи таблицы, InterBase хранит там BlobID. BlobID является уникальным числовым значением которое ссылается на данные Blob. Данные Blob хранятся в другом месте в базы данных, в ряде сегментов Blob, по сегментам и осуществляется чтение и запись BLOB. Сегменты Blob могут иметь изменяющуюся длину. Длина индивидуального сегмента определяется при записи. Сегменты удобны при работе с данными, который является слишком большим для одного буфера памяти приложения. Но не необходимо использовать множественные сегменты; Вы можете помещать все ваши данные Blob на единственном сегменте. Операции над BLOB даннымиInterbase поддерживает следующие операции над BLOB данными:
API Функции динамического SQL (DSQL) и структура данных XSQLD A необходимы, чтобы выполнить SELECT, INSERT, и инструкции UPDATE, требующиеся, чтобы выбирать, вставлять, или модифицировать уместные данные Blob. 6.1.Чтение данных из BLOB.Эти шесть шагов требуются для чтения данных из существующего BLOB: 1. Создается обычная инструкция SELECT для выбора строки содержащей BLOB столбец. 2. Подготавливается структура для вывода данных XSQLDA. 3. Подготавливается SELECT инструкция. 4. Выполняется инструкция. 5. Выбираем строки одну задругой 6. Читаем и обрабатываем BLOB данные для каждой строки. Опишем все это подробнее, для непонимающих. 6.2.Создание SELECT инструкцииchar *str = "SELECT PROJ_NAME, PROJ_DESC, PRODUCT FROM PROJECT WHERE \ PRODUCT IN ("software", "hardware","other") ORDER BY PROJ_NAME"; Подготовка структуры вывода XSQLDA1. Объявляем переменную содержащую 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 (BlobID), а не данные Blob, в памяти на которую кажет sqldata, когда строки данных выбраны, так что sqldata должна указывать на память с размером нужным для хранения BlobID. Следующий пример кода иллюстрирует назначения для 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 так как нет входных параметров */ ); Эта инструкция создает список выбора, это строки возвращаемые после выполнения инструкции. 6.2.Извлечение выбранных строкКонструкция цикла извлечения используется, чтобы извлечь(в XSQLDA вывода) данные столбцов для отдельной строки из списка выбора и обработать каждую строку прежде, чем следующая строка будет выбрана. Каждое выполнение isc_dsql_fetch () выбирает данные столбцов соответствующие подструктуры XSQLVAR структуры out_sqlda. Для столбца Blob, выбирается BlobID не являющийся фактическими данными, а просто указатель на них. 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 (seenext section) */ } if (fetch_stat != 100L) { /* isc_dsql_fetch returns 100 if no morerows remain to be retrieved */ SQLCODE = isc_sqlcode(status_vector); isc_print_sqlerror(SQLCODE,status_vector); return(1); } Чтение и обработка BLOB данныхДля чтения и обработки BLOB данных Объявите и инициализируйте BLOB дескриптор isc_blob_handle blob_handle; /* Объявление BLOB дескриптора. */ blob_handle= NULL; /* Уставите его в NULL перед использованием*/ Создайте буфер для хранения каждого прочитанного BLOB сегмента. Его размер должен быть максимальным размером сегмента в вашей программе используемой для чтения BLOB. charblob_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); 6.2.Запись данных в BLOBПеред тем как создать новый BLOB и записать туда данные вы должны сделать следующее. Включить BLOB данные в строку вставляемую в таблицу Заменить данные ссылающиеся на BLOB столбец строки Обновить данные ссылающиеся на BLOB столбец строки Вносимый в столбец Blob фактически не содержит данных Blob. Он содержит BlobID ссылающийся на данные, которые сохранены в другом месте. Так, чтобы установить или изменить столбец Blob, Вы должны установить (или сбросить) BlobID, сохраненный в нем. Если столбец Blob содержит BlobID, и Вы изменяете столбцы относящиеся к различным Blob (или содержащим NULL), Blob на который ссылается предварительно сохраненный BlobID будет удален в течение следующей сборки "мусора".(????) Все эти операции требуют следующих шагов: 1. Подготовьте соответствующую инструкцию DSQL. Это будет инструкция INSERT, если Вы вставляете новую строку в таблицу, или инструкция UPDATE для изменения строки. Каждая из этих инструкций будет нуждаться в соответствующей структуре ввода XSQLDA для передачи параметров инструкции во время выполнения. BlobID нового Blob будет одним переданных значений 2. Создайте новый BLOB, и запишите в него данные. 3. Свяжите BLOB ID нового BLOB со столбцом таблицы строк над которой вы будете выполнять INSERT и UPDATE. Примечание: вы не можете непосредственно обновлять BLOB данные. Если вы желаете модифицировать BLOB данные, вы должны: Создать новый BLOB Прочитать данные из старого BLOBA в буфер где вы сможете отредактировать и модифицировать их. Записать измененные данные в новый BLOB. Подготовить и выполнить инструкцию UPDATE которая модифицирует BLOB столбец содержащий BLOBID нового 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 с BLOBID, и устанавливает blob_id к указателю на BLOBID. 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 previousisc_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 ); 6.3.Удаление BLOBсуществуют четыре способа удаления BLOB. Удаляем строку содержащую BLOB. Вы можете использовать DSQL для выполнения DELETE инструкции. Заменяем различные BLOB. Если Blob столбец содержит BlobID, и вы модифицируете столбец ссылающийся на разные BLOB, ранее сохраненный BLOB будет удален следующей сборкой “мусора”. Сбрасываем в NULL столбец ссылающийся на BLOB, к примеру, используя DSQL инструкцию как следующую: UPDATE PROJECT SET PROJ_DESC = NULLWHERE 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 Буфер результатов содержит серию кластеров информации по каждому запрошенному элементу. Каждый кластер содержит три части. Первый байт определяет тип возвращенной информации. Второй байт – число определяющее число байт до конца кластера (длина инфо) Значение хранимое в переменном числе байт, которое интерпретируется в зависимости от типа первого байта кластера Следующая таблица показывает элементы информацию о которых можно получить Элемент Возвращаемое значение
В дополнение к этой информации IB возвращенной ответ на запрос, IB может также возвратить один или несколько сообщений состояния в буфер результата. Каждое сообщение состояния есть беззнаковый байт в длине
6.4.Пример вызова 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, setby isc_attach_database() */ &tr_handle, /* transaction handle, setby isc_start_transaction() */ &blob_handle, /* set by thisfunction to refer to the Blob */ &blob_id, /* Blob ID of the Blob toopen */ 0, /* BPB length = 0; no filter will beused */ NULL /* NULL BPB, since no filter willbe used */ ); if (status_vector[0] == 1 &&status_vector[1]) { isc_print_status(status_vector); return(1); } isc_blob_info( status_vector, &blob_handle, /* Set inisc_open_blob2() call above. */ sizeof(blob_items),/* Length ofitem-list buffer. */ blob_items, /* Item-list buffer. */ sizeof(res_buffer),/* Length of resultbuffer. */ 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 theresult 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; }; 6.5.Blob дескрипторыBlob дескриптор используется для предоставления динамического доступа к BLOB информации. К примеру, он может быть использован для хранения информации о BLOB данных для фильтрации, еще как кодовая страница для текстовых BLOB данных и информации о подтипе текстовых данных и не текстовых данных. Два дескриптора Blob необходимы всякий раз, когда фильтр используется при записи или чтении Blob: один описывать данные источника фильтра, и другой цель. BLOB дескриптор это структура определенная в заголовочном файле ibase.h как следующая: typedef struct { short blob_desc_subtype; /* type of Blobdata */ short blob_desc_charset; /* characterset */ short blob_desc_segment_size; /* segmentsize */ 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 previousisc_attach_database() call. */ &tr_handle, /* Set by previousisc_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 не могут быть созданы. 6.6.Использование ваших собственных фильтровВ отличие от стандарта фильтров InterBase, которые конвертируют подтипом 0 в подтип 1 и наоборот, внешний фильтр Blob - вообще часть библиотеки подпрограмм, которые Вы создаете и связываете с приложением. Вы можете писать, Blob на C или Паскаль (или любой язык, который может называться из C).Чтобы использовать ваши собственные фильтры, следуйте по этим шагами: 1. Решите, какой фильтр Вы, должны написать. 2. Напишите фильтры в базовом языке. 3. Сформируйте общедоступную библиотеку фильтров. 4. Сделайте библиотеку фильтров доступной. 5. Определить фильтры для базы данных. 6. Напишите приложение, которое требует фильтрацию. Шаги 2, 5 и 6 будут лучшеописаны в следующих разделах. 6.7.Объявление внешнего фильтра 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 определяют внешнюю подпрограмму, которую вызывает, когда вызывается фильтр. Параметр MODULE_NAME определяет filter.dll, динамически загружаемую библиотеку, содержащую выполнимый код фильтра. Параметр ENTRY_POINT определяет точку входа в DLL. Хотя пример показывает только простое имя файла, это - хорошая практика для определения полного квалифицированного пути, начиная с пользователей вашего приложения желающих загрузить файл. 6.8.Создание внешних Blob фильтровЕсли Вы хотите создавать ваши собственные фильтры, Вы должны иметь детальное понимание типов данных , которые Вы планируете конвертировать. InterBase не делает строгой проверки типа данных на данные Blob; это - ваша ответственность. Определений функций фильтров При написании фильтра, Вы должны включить точку входа, известной функции фильтра, в секции объявления программы. InterBase вызывает функцию фильтра, когда приложение выполняет операции на Blob, определенные для использования фильтра. Вся связь между InterBase и фильтром происходит через функцию фильтра. Функция самого фильтра может вызывать другие функции, которые включает исполняемая программу фильтра. Вы объявляете имя функции фильтра и имя выполняемой программы - фильтра с параметрами ENTRY_POINT И MODULE_NAME в инструкции DECLAREFILTER. Функция фильтрадолжна иметь следующее объявление, вызывающее последовательность: 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. 6.9.Определение управляющей структуры 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; /* Экземпляр ofisc_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_LONGctl_max_segment; /* Длина самого длинного BLOB сегмента */ ISC_LONGctl_number_segments; /* Полное число сегментов */ ISC_LONGctl_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).
6.10.Программирование действий функции фильтраКогда приложение вызывает функцию APIBlob для 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 Следующая таблица перечисляет действия, и определяет, когда функция фильтра вызвается длякаждого специфического действия. Большинство действий - результат событий, которые происходят, когда приложение вызывает функцию APIBlob.
6.11.Написание приложений которые требуют фильтрацииТребование фильтрации BLOB данных, читаемых или пишущихся в BLOB, происходи последующим шагам. 1. Создается буфер параметров BLOB(BPB) определяющий целевой или исходный подтип, и необязательный кодовый набор символов (для текстовых подтипов) 2. Вызовите или isc_open_blob2 () или isc_create_blob2 () чтобы открыть Blob для чтения или для записи, соответственно. В запросе, передайте BPB, чью информацию, InterBase будет использовать, чтобы определить который фильтр должен быть вызван. 6.12.Понятие буфера параметров 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. 6.13.Генерация буфера 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 INTERBASE5 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'snot needed in this case */ 3. Объявите массив символов который будет использоваться как BPB. Удостоверьтесь чтобы он был достаточного размера. charbpb[20]; 4. Объявите переменную unsignedshort в которою 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 */ ); 6.14.Создание буфера параметров BLOB напрямуюBPB можно создавать напрямую. BPB состоит из следующих частей: 1. Байт определяющий версию BPB, это константа isc_bpb_version1. 2. Ряд кластеров из одного или нескольких байт Каждый кластер состоит из следующих частей: 1. Первый байт это тип параметра. Это одна из констант определенная для всех типов параметров(например, isc_bpb_target_type). 2. Второй байт определяет число байт оставшихся до конца кластера (это и есть длина самого параметра) 3. Переменное число байт интерпретирующееся в зависимости от типа параметра. Примечание: Все числа в BPB представляются в универсальном формате сначала идет младший байт потом более старший. Следующая таблица показывает список типов параметров
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, /* sourcesubtype*/ }; Конечно, если Вы не знаете исходные и целевые подтипы до времени выполнения, Вы можете инициализировать 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); }
|
Дизайн: Piton Alien |