FirebirdSQL logo

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

Создание базы данных и соединение с существующей базой данных

Прежде всего нам нужно получить доступ к интерфейсуIMaster. IMaster — это основнойинтерфейс Firebird, необходимый для доступа ко всем остальныминтерфейсам. Поэтому существует особый способ доступа к нему —единственное, что нужно это использование простой функции OO API,называемой fb_get_master_interface(). Эта функция не имеет параметрови всегда завершается успешно. Существует один и только один экземпляр IMaster длякаждой клиентской библиотеки Firebird, поэтому не нужно заботиться обосвобождении памяти, используемой мастер-интерфейсом. Самый простой способ получить к нему доступиз вашей программы — использовать соответствующую глобальную или статическую переменную:

static IMaster* master = fb_get_master_interface();

Для многих методов, используемых в API Firebird, первым параметромявляется интерфейс IStatus. Это логичнаязамена ISC_STATUS_ARRAY, но работает отдельно с ошибками ипредупреждениями (не смешивая их в одном массиве), может содержать внутринеограниченное количество ошибок и (это важно, если вы планируетереализовать IStatus самостоятельно) всегда сохраняет строки, на которыеон ссылается внутри интерфейса. Обычно для вызова других методовтребуется хотя бы один экземпляр IStatus. Вы можете получить его изIMaster:

IStatus* st = master->getStatus();

Если по какой-либо причине метод getStatus() не работает (OOM дляпримера), то он возвращает NULL — в этом случае очевидно, что мы неможем использовать общий метод для сообщений об ошибке, основанный наIStatus.

Теперь мы рассмотрим первый интерфейс, напрямую связанный с обращениями кбазе данных. Это IProvider-интерфейс,называемый таким образом, потому что именно этот интерфейс должен бытьреализован любым поставщиком в Firebird. В клиентской библиотекеFirebird есть собственная реализация IProvider, которую необходимоиспользовать для запуска любых операций с базой данных. Чтобы получитьего, мы вызываем метод getDispatcher интерфейса IMaster:

IProvider* prov = master->getDispatcher();

При подключении к существующей базе данных или тем более при создании новой частобывает необходимо передать в вызов API множество дополнительных параметров(логин/пароль, размер страницы для новой базы и т.д.). Иметь отдельные параметрына уровне языка почти нереально — нам придется слишком часто модифицировать вызов,чтобы добавить новые параметры, и их количество будет очень большим,несмотря на то, что обычно их нужно передавать не слишком много.Поэтому для передачи дополнительных параметров используетсяспециальная структура данных в памяти, называемая блок параметров базыданных (database parameters block или DPB). Его формат чётко определён, иэто даёт возможность построить DPB байт за байтом. Однако гораздо прощеиспользовать специальный интерфейсIXpbBuilder, упрощающий создание блоков различных параметров.Чтобы получить экземпляр IXpbBuilder, необходимо знать об ещё одном универсальном интерфейсеFirebird API — IUtil. Это своего родаконтейнер для вызовов, которые плохо подходят для размещения в другихместах. Итак, мы делаем следующее

IUtil* utl = master->getUtilInterface();
IXpbBuilder* dpb = utl->getXpbBuilder(&status, IXpbBuilder::DPB, NULL, 0);

Этот код создает пустой конструктор блоков параметров типа DPB. Теперьдобавление необходимого параметра в него тривиально:

dpb->insertInt(&status, isc_dpb_page_size, 4 * 1024);

будет создавать базу данных с размером страницы 4 Кб и значениями

dpb->insertString(&status, isc_dpb_user_name, "sysdba");
dpb->insertString(&status, isc_dpb_password, "masterkey");

смысл которых понятен.

Следующее специфично для C++: Мы почти готовы вызвать методcreateDatabase() экземпляра IProvider, но перед этим необходимо сказатьнесколько слов о концепции Status Wrapper (обёртка над статусом). StatusWrapper не является интерфейсом, это очень тонкая обёртка надинтерфейсом IStatus. Она позволяет настраивать поведение C++ API(изменить способ обработки ошибок, возвращаемых в интерфейсе IStatus).Первое время мы рекомендуем использовать ThrowStatusWrapper, которыйвызывает исключение C++ каждый раз, когда в IStatus возвращается ошибка.

ThrowStatusWrapper status(st);

Теперь мы можем создать новую пустую базу данных:

IAttachment* att = prov->createDatabase(&status, "fbtests.fdb",
    dpb->getBufferLength(&status), dpb->getBuffer(&status));
printf("Database fbtests.fdb created\n");

Обратите внимание, что мы не проверяем статус после вызова createDatabase(),потому что в случае ошибки будет возбуждено исключение C++ или Pascal(поэтому в вашей программе очень полезно иметь try/catch/exceptсинтаксис). Мы также используем две новые функции из IXpbBuildergetBufferLength() и getBuffer(), которые извлекают данные из интерфейсав родном формате DPB. Как видите, нет необходимости явно проверять статусфункций, возвращая промежуточные результаты.

Отсоединение от только что созданной базы данных тривиально:

att->detach(&status);

Теперь осталось окружить все операторы блоком try и написать обработчикв блоке catch. При использовании ThrowStatusWrapper, вы всегда должныперехватывать определённый в C++ API исключение класса FbException,в Pascal вы также должны работать с классом FbException. Блокобработки исключений в простейшем случае может выглядеть так:

catch (const FbException& error)
{
    char buf[256];
    utl->formatStatus(buf, sizeof(buf), error.getStatus());
    fprintf(stderr, "%s\n", buf);
}

Обратите внимание, здесь мы используем ещё одну функцию изIUtilformatStatus(). Она возвращаетв буфере текст, описывающим ошибку (предупреждение), сохранённую впараметре IStatus.

Чтобы подключиться к существующей базе данных используйте методattachDatabase() интерфейса IProvider вместо createDatabase(). Всепараметры одинаковы для обоих методов.

att = prov->attachDatabase(&status, "fbtests.fdb", 0, NULL);

В этом примере не использует никаких дополнительных параметров DPB.Учтите, что без логина/пароля любое удалённое подключение будетнеудачно, если не настроена доверительная аутентификация. Конечноинформация для логина может быть предоставлена окружением (впеременных ISC_USER и ISC_PASSWORD), так же как это было раньше.

Папка examples содержит завершённые примеры, в том числе и примерысоздания базы данных — 01.create.cpp и 01.create.pas. При чтенииданного документа, полезно построить (build) примеры и попытатьсязапустить их.

docnext count = 76

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

Чтобы начать пользоваться сервисами (службами), прежде всего необходимоподключиться к менеджеру сервисов. Это делается с помощью методаattachServiceManager() интерфейсаIProvider. Этот метод возвращаетинтерфейс IService, который позжеиспользуется для связи с сервисом. Чтобы подготовить SPB для подключенияк диспетчеру сервисов, вы можете использовать IXpbBuilder:

IXpbBuilder* spb1 = utl->getXpbBuilder(&status, IXpbBuilder::SPB_ATTACH, NULL, 0);

spb1->insertString(&status, isc_spb_user_name, "sysdba");
spb1->insertString(&status, isc_spb_password, "masterkey");

и подключится:

IService* svc = prov->attachServiceManager(&status, "service_mgr",
    spb1->getBufferLength(&status), spb1->getBuffer(&status));

Используя IService, вы можете выполнять как доступные для служб действия— запускать службы, так и запрашивать различную информацию о запущенныхутилитах и сервере в целом. При запросе информации, есть одноограничение — формат блока параметров, используемый методом query(), вFirebird 4 не поддерживается IXpbBuilder. Вероятно, поддержка будетдобавлена в более поздних версиях, в Firebird 4 вам придется создавать ианализировать этот блок вручную. Формат этого блока повторяет старыйформат (используемый в ISC API) один в один.

Чтобы стартовать сервис, необходимо прежде всего создать соответствующийSPB:

IXpbBuilder* spb2 = utl->getXpbBuilder(&status, IXpbBuilder::SPB_START, NULL, 0);

и добавить к нему необходимые элементы. Например, для печати статистикишифрования для базы данных employee в SPB следует поместить следующее:

spb2->insertTag(&status, isc_action_svc_db_stats);
spb2->insertString(&status, isc_spb_dbname, "employee");
spb2->insertInt(&status, isc_spb_options, isc_spb_sts_encryption);

После этого сервис можно запустить с использованием метода start()интерфейса IService:

svc->start(&status, spb2->getBufferLength(&status), spb2->getBuffer(&status));

Многие запущенные службы (включая упомянутый здесь gstat) во времявыполнения возвращают текстовую информацию. Чтобы отобразить её,необходимо запросить эту информацию у запущенного сервиса построчно. Этоделается с помощью вызова метода query() интерфейсаIService с соответствующими блокамипараметров для приёма и отправки. Блок отправки может содержатьразличную вспомогательную информацию (например, тайм-аут запроса услужбы) или информацию, которая должна быть передана в служебнуюпрограмму stdin, или может быть пустым в простейшем случае. Блок приемадолжен содержать список тегов, которые вы хотите получать из службы. Длябольшинства утилит это единственный isc_info_svc_line:

const unsigned char receiveItems1[] = {isc_info_svc_line};

Кроме того, для запроса этой информации для неё необходим буфер:

unsigned char results[1024];

После этих предварительных шагов мы готовы запросить информацию изсервиса в цикле (каждая строка возвращается в одном вызове query()):

do
{
    svc->query(&status, 0, NULL,
               sizeof(receiveItems1), receiveItems1,
               sizeof(results), results);
} while (printInfo(results, sizeof(results)));

В этом примере мы предполагаем, что функция printInfo() возвращаетTRUE, пока сервис возвращает блок результатов, содержащий следующуювыходную строку, то есть до конца потока данных из сервиса. Форматблока результатов варьируется от сервиса к сервису, а некоторые сервисы,такие как gsec, создают исторические форматы, которые не являютсятривиальными для синтаксического анализа, но это выходит за рамки даннойглавы. Минимальный рабочий пример printInfo() присутствует в примере09.service.cpp.

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

const unsigned char receiveItems2[] = {isc_info_svc_server_version};

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

svc->query(&status, 0, NULL,
           sizeof(receiveItems2), receiveItems2,
           sizeof(results), results);

printInfo(results, sizeof(results));

После завершения сервисных задач не забудьте отключить сервис:

svc->detach(&status);

Работа с транзакциями

Только создание пустых баз данных определенно недостаточно для работы сРСУБД. Мы хотим иметь возможность создавать в базе данных различныеобъекты (например, таблицы и т. д.) и вставлять данные в эти таблицы. ВFirebird любая операция с базой данных выполняется под управлениемтранзакций. Поэтому прежде всего мы должны научиться стартоватьтранзакцию. Здесь мы не обсуждаем распределенные транзакции(поддерживаемые интерфейсом IDtc), чтобыизбежать ненужных для большинства пользователей сложностей. Запуск нераспределенной транзакции очень прост и выполняется через интерфейсподключения:

ITransaction* tra = att->startTransaction(&status, 0, NULL);

В этом примере используются параметры транзакции по умолчанию — TPB непередается методу startTransaction(). Если вам нужна транзакция спараметрами отличными от параметров по умолчанию, вы можете создатьсоответствующий IXpbBuilder идобавить к нему необходимые элементы:

IXpbBuilder* tpb = utl->getXpbBuilder(&status, IXpbBuilder::TPB, NULL, 0);
tpb->insertTag(&status, isc_tpb_read_committed);

и передать готовый TPB в startTransaction():

ITransaction* tra = att->startTransaction(&status, tpb->getBufferLength(&status),
    tpb->getBuffer(&status));

Интерфейс транзакции используется как параметр во множестве другихвызовах API, но сам он не выполняет никаких действий, кромефиксации/отката (commit/rollback) транзакции, может быть с сохранениемконтекста транзакции (retaining):

tra->commit(&status);

Вы можете посмотреть, как начинать и подтверждать транзакцию в примерах01.create.cpp и 01.create.pas.

Выполнение оператора SQL без входных параметров и возвращаемых строк

После старта транзакции мы готовы выполнять наши первые SQL операторы.Используемый для этого метод execute() вIAttachment является довольноуниверсальным, и может также использоваться для выполнения операторовSQL с входными и выходными параметрами (что типично для инструкцииEXECUTE PROCEDURE), но сейчас мы будем использовать наиболее простую егоформу. Могут быть выполнены как DDL, так и DML операторы:

att->execute(&status, tra, 0, "create table dates_table (d1 date)",
    SQL_DIALECT_V6, NULL, NULL, NULL, NULL);
tra->commitRetaining(&status);
att->execute(&status, tra, 0, "insert into dates_table values (CURRENT_DATE)",
    SQL_DIALECT_V6, NULL, NULL, NULL, NULL);

Как вы видите, интерфейс транзакции является обязательным параметром дляметода execute() (должен быть NULL, только если вы выполняете инструкциюSET TRANSACTION). Следующим параметром следует длина SQL оператора(может быть равна нулю, в этом случае используются правила C дляопределения длины строки), потом текст оператора и диалект SQL, которыйдолжен использоваться для него. Далее следует несколько NULL которыеподставляются для описания метаданных, и буферов входных параметров ивыходных данных. Полное описание этого метода представлено в интерфейсеIAttachment.

Выполнение SQL операторов с входными параметрами

Существует два способа выполнения оператора с входными параметрами.Выбор правильного метода зависит от того, нужно ли вам выполнять егоболее одного раза, и знаете ли вы заранее формат параметров. Когда этотформат известен, и оператор нужно запускать только один раз, тогда выможете воспользоваться одиночным вызовом IAttachment::execute(). Впротивном случае сначала необходимо подготовить SQL-запрос, после чегоего можно выполнять многократно с различными параметрами.

Чтобы подготовить SQL оператор для выполнения, используйте методprepare() интерфейса IAttachment:

IStatement* stmt = att->prepare(&status, tra, 0,
    "UPDATE department SET budget = ? * budget + budget WHERE dept_no = ?",
    SQL_DIALECT_V6, IStatement::PREPARE_PREFETCH_METADATA);

Если вы не собираетесь использовать описание параметров из Firebird(т.е. вы можете предоставить эту информацию самостоятельно), используйтеIStatement::PREPARE_PREFETCH_NONE вместоIStatement::PREPARE_PREFETCH_METADATA — это немного снизитклиент/серверный трафик и сохранит ресурсы.

В ISC API структура XSQLDA используется для описания формата параметровоператора. Новый API не использует XSQLDA — вместо неё используетсяинтерфейс IMessageMetadata.Набор входных параметров (а также запись, взятая из курсора) описываетсяв Firebird API таким же образом, далее называемый сообщением.IMessageMetadata передаётся в качестве параметра в методы обменасообщениями между программой и движком базы данных. Существует многоспособов получить экземпляр IMessageMetadata, вот некоторые из них:

  • получить из IStatement;

  • построить используяIMetadataBuilder интерфейс;

  • иметь собственную реализацию этого интерфейса.

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

IMessageMetadata* meta = stmt->getInputMetadata(&status);

Или мы можем построить сообщение метаданных самостоятельно. Для этогопрежде всего нам необходимо получить интерфейс построителя:

IMetadataBuilder* builder = master->getMetadataBuilder(&status, 2);

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

Теперь необходимо задать индивидуальные характеристики полей впостроителе. Минимально необходимыми являются типы полей и длина длястроковых полей:

builder->setType(&status, 0, SQL_DOUBLE + 1);

builder->setType(&status, 1, SQL_TEXT + 1);
builder->setLength(&status, 1, 3);

Новый API использует старые константы для типов SQL, наименьший бит, каки раньше, используется для обозначения возможности принимать nullзначение. В некоторых случаях имеет смысл установить подтип (для BLOB),набор символов (для текстовых полей) или масштаб (для числовых полей).Наконец, пришло время получить экземпляр IMessageMetadata:

IMessageMetadata* meta = builder->getMetadata(&status);

Здесь мы не обсуждаем собственную реализацию IMessageMetadata. Если вамэто интересно, то вы можете посмотреть пример 05.user_metadata.cpp.

Итак, мы получили экземпляр описания метаданных входных параметров. Нодля работы с сообщением нам также необходим буфер. Размер буфераявляется одной из основных характеристик сообщений метаданных ивозвращается методом getMessageLength() из IMessageMetadata:

char* buffer = new char[meta->getMessageLength(&status)];

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

double* percent_inc = (double*) &buffer[meta->getOffset(&status, 0)];
char* dept_no = &buffer[meta->getOffset(&status, 1)];

Кроме того, не забывайте установить NULL флаги:

short* flag = (short*)&buffer[meta->getNullOffset(&status, 0)];
*flag = 0;

flag = (short*) &buffer[meta->getNullOffset(&status, 1)];
*flag = 0;

После завершения манипуляций со смещениями, мы готовы получить значенияпараметров:

getInputValues(dept_no, percent_inc);

и выполнить подготовленный оператор:

stmt->execute(&status, tra, meta, buffer, NULL, NULL);

Два последних NULL в параметрах предназначены для выходных сообщений иобычно используются для оператора EXECUTE PROCEDURE.

Если вам не нужно получать метаданные из оператора и вы планируетевыполнить его только один раз, то вы можете выбрать более простой способ— используйте метод execute() из интерфейсаIAttachment:

att->execute(&status, tra, 0,
    "UPDATE department SET budget = ? * budget + budget WHERE dept_no = ?",
    SQL_DIALECT_V6, meta, buffer, NULL, NULL);

В этом случае вам вообще не нужно использоватьIStatement.

Пример того, как выполнить оператор UPDATE с параметрами, присутствует в02.update.cpp, вы также увидите, как возбужденное исключение втриггере/процедуре может быть перехвачено в программе на C++.

Открытие курсора и извлечение данных из него

Единственный способ получить строки данных, возвращаемых операторомSELECT в OO API — это использовать интерфейсIResultSet. Этот интерфейсвозвращается методом openCursor() как в IAttachment, так и в IStatement.openCursor() в большинстве аспектов похож на execute(), и решение какимобразом открыть курсор (с использованием подготовленного оператора илинепосредственно из интерфейса подключения) то же. В примерах03.select.cpp и 04.print_table.cpp используются оба способа.Обратите внимание на одно отличие метода openCursor() по сравнению сexecute() — никто не передает буфер для выходного сообщения вopenCursor(), он будет передан позже, когда данные будут извлечены изкурсора. Это позволяет открывать курсор с неизвестным форматом выходногосообщения (NULL передается вместо выходных метаданных). В этом случаеFirebird использует формат сообщения по умолчанию, который может бытьзапрошен через интерфейс IResultSet:

const char* sql = "select * from ..."; // some select statement

IResultSet* curs = att->openCursor(&status, tra, 0, sql, SQL_DIALECT_V6,
    NULL, NULL, NULL, NULL, 0);

IMessageMetadata* meta = curs->getMetadata(&status);

Позже эти метаданные могут использоваться для выделения буфера дляданных и разбора извлечённых строк.

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

IStatement* stmt = att->prepare(&status, tra, 0, sql, SQL_DIALECT_V6,
    IStatement::PREPARE_PREFETCH_METADATA);

IMessageMetadata* meta = stmt->getOutputMetadata(&status);

IResultSet* curs = stmt->openCursor(&status, tra, NULL, NULL, NULL, 0);

Мы получили (тем или иным способом) экземпляр описания метаданныхвыходных полей (строк в наборе данных). Для работы с сообщением намтакже нужен буфер:

unsigned char* buffer = new unsigned char[meta->getMessageLength(&status)];

В IResultSet есть много различных методов выборки, но когда курсороткрыт не с параметром SCROLL, то работает только fetchNext(), то естьможно перемещаться по записям только вперед. В дополнение к ошибкам ипредупреждениям в статусе метод fetchNext() возвращает код завершения,который может иметь значения RESULT_OK (когда буфер заполняетсязначениями для следующей строки) или RESULT_NO_DATA (когда в курсоребольше строк не осталось). RESULT_NO_DATA не является состоянием ошибки,это нормальное состояние после завершения метода, которое сигнализирует,что данных в курсоре больше нет. Если используется оболочка статуса(Status Wrapper), то исключение не бросается в случае возврата ошибки.Может быть возвращено еще одно значение — RESULT_ERROR — оно означаетотсутствие данных в буфере и ошибки в статусе векторе. Метод fetchNext()обычно вызывается в цикле:

while (curs->fetchNext(&status, buffer) == IStatus::RESULT_OK)
{
    // row processing
}

То, что происходит при обработке строк, зависит от ваших потребностей.Для получения доступа к определённому полю следует использовать смещениеполя:

unsigned char* field_N_ptr = buffer + meta->getOffset(&status, n);

где n - номер поля в сообщении. Этот указатель должен быть присвоенсоответствующему типу, в зависимости от типа поля. Например, для поляVARCHAR, следует использовать приведение к структуре vary:

vary* v_ptr = (vary*) (buffer + meta->getOffset(&status, n));

Теперь мы можем напечатать значение поля:

printf("field %s value is %*.*s\n",
       meta->getField(&status, n),
       v_ptr->vary_length,
       v_ptr->vary_length,
       v_ptr->vary_string);

Если вам нужна максимальная производительность, будет полезно кэшироватьнеобходимые значения метаданных, как это сделано в наших примерах03.select.cpp и 04.print_table.cpp.

Использование макросов FB_MESSAGE для статических сообщений

Работа с данными с использованием смещений довольно эффективна, нотребует написания большого количества кода. В C++ эту проблему можнорешить с помощью шаблонов, но даже по сравнению с ними наиболее удобнымспособом работы с сообщением является представление его в родном (длязаданного языка) форме — структуре в C/C++, записи в Pascal и т. д.Конечно это работает только в том случае, если формат сообщения известензаранее. Для создания таких структур в C++ в Firebird существуетспециальный макрос FB_MESSAGE.

FB_MESSAGE имеет 3 аргумента: имя сообщения (структуры), тип обёрткистатуса (status wrapper) и список полей. Использование первого и второгоаргумента очевидно, список полей содержит пары (field_type, field_name),где field_type является одним из следующих:

  • FB_BIGINT

  • FB_BLOB

  • FB_BOOLEAN

  • FB_CHAR(len)

  • FB_DATE

  • FB_DECFLOAT16

  • FB_DECFLOAT34

  • FB_DOUBLE

  • FB_FLOAT

  • FB_INTEGER

  • FB_INTL_CHAR(len, charSet)

  • FB_INTL_VARCHAR(len, charSet)

  • FB_SCALED_BIGINT(x)

  • FB_SCALED_INTEGER(x)

  • FB_SCALED_SMALLINT(x)

  • FB_SMALLINT

  • FB_TIME

  • FB_TIME_TZ

  • FB_TIME_TZ_EX

  • FB_TIMESTAMP

  • FB_TIMESTAMP_TZ

  • FB_TIMESTAMP_TZ_EX

  • FB_VARCHAR(len)

В сгенерированной препроцессором структуре типы integer и floatсопоставляются с соответствующими типами C, типы date и time — склассами FbDate иFbTime (все упомянутые здесь классынаходятся в пространстве имен Firebird), тип timestamp — с классомFbTimestamp, содержащим два публичныхчлена данных дату и время соответствующих классов, тип char — соструктурой FbChar и varchar — со структуройFbVarChar. Для каждого поля препроцессорсоздаст два члена данных — name для значения поля/параметра и nameNullдля индикатора NULL. Конструктор сообщений имеет 2 параметра — указательна оболочку статуса (status wrapper) и главный интерфейс (masterinterface):

FB_MESSAGE(Output, ThrowStatusWrapper,
    (FB_SMALLINT, relationId)
    (FB_CHAR(31), relationName)
    (FB_VARCHAR(100), description)
) output(&status, master);

Для статических сообщений использование FB_MESSAGE является самым лучшимвыбором, в то же время они легко могут быть переданы в методы execute,openCursor и fetch:

rs = att->openCursor(&status, tra, 0, sqlText,
      SQL_DIALECT_V6, NULL, NULL, output.getMetadata(), NULL, 0);

и используется для работы со значениями отдельных полей:

while (rs->fetchNext(&status, output.getData()) == IStatus::RESULT_OK)
{
  printf("%4d %31.31s %*.*s\n", output->relationId, output->relationName.str,
    output->descriptionNull ? 0 : output->description.length,
    output->descriptionNull ? 0 : output->description.length,
    output->description.str);
}

Пример использования макроса FB_MESSAGE для работы с сообщениямиприведен в примере 06.fb_message.cpp.

Работа с BLOB

Для BLOBs Firebird хранит в буфере сообщения идентификатор BLOB — 8байтовый объект, который должен быть выравнен по 4-байтной границе.Идентификатор имеет тип ISC_QUAD. ИнтерфейсIAttachment имеет 2 метода дляработы с BLOB — openBlob() и createBlob(), возвращающие интерфейсIBlob и имеющие одинаковый наборпараметров, но выполняющие несколько разные действия: openBlob()принимает BLOB идентификатор из сообщения и подготавливает BLOB длячтения, а createBlob() создает новый BLOB, помещает его идентификатор всообщение и подготавливает BLOB для записи.

Для работы с BLOBs прежде всего необходимо включить в сообщение ихBLOB-идентификаторы. Если вы получите метаданные из поля движка Firebirdсоответствующего типа, то этот идентификатор уже будет присутствовать. Вэтом случае вы просто используете его смещение (при условии, чтопеременная blobFieldNumber содержит номер поля BLOB) (и соответствующееNULL смещение для проверки NULL или установки NULL флага) для полученияуказателя в буфере сообщений:

ISC_QUAD* blobPtr =
  (ISC_QUAD*) &buffer[metadata->getOffset(&status, blobFieldNumber)];
ISC_SHORT* blobNullPtr =
  (ISC_SHORT*) &buffer[metadata->getNullOffset(&status, blobFieldNumber)];

Если вы используете статические сообщениями макрос FB_MESSAGE, то полеBLOB будет объявлено как тип FB_BLOB:

FB_MESSAGE(Msg, ThrowStatusWrapper,
    (FB_BLOB, b)
) message(&status, master);

ISC_QUAD* blobPtr = &message->b;
ISC_SHORT* blobNullPtr = &message->bNull;

Для создания нового BLOB, вызовите метод createBlob():

IBlob* blob = att->createBlob(status, tra, blobPtr, 0, NULL);

Последние два параметра требуются только в том случае, если вы хотитеиспользовать blob-фильтры или blob-поток, которые не рассматриваютсяздесь.

Теперь Blob интерфейс готов принять данные в BLOB. Используйте методputSegment() для отправки данных в движок:

void* segmentData;
unsigned segmentLength;
while (userFunctionProvidingBlobData(&segmentData, &segmentLength))
    blob->putSegment(&status, segmentLength, segmentData);

После отправки некоторых данных в BLOB не забудьте закрытьblob-интерфейс:

blob->close(&status);

Убедитесь, что null флаг не установлен (не требуется, если вы сбросиливесь буфер сообщений перед созданием BLOB):

*blobNullPtr = 0;

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

Чтобы прочитать blob, необходимо получить его идентификатор в сообщенииот ядра firebird. Это можно сделать с помощью методов fetch() илиexecute(). После этого используйте метод openBlob():

IBlob* blob = att->openBlob(status, tra, blobPtr, 0, NULL);

Blob интерфейс готов предоставить данные BLOB. Используйте методgetSegment() для получения данных из движка:

char buffer[BUFSIZE];
unsigned actualLength;

for(;;)
{
  switch (blob->getSegment(&status, sizeof(buffer), buffer, &actualLength))
  {
    case IStatus::RESULT_OK:
      userFunctionAcceptingBlobData(buffer, actualLength, true);
      continue;

    case IStatus::RESULT_SEGMENT:
      userFunctionAcceptingBlobData(buffer, actualLength, false);
      continue;

    default:
      break;
  }
}

Последний параметр в userFunctionAcceptingBlobData() — это флагдостижения конца сегмента — когда getSegment() возвращает код завершенияRESULT_SEGMENT, о чём будет уведомлена функция (в последний параметрпередан false), то есть этот сегмент прочитан не полностью, ипродолжение ожидается при следующем вызове.

Закончив работать с BLOB, не забудьте закрыть его:

blob->close(&status);

Пакетное изменение данных

Поскольку Firebird 4.0 поддерживает пакетное выполнение операторов с входными параметрами, это означаетотправку более чем одного набора параметров при выполнении оператора. Пакетный интерфейс разработан (в первую очередь)для удовлетворения требований JDBC по пакетной обработке подготовленных операторов, но имеет ряд серьезных отличий:

  • как и все операции с данными в firebird ориентированы на сообщения, а не на одно поле;

  • в качестве важного расширения пакетный интерфейс поддерживает встроенное использование BLOB-объектов(особенно эффективно при работе с небольшими BLOB-объектами);

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

Интерфейс IBatch (точно так же, как и IResultSet)может быть создан двумя способами – с использованием интерфейса IStatement илиIAttachment, в обоих случаях вызывается метод createBatch() соответствующего интерфейса.Во втором случае текст оператора SQL, который должен выполняться в пакете, передается непосредственно в createBatch().Настройка пакетной обработки осуществляется с помощью блока Batch parameters, формат которого более или менее похожна DPB v.2 – в начале тег (IBatch::CURRENT_VERSION), за которым следует набор широких скоплений: тег 1 байт, длина 4 байта,значение указанной длины байт. Возможные теги описаны в пакетном интерфейсе.Самый простой (и рекомендуемый) способ создать блок параметров для пакетного создания — использоватьсоответствующий интерфейс IXpbBuilder:

IXpbBuilder* pb = utl->getXpbBuilder(&status, IXpbBuilder::BATCH, NULL, 0);
pb->insertInt(&status, IBatch::RECORD_COUNTS, 1);

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

Чтобы создать пакетный интерфейс с нужными параметрами, передайте блок параметров в вызов createBatch():

IBatch* batch = att->createBatch(&status, tra, 0, sqlStmtText, SQL_DIALECT_V6, NULL,
  pb->getBufferLength(&status), pb->getBuffer(&status));

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

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

IMessageMetadata* meta = batch->getMetadata(&status);

Конечно, если вы передали свой собственный формат сообщений в пакет, вы можете просто использовать его.

Далее я предполагаю, что существует некоторая функция fillNextMessage(unsigned char* data, IMessageMetadata* metadata)и она может заполнить буфер data в соответствии с переданным форматом metadata. Для работы с сообщениями нам нужен буфер для данных:

unsigned char* data = new unsigned char[meta->getMessageLength(&status)];

Теперь мы можем добавить в пакет несколько сообщений с заполненными данными:

fillNextMessage(data, meta);
batch->add(&status, 1, data);

fillNextMessage(data, meta);
batch->add(&status, 1, data);

Альтернативный способ работы с сообщениями (с помощью макроса FB_MESSAGE) присутствует в примере использованияпакетного интерфейса 11.batch.cpp.

Наконец, пакет должен быть выполнен:

IBatchCompletionState* cs = batch->execute(&status, tra);

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

unsigned total = cs->getSize(&status);

Теперь выводим состояние каждого сообщения:

for (unsigned p = 0; p < total; ++p) printf("Msg %u state %d\n", p, cs->getState(&status, p));

Когда закончите анализ состояния завершения, не забудьте его удалить:

cs->dispose();

Полный пример печати содержимого IBatchCompletionState находитсяв функции print_cs() в примере 11.batch.cpp.

Если по какой-то причине вы хотите сделать пакетные буферы пустыми, не выполняя их(т.е. подготовиться к обработке новой порции сообщений), используйте метод cancel():

batch->cancel(&status);

Как и у остальных наших интерфейсов доступа к данным, у IBatch есть специальный метод для его закрытия:

batch->close(&status);

Вместо этого можно использовать стандартный вызов release(), если вас не волнуют ошибки:

batch->release();

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

Note
Замечание

JDBC не рекомендует использовать слишком большие пакеты, например, "Oracle рекомендует сохранять размеры пакетов в диапазоне от 50 до 100".Firebird поддерживает большие пакеты, но в любом случае должен ограничивать максимальный размер пакета — см. TAG_BUFFER_BYTES_SIZE.Если общий размер сообщений превышает этот предел, возвращается ошибка isc_batch_too_big.Обратите внимание, что из-за глубокой буферизации пакетных данных при их отправке по сети вы не получите эту ошибку сразу,а только при сбросе буферов от клиента к серверу. Это может происходить как в методах add(),так и в методах execute()execute() выполняет финальную сброс буфера. Если вы все еще хотите выполнить пакет с сообщениями,которые заполнили буфер, вы можете это сделать (когда функция execute() вернула ошибку, просто повторите ее).Фактическое количество обработанных сообщений будет возвращено в IBatchCompletionState.Оптимальный размер пакета должен быть найден для каждого конкретного случая, но скорее всего, если он более 1000,то вряд ли вы получите серьезный прирост производительности.

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

unsigned aligned = meta->getAlignedLength(&status);

Позже этот размер пригодится при выделении массива сообщений и работе с ним:

unsigned char* data = new unsigned char[aligned * N]; // N is desired number of messages

for (int n = 0; n < N; ++n)
  fillNextMessage(&data[aligned * n], meta);

batch->add(&status, N, data);

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

BLOBs в целом несовместимы с пакетами — пакет эффективен, когда нужно передать на сервер много мелких данных за один шаг,BLOBs рассматриваются как большие объекты, и поэтому в целом нет смысла использовать их в пакетах.Но на практике часто случается, что BLOB не слишком велики — и в этом случае использование традиционного BLOB API(создание BLOB, передача сегментов на сервер, закрытие BLOB, передача идентификатора BLOB в сообщении) убивает производительность,особенно при использовании через WAN. Поэтому в firebird пакет поддерживает передачу BLOB на сервер вместе с другими сообщениями.Чтобы использовать эту возможность, в первую очередь должна быть установлена политика использования BLOB-объектов длясоздаваемого пакета (как опция в блоке параметров):

pb->insertInt(&status, IBatch::BLOB_IDS, IBatch::BLOB_IDS_ENGINE);

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

FB_MESSAGE(Msg, ThrowStatusWrapper,
  (FB_VARCHAR(5), id)
  (FB_VARCHAR(10), name)
  (FB_BLOB, desc)
) project(&status, master);

В этом случае, чтобы отправить сообщение, содержащее blob, на сервер, можно сделать что-то вроде этого:

project->id = ++idCounter;
project->name.set(currentName);

batch->addBlob(&status, descriptionSize, descriptionText, &project->desc);

batch->add(&status, 1, project.getData());

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

batch->addBlob(&status, descriptionSize, descriptionText, &project->desc, bpbLength, bpb);

После добавления первой части BLOB получите следующую часть данных в descriptionText, с размером descriptionSize после чего:

batch->appendBlobData(&status, descriptionSize, descriptionText);

Это можно сделать в цикле, но будьте осторожны, чтобы не переполнить внутренние пакетные буферы — их размерконтролируется параметром BUFFER_BYTES_SIZE при создании интерфейса IBatch, но не может превышать 256 МБ(по умолчанию 16 МБ). Если вам нужно обработать такой большой BLOB (например, на фоне множества мелких — этим можнообъяснить использование пакетной обработки), просто используйте стандартный API IBlob и метод registerBlob интерфейса IBatch.

Еще один возможный выбор политики BLOB — BLOB_IDS_USER. Использование на первый взгляд не сильно меняется —перед вызовом addBlob() правильный и уникальный для каждого пакета идентификатор должен быть помещен в память,на которую ссылается последний параметр. Конечно, тот же идентификатор должен быть передан в сообщении для BLOB.Принимая во внимание, что генерация BLOB-идентификаторов движком происходит очень быстро, такая политика может показаться бесполезной,но представьте случай, когда вы получаете BLOB и другие данные в относительно независимых потоках (например, в блоках файла),а некоторые хорошие идентификаторы уже присутствуют в них. В таком случае использование предоставленных пользователемидентификаторов BLOB может значительно упростить код.

Note
Обратите внимание

В отличие от BLOB-объектов, созданных с помощью обычного createBlob(), BLOB-объекты, созданные с помощью интерфейса IBatch,по умолчанию являются потоковыми, а не сегментированными. Сегментированные BLOB не представляют ничего интересногопо сравнению с потоковым и поэтому не рекомендуются для использования в новых разработках. Мы поддерживаем этот форматтолько по соображениям обратной совместимости. Если вам действительно нужны сегментированные BLOB, то это значениепо умолчанию можно переопределить, вызвав:

batch->setDefaultBpb(&status, bpbLength, bpb);

Разумеется, переданный BPB может содержать и любые другие параметры создания BLOB. Как вы, возможно, уже заметили,вы также можете передать BPB напрямую в addBlob(), но если большинство BLOB-объектов, которые вы собираетесь добавить,имеют одинаковый формат, отличный от формата по умолчанию, то использование setDefaultBpb() немного более эффективно.Возвращаясь к сегментированным BLOB — вызов addBlob() добавит первый сегмент в BLOB, последующие вызовы appendBlobData()добавят дополнительные сегменты. Не забывайте, что размер сегмента ограничен 64Кб – 1, попытка передать больше данныхза один вызов вызовет ошибку.

Следующий шаг - работа с существующими потоками BLOB для чего используется метод addBlobStream().Используя его, можно добавить более одного BLOB в пакет за один вызов. Поток BLOB — это последовательность BLOB,каждый из которых начинается с заголовка BLOB. Заголовок должен быть правильно выровнен — для этого в интерфейсе IBatchпредусмотрен специальный вызов:

unsigned alignment = batch->getBlobAlignment(&status);

Предполагается, что все компоненты потока BLOB в пакете должны быть выровнены как минимум по границе выравнивания,включая размер порций потока, передаваемых в addBlobStream(), который должен быть кратен этому выравниванию.Заголовок содержит 3 поля: 8-байтовый идентификатор BLOB (должен быть ненулевым), 4-байтовый общий размер BLOB и 4-байтовый размер BPB.Общий размер BLOB-объекта включает в себя BPB внутри, т. е. всегда можно найти следующий BLOB-объект в потоке в байтразмера BLOB-объекта после заголовка (с учетом выравнивания). BPB (если присутствует, т.е. если размер BPB не равен нулю)помещается сразу после заголовка. После передачи данных BLOB-объекта BPB их формат зависит от типа BLOB-объекта —потокового или сегментированного. В случае потокового BLOB это простая последовательность байтов, имеющая размер blob-size – BPB-size.С сегментированным BLOB-объектом все немного сложнее: данные BLOB-объекта представляют собой набор сегментов,где каждый сегмент имеет следующий формат: размер сегмента 2 байта (это должно быть выровнено по границе IBatch::BLOB_SEGHDR_ALIGN),за которым следуют хранящиеся в нем 2 байта количества байт.

Когда в поток добавляется BLOB, его размер не всегда известен заранее. Чтобы не было слишком большого буферадля этого BLOB (помните, что размер должен быть указан в заголовке BLOB перед данными BLOB), можно использовать записьпродолжения BLOB. В заголовке BLOB вы оставляете размер BLOB со значением, известным при создании этого заголовка,и добавляете запись продолжения, которая имеет формат, абсолютно такой же, как и заголовок BLOB, но здесь идентификаторBLOB должен быть равен нулю, а размер BPB всегда также должен быть равен нулю. Обычно вам потребуется иметь одну записьпродолжения для каждого вызова addBlobStream().

Последний метод, используемый для работы с BLOB, стоит особняком от первых трех, которые передают данные BLOB вместес остальными пакетными данными — он необходим для регистрации в идентификаторе пакета BLOB, созданного с использованием стандартного BLOB API.Это может быть неизбежным, если нужно передать в пакет действительно большой BLOB. Не используйте идентификатор такогоBLOB в пакете напрямую — это приведет к ошибке недопустимого идентификатора BLOB во время пакетного выполнения. Вместо этого выполните:

batch->registerBlob(&status, &realId, &msg->desc);

Если политика BLOB заставляет механизм Firebird генерировать идентификаторы BLOB, этого кода достаточно,чтобы правильно зарегистрировать существующий BLOB в пакете. В других случаях вам нужно будет назначить правильный (из пакета POV) идентификатордля msg→desc.

Почти все упомянутые методы используются в 11.batch.cpp — используйте его, чтобы увидеть живой пример пакетной обработки в firebird.

Пару слов о доступе к пакетам из ISC API - можно выполнить подготовленный оператор ISC в пакетном режиме.Для поддержки этого добавлено две новые функций API, а именно fb_get_transaction_interface иfb_get_statement_interface, которые позволяют получить доступ к соответствующим интерфейсам,идентичным существующим дескрипторам ISC. Пример этого присутствует в 12.batch_isc.cpp.

Работа с событиями

Интерфейс событий не был завершен в Firebird 4.0, мы ожидаем, что вследующей версии будет что-то более интересное. Минимальная существующаяподдержка выглядит следующим образом:IAttachment содержит методqueEvents(), который выполняет почти те же функции, что и вызовisc_que_events(). Вместо пары параметров FPTR_EVENT_CALLBACK ast иvoid* arg, необходимых для вызова кода пользователя, когда в Firebirdпроисходит событие, используется интерфейс обратного вызоваIEventCallback. Это традиционный подход, который помогает избежатьнебезопасных бросков из void* в пользовательской функции. Другое важноеразличие заключается в том, что вместо идентификатора события (видаобработчика) эта функция возвращает ссылку на интерфейсIEvents, имеющий метод cancel(),используемый для остановки ожидании события. В отличие отидентификатора, который уничтожается автоматически при поступлениисобытия, интерфейс не может быть уничтожен автоматически, если событиеполучено непосредственно перед вызовом метода cancel(), то это вызоветsegfault из-за того, что интерфейс уже будет уничтожен. Поэтому послеполучения события интерфейс IEventsдолжен быть явно освобождён. Это может быть сделано, например, прямоперед запросом события из очереди в следующий раз:

events->release();
events = NULL;

events = attachment->queEvents(&status, this, eveLen, eveBuffer);

Установка указателя интерфейса в NULL полезна в случае возникновенияисключения в queEvents. В других аспектах обработка событий неизменилась по сравнению с ISC API. Для получения дополнительнойинформации используйте наш пример 08.events.cpp.

Написание плагинов

Чтобы написать плагин, нужно реализовать некоторые интерфейсы ипоместить вашу реализацию в динамическую библиотеку (.dll в Windowsили .so в Linux), которую называют модулем плагина или просто модулем.В большинстве случаев одиночный плагин размещается в динамическойбиблиотеке, но не обязательно. Один из этих интерфейсов —IPluginModule — является модульным(как более или менее ясно из его имени), другие отвечают за плагин.Также каждый модуль плагина должен содержать специальнуюэкспортированную точку входа firebird_plugin(), имя которой указано вфайле include/firebird/Interfaces.h как FB_PLUGIN_ENTRY_POINT.

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

Далее активно используется пример плагина шифрования базы данныхexamples/dbcrypt/DbCrypt.cpp. Будет хорошей идеей собрать этот примерсамостоятельно и изучить его при чтении позже.

Реализация модуля плагина

Плагины активно взаимодействуют со специальным компонентом Firebird,называемым диспетчером плагинов. В частности, менеджер плагинов должензнать, какие модули плагина были загружены и должен быть уведомлен, еслиоперационная система пытается выгрузить один из этих модулей без явнойкоманды диспетчера плагина (это может произойти прежде всего прииспользовании встроенного сервера (embedded) — когда в программевызывается exit() или основная библиотека Firebird fbclientвыгружается). Основная задача интерфейса IPluginModule — этоуведомление. Прежде всего, нужно решить — как определить, что модульбудет выгружен? Когда динамическая библиотека выгружается по какой-либопричине, выполняется множество зависимых от ОС действий, и некоторые изэтих действий могут использоваться для обнаружения этого факта впрограмме. При написании плагинов, распространяемых вместе с firebird,мы всегда используем вызов деструктора глобальной переменной. Большой«плюс» этого метода заключается в том, что он независим от ОС (хотячто-то вроде функции exit(), возможно, также успешно используется). Ноиспользование деструктора позволяет легко сконцентрировать почти все,что связано с обнаружением выгрузки в одном классе, реализуя в то жевремя интерфейс IPluginModule.

Минимальная реализация выглядит следующим образом:

class PluginModule : public IPluginModuleImpl<PluginModule, CheckStatusWrapper>
{

private:
  IPluginManager* pluginManager;

public:
  PluginModule()
    : pluginManager(NULL)
  { }


  ~PluginModule()
  {
    if (pluginManager)
    {
      pluginManager->unregisterModule(this);
      doClean();
    }
  }

  void registerMe(IPluginManager* m)
  {
    pluginManager = m;
    pluginManager->registerModule(this);
  }

  void doClean()
  {
    pluginManager = NULL;
  }

};

Единственным членом данных является интерфейс диспетчера плагиновIPluginManager. Он передаетсяфункции registerModule() и сохраняется в приватной переменной, в то жевремя модуль регистрируется в диспетчере плагинов методом callModule() ссобственным адресом в качестве единственного параметра. ПеременнаяpluginManager не только сохраняет указатель на интерфейс, ноодновременно служит в качестве флага, что модуль зарегистрирован. Когдавызывается деструктор зарегистрированного модуля, он уведомляетдиспетчер плагинов о неожиданной выгрузке с помощью вызоваunregisterModule(), передающим указатель на себя. Когда диспетчерплагинов будет регулярно выгружать модуль, то в первую очередь вызовметода doClean() меняет состояние модуля на незарегистрированное, и этопозволяет избежать вызова unregisterModule(), когда ОС выполняетфактическую выгрузку.

Реализовав интерфейс плагина IPluginModule, мы встретились с первыминтерфейсом, необходимым для реализации плагинов — IPluginManager. Онбудет активно использоваться позже, остальные члены этого класса вряд липотребуются вам после копирования в вашу программу. Просто не забудьтеобъявить глобальную переменную этого типа и вызвать функцию registerMe()из FB_PLUGIN_ENTRY_POINT.

Основной интерфейс любого плагина

Приступим к реализации самого плагина. Тип основного интерфейса зависитот типа плагина, что очевидно, но все они основаны на общем интерфейсеIPluginBase с подсчётом ссылок, который выполняет общие для всехплагинов (и очень простые) задачи. Каждый плагин имеет некоторый (тоже сподсчётом ссылок) объект, которому принадлежит этот плагин. Чтобывыполнять интеллектуальное управление жизненным циклом плагинов, любойплагин должен иметь возможность хранить информацию о владельце исообщать её диспетчеру плагинов по запросу. Это означает, что каждыйплагин должен реализовывать два тривиальных метода setOwner() иgetOwner(), содержащиеся в интерфейсе IPluginBase. Зависимые от типаплагина методы, безусловно, более интересны — они обсуждаются в частиописания интерфейсов.

Давайте рассмотрим типичную часть реализации любого плагина (здесь яспециально использую несуществующий тип SomePlugin):

class MyPlugin : public ISomePluginImpl<MyPlugin, CheckStatusWrapper>
{
public:
  explicit MyPlugin(IPluginConfig* cnf) throw()
     : config(cnf), refCounter(0), owner(NULL)
  {
    config->addRef();
  }
  ...

Конструктор получает в качестве параметра интерфейс конфигурацииплагина. Если вы собираетесь конфигурировать плагин каким-то образом, торекомендуется сохранить этот интерфейс в вашем плагине и использоватьего позже. Это позволит вам использовать общий стиль конфигурацииFirebird, позволяя пользователям иметь привычную конфигурацию и свести кминимуму написание кода. Конечно, при сохранении какого-либо ссылочногоинтерфейса лучше не забывать добавлять ссылку на него. Также не забудьтеустановить счетчик ссылок в 0 и владельца плагина в NULL.

  ~MyPlugin()
  {
    config->release();
  }

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

  // IRefCounted implementation
  int release()
  {
    if (--refCounter == 0)
    {
      delete this;
      return 0;
    }
    return 1;
  }


  void addRef()
  {
    ++refCounter;
  }

Абсолютно типичная реализация объекта с подсчётом ссылок.

  // IPluginBase implementation
  void setOwner(IReferenceCounted* o)
  {
    owner = o;
  }

  IReferenceCounted* getOwner()
  {
    return owner;
  }

Как и было обещано, реализация IPluginBase тривиальна.

  // ISomePlugin implementation
  // … here go various methods required for particular plugin type
private:
  IPluginConfig* config;
  FbSampleAtomic refCounter;
  IReferenceCounted* owner;
};

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

Фабрика плагинов

Еще один интерфейс, необходимый для работы плагина —IPluginFactory. Фабрика создаетэкземпляры плагина и возвращает их в диспетчер плагинов. Фабрика обычновыглядит так:

class Factory : public IPluginFactoryImpl<Factory, CheckStatusWrapper>
{
public:
  IPluginBase* createPlugin(CheckStatusWrapper* status,
                            IPluginConfig* factoryParameter)
  {
    MyPlugin* p = new MyPlugin(factoryParameter);
    p->addRef();
    return p;
  }
};

Здесь внимание следует уделить тому факту, что даже в случае, когда кодв функции может генерировать исключения (оператор new может бросать вслучае, когда память исчерпана), то не обязательно всегда вручнуюопределять блок try/catch — реализация интерфейсов Firebird делает этуработу за вас, в реализации IPluginFactory эта обработка происходит вшаблоне IPluginFactoryImpl. Обратите внимание, что обертки статуса поумолчанию выполняют полноценную обработку только для FbException. Ноесли вы работаете над каким-то крупным проектом, то определите своюсобственную оболочку, в этом случае вы можете обрабатывать любой типисключения C++ и передавать полезную информацию об этом из своегоплагина.

Точка инициализации модуля плагина

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

PluginModule module;

Factory factory;

Если модуль содержит более одного плагина, вам понадобится фабрика длякаждого плагина.

Для FB_PLUGIN_ENTRY_POINT мы не должны забывать, что она должна бытьэкспортирована из модуля плагина, для этого требуется учет некоторыхособенностей ОС. Мы делаем это, используя макрос FB_DLL_EXPORT,определенный в examples/interfaces/ifaceExamples.h. Если вы уверены,что используете плагин только для некоторых конкретных ОС, то вы можетесделать это место немного проще. В минимальном случае функция должнарегистрировать модуль и все фабрики в диспетчере плагинов:

extern "C" void FB_DLL_EXPORT FB_PLUGIN_ENTRY_POINT(IMaster* master)
{
  IPluginManager* pluginManager = master->getPluginManager();
  module.registerMe(pluginManager);
  pluginManager->registerPluginFactory(IPluginManager::TYPE_DB_CRYPT,
                                       "DbCrypt_example",
                                       &factory);
}

Прежде всего, мы вызываем недавно написанную нами функциюPluginModule::registerMe(), которая сохраняет IPluginManager длядальнейшего использования и регистрирует наш модуль плагина. Затемрегистрируем фабрику (или фабрики если в одном модуле будет несколькоплагинов). Мы должны передать правильный тип плагина (допустимые типыперечислены в интерфейсе IPluginManager) и имя, под которым будетзарегистрирован плагин. В простейшем случае он должен совпадать с именемдинамической библиотеки модуля плагина. Это правило поможет вам ненастраивать плагин вручную в plugins.conf.

Обратите внимание — в отличие от приложений плагины не должныиспользовать fb_get_master_interface() для получения IMaster. Вместоэтого следует использовать экземпляр, переданный вFB_PLUGIN_ENTRY_POINT. Если вам нужен мастер-интерфейс в вашем плагине,позаботьтесь об его сохранении в этой функции.

Интерфейсы от A до Z

В этом глоссарии мы не перечисляем интерфейсы, которые не используютсяактивно (например, IRequest, необходимые в первую очередь для поддержкиустаревших запросов API ISC). Та же ссылка может быть получена изнекоторых методов (например, compileRequest() в IAttachment). Дляинтерфейсов/методов, имеющих прямой аналог в старом API, этот аналогбудет указан.

Основные интерфейсы

IAttachment

Интерфейс IAttachment заменяет isc_db_handle.

  1. getInfo

    void getInfo(StatusType* status,
                 unsigned itemsLength,
                 const unsigned char* items,
                 unsigned bufferLength,
                 unsigned char* buffer)

    Заменяет isc_database_info().

  2. startTransaction

    ITransaction* startTransaction(StatusType* status,
                                   unsigned tpbLength,
                                   const unsigned char* tpb)

    Частично заменяет isc_start_multiple(), использует координатор, чтобызапустить более одной распределённой транзакции. Позволяет объединить 2транзакции в одну распределённую.

  3. reconnectTransaction

    ITransaction* reconnectTransaction(StatusType* status,
                                       unsigned length,
                                       const unsigned char* id)

    Позволяет подключиться к транзакции в состоянии limbo. Параметр Idсодержит номер транзакции в сетевом формате заданной длины.

  4. compileRequest

    IRequest* compileRequest(StatusType* status,
                             unsigned blrLength,
                             const unsigned char* blr)

    Поддержка ISC API.

  5. transactRequest

    void transactRequest(StatusType* status,
                         ITransaction* transaction,
                         unsigned blrLength,
                         const unsigned char* blr,
                         unsigned inMsgLength,
                         const unsigned char* inMsg,
                         unsigned outMsgLength,
                         unsigned char* outMsg)

    Поддержка ISC API.

  6. createBlob

    IBlob* createBlob(StatusType* status,
                      ITransaction* transaction,
                      ISC_QUAD* id,
                      unsigned bpbLength,
                      const unsigned char* bpb)

    Создает новый blob, сохраняет его идентификатор в id, заменяетisc_create_blob2().

  7. openBlob

    IBlob* openBlob(StatusType* status,
                    ITransaction* transaction,
                    ISC_QUAD* id,
                    unsigned bpbLength,
                    const unsigned char* bpb)

    Открывает существующий blob, заменяет isc_open_blob2().

  8. getSlice

    int getSlice(StatusType* status,
                 ITransaction* transaction,
                 ISC_QUAD* id,
                 unsigned sdlLength,
                 const unsigned char* sdl,
                 unsigned paramLength,
                 const unsigned char* param,
                 int sliceLength,
                 unsigned char* slice)

    Поддержка ISC API.

  9. putSlice

    void putSlice(StatusType* status,
                  ITransaction* transaction,
                  ISC_QUAD* id,
                  unsigned sdlLength,
                  const unsigned char* sdl,
                  unsigned paramLength,
                  const unsigned char* param,
                  int sliceLength,
                  unsigned char* slice)

    Поддержка ISC API.

  10. executeDyn

    void executeDyn(StatusType* status,
                    ITransaction* transaction,
                    unsigned length,
                    const unsigned char* dyn)

    Поддержка ISC API.

  11. prepare

    IStatement* prepare(StatusType* status,
                        ITransaction* tra,
                        unsigned stmtLength,
                        const char* sqlStmt,
                        unsigned dialect,
                        unsigned flags)

    Заменяет isc_dsql_prepare(). Дополнительный параметр flags позволяютконтролировать, какая информация будет предварительно загружена издвижка сразу (т.е. в одном сетевом пакете для удаленной операции).

  12. execute

    ITransaction* execute(StatusType* status,
                          ITransaction* transaction,
                          unsigned stmtLength,
                          const char* sqlStmt,
                          unsigned dialect,
                          IMessageMetadata* inMetadata,
                          void* inBuffer,
                          IMessageMetadata* outMetadata,
                          void* outBuffer)

    Выполняет любую инструкцию SQL, за исключением возврата нескольких строкданных. Частичный аналог isc_dsql_execute2() — вход и выход XSLQDAзаменены на входные и выходные сообщения с соответствующими буферами.

  13. openCursor

    IResultSet* openCursor(StatusType* status,
                           ITransaction* transaction,
                           unsigned stmtLength,
                           const char* sqlStmt,
                           unsigned dialect,
                           IMessageMetadata* inMetadata,
                           void* inBuffer,
                           IMessageMetadata* outMetadata,
                           const char* cursorName,
                           unsigned cursorFlags)

    Выполняет оператор SQL, потенциально возвращающий несколько строкданных. Возвращает интерфейсIResultSet, который используется дляизвлечения этих данных. Формат выходных данных определяется параметромoutMetadata, при задании NULL используется формат по умолчанию. ПараметрcursorName указывает имя открытого курсора (аналогisc_dsql_set_cursor_name()). Параметр cursorFlags необходим, чтобыоткрыть двунаправленный указатель курсора, для этого необходимо указатьзначение IStatement::CURSOR_TYPE_SCROLLABLE.

  14. queEvents

    IEvents* queEvents(StatusType* status,
                       IEventCallback* callback,
                       unsigned length,
                       const unsigned char* events)

    Заменяет вызов isc_que_events(). Вместо функции обратного вызова сvoid* параметром используется интерфейс обратного вызова.

  15. cancelOperation

    void cancelOperation(StatusType* status, int option)

    Замена fb_cancel_operation().

  16. ping

    void ping(StatusType* status)

    Проверяет состояния соединения. Если тест не удаётся, то единственнаявозможная операция с подключением — закрыть его.

  17. getIdleTimeout

    unsigned getIdleTimeout(StatusType* status)

    Возвращает таймаут простоя соединения в секундах.

  18. setIdleTimeout

    void setIdleTimeout(StatusType* status, unsigned timeOut)

    Устанавливает таймаут простоя соединения в секундах.

  19. getStatementTimeout

    unsigned getStatementTimeout(StatusType* status)

    Возвращает таймаут выполнения запроса в миллисекундах.

  20. setStatementTimeout

    void setStatementTimeout(StatusType* status, unsigned timeOut)

    Устанавливает таймаут выполнения запроса в миллисекундах.

  21. createBatch

    IBatch* createBatch(StatusType* status,
                        ITransaction* transaction,
                        unsigned stmtLength,
                        const char* sqlStmt,
                        unsigned dialect,
                        IMessageMetadata* inMetadata,
                        unsigned parLength,
                        const unsigned char* par)

    Подготавливает sqlStmt и создает интерфейс IBatch, готовый принять несколько наборов входных параметровв формате inMetadata. Если оставить NULL в inMetadata, то пакет будет использовать формат по умолчанию для sqlStmt.В функцию createBatch() можно передать блок параметров, что позволит настроить поведение пакета.

  22. createReplicator

    IReplicator* createBatch(StatusType* status)

    Создаёт экземпляр плагина репликации с интерфейсом IReplicator.

  23. detach

    void detach(StatusType* status)

    Отсоединяет от текущей базы данных. Заменяет isc_detach_database(). В случае успеха освобождает интерфейс.

  24. dropDatabase

    void dropDatabase(StatusType* status)

    Удаляет текущую базу данных. Заменяет isc_drop_database(). В случае успеха освобождает интерфейс.

IDtc

Интерфейс IDtc — координатор распределенных транзакций. Используется длязапуска распределенной (работает с двумя или более подключениями)транзакции. В отличие от пред-FB3-подхода, когда распределеннаятранзакция должна запускаться таким образом с самого начала, координаторраспределенных транзакций Firebird 3 позволяет также присоединить ужезапущенные транзакции в единую распределенную транзакцию.

  1. join

    ITransaction* join(StatusType* status, ITransaction* one, ITransaction* two)

    Объединяет 2 независимых транзакции в распределенную транзакцию. Приуспешном выполнении обе транзакции, переданные в join(),освобождаются, а указатели на них больше не должны использоваться.

  2. startBuilder

    IDtcStart* startBuilder(StatusType* status)

    Возвращает IDtcStart интерфейс.

IDtcStart

Интерфейс IDtcStart — заменяет массив структур TEB (переданisc_start_multiple() в ISC API). Этот интерфейс собирает подключения(и, вероятно, соответствующие TPB), для которых должна быть запущенараспределённая транзакция.

  1. addAttachment

    void addAttachment(StatusType* status, IAttachment* att)

    Добавляет подключение, транзакция для него будет запущена с TPB поумолчанию.

  2. addWithTpb

    void addWithTpb(StatusType* status, IAttachment* att, unsigned length, const unsigned char* tpb)

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

  3. start

    ITransaction* start(StatusType* status)

    Начинает распределенную транзакцию для собранных подключений. При успехевозвращает интерфейс IDtcStart.

IEventCallback

Интерфейс IEventCallback — заменяет функцию обратного вызова,используемую в вызове isc_que_events(). Должен быть реализованпользователем для отслеживания событий с помощью методаIAttachment::queEvents().

  1. eventCallbackFunction

    void eventCallbackFunction(unsigned length, const unsigned char* events)

    Вызывается каждый раз, когда происходит событие.

IEvents

Интерфейс IEvents — заменяет идентификатор события при работе смониторингом событий.

  1. cancel

    void cancel(StatusType* status)

    Отменяет мониторинг событий, начатый в IAttachment::queEvents().

IFirebirdConf

Интерфейс IFirebirdConf — доступ к основной конфигурации Firebird.Используется как для конфигурации по умолчанию, заданной конфигурациейfirebird.conf, так и для каждой базы данных, скорректированной спомощью database.conf. Чтобы ускорить доступ к значениям конфигурации,вызовы, обращающиеся к фактическим значениям, используют целочисленныйключ вместо символьного имени параметра. Ключ стабилен во время работысервера (т. е. плагин может получить его один раз и использовать дляполучения значения параметров конфигурации для разных баз данных).

  1. getKey

    unsigned getKey(const char* name)

    Возвращает ключ для заданного имени параметра. ~0 (все биты равны 1)возвращается в случае, когда такого параметра нет.

  2. asInteger

    ISC_INT64 asInteger(unsigned key)

    Возвращает значение целочисленного параметра.

  3. asString

    const char* asString(unsigned key)

    Возвращает значение строкового параметра

  4. asBoolean

    FB_BOOLEAN asBoolean(unsigned key)

    Возвращает значение логического параметра. Стандартные аббревиатуры(1/true/t/yes/y) рассматриваются как true, все остальные — как false.

  5. getVersion

    unsigned getVersion(StatusType* status)

    Возвращает версию диспетчера конфигурации, связанную с этим интерфейсом.Различные версии диспетчера конфигурации могут сосуществовать на одном сервере, например, когдастарый движок БД используется на современном сервере. Обратите внимание — ключи (см. getKey())разных версий не совпадают и при неправильном использовании всегда будут возвращать 0/nullptr/false.

IInt128

Интерфейс IInt128 помогает работать со 128-битными целыми числами, которые используется в качестве базового типадля числовых и десятичных чисел с точностью более 18.

  1. toString

    void toString(StatusType* status, const FB_I128* from, int scale, unsigned bufferLength, char* buffer)

    Преобразует 128-битное целое значение в строку с учетом масштаба.

  2. fromString

    void fromString(StatusType* status, int scale, const char* from, FB_I128* to)

    Собирает 128-битное целое значение из строки с учетом масштаба.

IMaster

IMaster — основной интерфейс, с которого начинаются все операции сAPI-интерфейсом Firebird.

  1. getStatus

    IStatus* getStatus()

    Возвращает экземпляр интерфейса IStatus.

  2. getDispatcher

    IProvider* getDispatcher()

    Возвращает экземпляр интерфейсаIProvider, реализованный YValve(основной экземпляр поставщика).

  3. getPluginManager

    IPluginManager* getPluginManager()

    Возвращает экземпляр интерфейсаIPluginManager.

  4. getTimerControl

    ITimerControl* getTimerControl()

    Возвращает экземпляр интерфейсаITimerControl.

  5. getDtc

    IDtc* getDtc()

    Возвращает экземпляр интерфейса IDtc.

  6. getUtilInterface

    IUtil* getUtilInterface()

    Возвращает экземпляр интерфейса IUtil.

  7. getConfigManager

    IConfigManager* getConfigManager()

    Возвращает экземпляр интерфейсаIConfigManager.

IMessageMetadata

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

  1. getCount

    unsigned getCount(StatusType* status)

    Возвращает количество полей/параметров в сообщении. Во всех вызовах,содержащих индексный параметр, это значение должно быть: 0 >= index < getCount().

  2. getField

    const char* getField(StatusType* status, unsigned index)

    Возвращает имя поля.

  3. getRelation

    const char* getRelation(StatusType* status, unsigned index)

    Возвращает имя отношения (из которого выбрано данное поле).

  4. getOwner

    const char* getOwner(StatusType* status, unsigned index)

    Возвращает имя владельца отношения.

  5. getAlias

    const char* getAlias(StatusType* status, unsigned index)

    Возвращает псевдоним поля.

  6. getType

    unsigned getType(StatusType* status, unsigned index)

    Возвращает SQL тип поля.

  7. isNullable

    FB_BOOLEAN isNullable(StatusType* status, unsigned index)

    Возвращает true, если поле может принимать значение NULL.

  8. getSubType

    int getSubType(StatusType* status, unsigned index)

    Возвращает подтип поля BLOB (0 - двоичный, 1 - текст и т. д.).

  9. getLength

    unsigned getLength(StatusType* status, unsigned index)

    Возвращает максимальную длину поля.

  10. getScale

    int getScale(StatusType* status, unsigned index)

    Возвращает масштаб для числового поля.

  11. getCharSet

    unsigned getCharSet(StatusType* status, unsigned index)

    Возвращает набор символов для символьных полей и текстового BLOB.

  12. getOffset

    unsigned getOffset(StatusType* status, unsigned index)

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

  13. getNullOffset

    unsigned getNullOffset(StatusType* status, unsigned index)

    Возвращает смещение NULL индикатора для поля в буфере сообщений.

  14. getBuilder

    IMetadataBuilder* getBuilder(StatusType* status)

    Возвращает интерфейсIMetadataBuilder,инициализированный метаданными этого сообщения.

  15. getMessageLength

    unsigned getMessageLength(StatusType* status)

    Возвращает длину буфера сообщения (используйте его для выделения памятипод буфер).

  16. getAlignment

    unsigned getAlignment(StatusType* status)

    Возвращает выравнивание в байтах.

  17. getAlignedLength

    unsigned getAlignedLength(StatusType* status)

    Возвращает размер структуры метаданных после выравнивания.

IMetadataBuilder

Интерфейс IMetadataBuilder — позволяет описать типы данных длясуществующих сообщений или создавать метаданные с самого начала.

  1. setType

    void setType(StatusType* status, unsigned index, unsigned type)

    Устанавливает SQL тип поля.

  2. setSubType

    void setSubType(StatusType* status, unsigned index, int subType)

    Устанавливает подтип BLOB поля.

  3. setLength

    void setLength(StatusType* status, unsigned index, unsigned length)

    Устанавливает максимальную длину символьного поля.

  4. setCharSet

    void setCharSet(StatusType* status, unsigned index, unsigned charSet)

    Устанавливает набор символов для символьного поля и текстового BLOB.

  5. setScale

    void setScale(StatusType* status, unsigned index, unsigned scale)

    Устанавливает масштаб для числовых полей.

  6. truncate

    void truncate(StatusType* status, unsigned count)

    Обрезает сообщение чтобы оно содержало не более count полей.

  7. moveNameToIndex

    void moveNameToIndex(StatusType* status, const char* name, unsigned index)

    Реорганизует поля в сообщении — перемещает поле с именем name в заданноеположение.

  8. remove

    void remove(StatusType* status, unsigned index)

    Удаляет поле.

  9. addField

    unsigned addField(StatusType* status)

    Добавляет поле.

  10. getMetadata

    IMessageMetadata* getMetadata(StatusType* status)

    Возвращает интерфейсIMessageMetadata, построенныйэтим построителем.

  11. setField

    void setField(StatusType* status, unsigned index, const char* field)

    Устанавливает имя поля/столбца.

  12. setRelation

    void setRelation(StatusType* status, unsigned index, const char* relation)

    Устанавливает имя отношения для поля.

  13. setOwner

    void setOwner(StatusType* status, unsigned index, const char* owner)

    Устанавливает имя владельца.

  14. setAlias

    void setAlias(StatusType* status, unsigned index, const char* alias)

    Устанавливает псевдоним поля.

IOffsetsCallback

Интерфейс IOffsetsCallback

  1. setOffset

    void setOffset(StatusType* status, unsigned index, unsigned offset, unsigned nullOffset)

    Уведомляет, что должны быть установлены смещения для поля/параметра синдексом index. Должен быть реализован пользователем при реализацииинтерфейса MessageMetadata и сиспользованием IUtil::setOffsets().

IBatch

Интерфейс IBatch — позволяет обрабатывать несколько наборов параметров при выполнении одного оператора.

  1. add

    void add(StatusType* status, unsigned count, const void* inBuffer)

    Добавляет количество сообщений из inBuffer в пакет. Общий размер сообщений, которые можно добавить в пакет,ограничен параметром TAG_BUFFER_BYTES_SIZE при создании пакета.

  2. addBlob

    void addBlob(StatusType* status,
                 unsigned length, const void* inBuffer,
                 ISC_QUAD* blobId,
                 unsigned bpbLength, const unsigned char* bpb)

    Добавляет в пакет один BLOB длиной length байт из inBuffer, идентификатор BLOB находится по адресу blobId.Если большой двоичный объект должен быть создан с параметрами, отличными от параметров по умолчанию, то может бытьпередан BPB (формат соответствует формату, используемому в Attachment::createBlob).Общий размер встроенных больших двоичных объектов, которые можно добавить в пакет (включая необязательные BPB,заголовки BLOB, размер сегментов с учётом выравнивания) ограничен параметром TAG_BUFFER_BYTES_SIZE при создании пакета(влияет на все методы, ориентированные на BLOB, кроме registerBlob()).

  3. appendBlobData

    void appendBlobData(StatusType* status, unsigned length, const void* inBuffer)

    Расширяет последний добавленный BLOB: добавляет к нему байты длинной length, взятые по адресу inBuffer.

  4. addBlobStream

    addBlobStream(StatusType* status, unsigned length, const void* inBuffer)

    Добавляет данные BLOB (это может быть несколько объектов или часть одного BLOB) в пакет. Заголовок каждого BLOB в потокевыравнивается по границе getBlobAlignment() и содержит 3 поля: первое - 8-байтовый идентификатор BLOB (в формате ISC_QUAD),второе - 4-байтная длина BLOB, третье - 4-байтная длина BPB. Заголовок большого двоичного объекта не должен пересекатьграницы буфера в этом вызове функции. Данные BPB помещаются сразу после заголовка, затем идут данные BLOB-объектов.Длина большого двоичного объекта включает BPB (если он присутствует). Все данные могут быть распределены междунесколькими вызовами addBlobStream(). Данные BLOB-объекта, в свою очередь, могут быть структурированы в случаесегментированного BLOB-объекта, см. главу "Пакетное изменение данных".

  5. registerBlob

    void registerBlob(StatusType* status, const ISC_QUAD* existingBlob, ISC_QUAD* blobId)

    Позволяет использовать в пакете BLOB, добавленные с помощью стандартного интерфейса IBlob. Эта функция содержит 2параметра ISC_QUAD*, важно их не смешивать – второй параметр (existingBlob) является указателем на идентификатор BLOB,уже добавленный вне области действия пакета, третий (blobId) указывает на идентификатор BLOB, который будет помещен в сообщение в этом пакете'.

  6. execute

    IBatchCompletionState* execute(StatusType* status, ITransaction* transaction)

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

  7. cancel

    void cancel(StatusType* status)

    Очищает буферы сообщений и BLOB, возвращая пакет в состояние, в котором он находился сразу после создания.Обратите внимание: интерфейс IBatch с подсчетом ссылок не содержит какой-либо специальной функции для его закрытия,для этого используйте release().

  8. getBlobAlignment

    unsigned getBlobAlignment(StatusType* status)

    Возвращает необходимое выравнивание для данных, помещаемых в буфер функцией addBlobStream().

  9. getMetadata

    IMessageMetadata* getMetadata(StatusType* status)

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

  10. setDefaultBpb

    void setDefaultBpb(StatusType* status, unsigned parLength, const unsigned char* par)

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

  11. getInfo

    void getInfo(StatusType* status,
                 unsigned itemsLength, const unsigned char* items,
                 unsigned bufferLength, unsigned char* buffer)

    Запрашивает информацию о пакете.

Тег для блока параметров:

  • VERSION1

Теги для скоплений в блоке параметров:

  • TAG_MULTIERROR (0/1) – может иметь более одного сообщения с ошибками;

  • TAG_RECORD_COUNTS (0/1) - учет измененных записей по сообщениям;

  • TAG_BUFFER_BYTES_SIZE (integer) - максимально возможный размер буфера на сервере (по умолчанию 16Мб, максимум 256Мб);

  • TAG_BLOB_POLICY - политика, используемая для хранения BLOB-объектов;

  • TAG_DETAILED_ERRORS (integer) - сколько векторов с подробной информацией об ошибках хранится в состоянии завершения (по умолчанию 64, максимум 256).

Политики, используемые для хранения BLOB:

  • BLOB_NONE – нельзя использовать встроенные BLOB-объекты (registerBlob() работает в любом случае);

  • BLOB_ID_ENGINE - BLOB добавляются один за другим, BLOB-идентификаторы генерируются движком firebird;

  • BLOB_ID_USER - BLOB добавляются один за другим, BLOB-идентификаторы генерируются пользователем;

  • BLOB_STREAM - BLOB-объекты добавляются в поток.

Элементы, допустимые в вызове getInfo():

  • INF_BUFFER_BYTES_SIZE – фактический максимально возможный размер буфера (устанавливается через TAG_BUFFER_BYTES_SIZE);

  • INF_DATA_BYTES_SIZE - размер уже добавленных сообщений;

  • INF_BLOBS_BYTES_SIZE - размер уже добавленных BLOB;

  • INF_BLOB_ALIGNMENT - требуемое выравнивание для данных BLOB (дублирует getBlobAlignment).

IPluginConfig

Интерфейс IPluginConfig — передается фабрике плагинов при созданииэкземпляра плагина (с конкретной конфигурацией).

  1. getConfigFileName

    const char* getConfigFileName()

    Возвращает рекомендованное имя файла, в котором ожидается сохранениеконфигурации для плагина.

  2. getDefaultConfig

    IConfig* getDefaultConfig(StatusType* status)

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

  3. getFirebirdConf

    IFirebirdConf* getFirebirdConf(StatusType* status)

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

  4. setReleaseDelay

    void setReleaseDelay(StatusType* status, ISC_UINT64 microSeconds)

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

IPluginFactory

Интерфейс IPluginFactory — должен быть реализован автором плагина принаписании плагина.

  1. createPlugin

    IPluginBase* createPlugin(StatusType* status, IPluginConfig* factoryParameter)

    Создает новый экземпляр плагина с переданной рекомендуемойконфигурацией.

IPluginManager

Интерфейс IPluginManager — API диспетчера плагинов.

  1. registerPluginFactory

    void registerPluginFactory(unsigned pluginType,
                               const char* defaultName,
                               IPluginFactory* factory)

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

  2. registerModule

    void registerModule(IPluginModule* cleanup)

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

  3. unregisterModule

    void unregisterModule(IPluginModule* cleanup)

    Разрегистрирует модуль плагина.

  4. getPlugins

    IPluginSet* getPlugins(StatusType* status,
                           unsigned pluginType,
                           const char* namesList,
                           IFirebirdConf* firebirdConf)

    Возвращает интерфейс IPluginSet, предоставляющий доступ к спискуплагинов данного типа. Имена включенных плагинов берутся из namesList,если отсутствует (NULL), то из настроек конфигурации для данного типаpluginType. Если указан параметр firebirdConf, то он используется длявсех целей конфигурации (включая получение списка плагинов и переход кметоду PluginFactory::createPlugin()), если отсутствует (NULL), тоиспользуется настройка по умолчанию (из firebird.conf).

  5. getConfig

    IConfig* getConfig(StatusType* status, const char* filename)

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

  6. releasePlugin

    void releasePlugin(IPluginBase* plugin)

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

Константы, определенные интерфейсом IPluginManager (типы плагинов):

  • TYPE_PROVIDER

  • TYPE_AUTH_SERVER

  • TYPE_AUTH_CLIENT

  • TYPE_AUTH_USER_MANAGEMENT

  • TYPE_EXTERNAL_ENGINE

  • TYPE_TRACE

  • TYPE_WIRE_CRYPT

  • TYPE_DB_CRYPT

  • TYPE_KEY_HOLDER

  • TYPE_REPLICATOR

IPluginModule

Интерфейс IPluginModule — представляет модуль плагина (динамическаябиблиотека). Должен быть реализован автором плагина в каждом модулеплагина (по одному экземпляру на модуль).

  1. doClean

    void doClean()

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

IPluginSet

Интерфейс IPluginSet — представляет собой набор плагинов данного типа.Обычно используется внутренним кодом Firebird, но рекомендуется дляиспользования в плагинах, загружающих другие плагины.

  1. getName

    const char* getName()

    возвращает имя текущего плагина в наборе.

  2. getModuleName

    const char* getModuleName()

    возвращает имя модуля текущего плагина в наборе (в простом случаесовпадает с именем плагина).

  3. getPlugin

    IPluginBase* getPlugin(StatusType* status)

    возвращает экземпляр текущего плагина, возвращенный интерфейс долженбыть преобразован в основной интерфейс плагина запрошенного типа вметоде IPluginManager::getPlugins(). Возвращает NULL, если в наборебольше нет плагинов. Счётчик ссылок плагина, возвращаемого этойфункцией, увеличивается при возврате — не забудьте использовать методreleasePlugin() интерфейсаIPluginManager для освобожденияплагинов, возвращаемых этим методом.

  4. next

    void next(StatusType* status)

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

  5. set

    void set(StatusType* status, const char* list)

    сбрасывает интерфейс: заставляет его работать со списком плагинов,предоставляемых параметром списка. Тип плагинов остается неизменным.

IProvider

Интерфейс IPluginModule — основной интерфейс для начала доступа к базеданных/сервису.

  1. attachDatabase

    IAttachment* attachDatabase(StatusType* status,
                                const char* fileName,
                                unsigned dpbLength,
                                const unsigned char* dpb)

    Создаёт соединение с существующей базой данных. Заменяет isc_attach_database().

  2. createDatabase

    IAttachment* createDatabase(StatusType* status,
                                const char* fileName,
                                unsigned dpbLength,
                                const unsigned char* dpb)

    Создаёт новую базу данных и возращает интерфейс соединения с ней. Заменяет isc_create_database().

  3. attachServiceManager

    IService* attachServiceManager(StatusType* status,
                                   const char* service,
                                   unsigned spbLength,
                                   const unsigned char* spb)

    Заменяет isc_service_attach().

  4. shutdown

    void shutdown(StatusType* status, unsigned timeout, const int reason)

    Заменяет fb_shutdown().

  5. setDbCryptCallback

    void setDbCryptCallback(IStatus* status, ICryptKeyCallback* cryptCallback)

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

IResultSet

Интерфейс IResultSet — заменяет (с расширенной функциональностью)некоторые функции isc_stmt_handle. Этот интерфейс возвращается вызовомopenCursor() из IAttachment илиIStatement. Все вызовы fetch…​,кроме fetchNext(), работают только для двунаправленного (открытого сфлагом CURSOR_TYPE_SCROLLABLE) курсора.

  1. fetchNext

    int fetchNext(StatusType* status, void* message)

    выбирает следующую запись, заменяет isc_dsql_fetch(). Этот метод (идругие методы выборки) возвращает код завершенияStatus::RESULT_NO_DATA при достижении EOF, и статусStatus::RESULT_OK при успешном завершении.

  2. fetchPrior

    int fetchPrior(StatusType* status, void* message)

    выбирает предыдущую запись.

  3. fetchFirst

    int fetchFirst(StatusType* status, void* message)

    выбирает первую запись.

  4. fetchLast

    int fetchLast(StatusType* status, void* message)

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

  5. fetchAbsolute

    int fetchAbsolute(StatusType* status, int position, void* message)

    получает запись по абсолютной позиции в наборе результатов.

  6. fetchRelative

    int fetchRelative(StatusType* status, int offset, void* message)

    извлекает запись по положению относительно текущей.

  7. isEof

    FB_BOOLEAN isEof(StatusType* status)

    проверка EOF.

  8. isBof

    FB_BOOLEAN isBof(StatusType* status)

    проверка BOF.

  9. getMetadata

    IMessageMetadata* getMetadata(StatusType* status)

    возвращает метаданные для сообщений в наборе результатов, особеннополезно, когда набор результатов открывается вызовомIAttachment::openCursor() с параметром формата вывода метаданныхравным NULL (это единственный способ получить формат сообщения в данномслучае).

  10. close

    void close(IStatus* status)

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

IService

Интерфейс IService — заменяет isc_svc_handle.

  1. detach

    void detach(StatusType* status)

    Закрывает подключение к менеджеру сервисов, при успехе освобождаетинтерфейс. Заменяет isc_service_detach().

  2. query

    void query(StatusType* status,
               unsigned sendLength,
               const unsigned char* sendItems,
               unsigned receiveLength,
               const unsigned char* receiveItems,
               unsigned bufferLength,
               unsigned char* buffer)

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

  3. start

    void start(StatusType* status,
               unsigned spbLength,
               const unsigned char* spb)

    Запускает утилиту в диспетчере служб. Заменяет isc_service_start().

IStatement

Интерфейс IStatement — заменяет (частично) isc_stmt_handle.

  1. getInfo

    void getInfo(StatusType* status,
                 unsigned itemsLength,
                 const unsigned char* items,
                 unsigned bufferLength,
                 unsigned char* buffer)

    Заменяет isc_dsql_sql_info().

  2. getType

    unsigned getType(StatusType* status)

    Тип оператора, в настоящее время можно найти только в источникахfirebird в dsql/dsql.h.

  3. getPlan

    const char* getPlan(StatusType* status, FB_BOOLEAN detailed)

    Возвращает план выполнения оператора.

  4. getAffectedRecords

    ISC_UINT64 getAffectedRecords(StatusType* status)

    Возвращает количество записей, которые затронуты оператором.

  5. getInputMetadata

    IMessageMetadata* getInputMetadata(StatusType* status)

    Возвращает метаданные параметров.

  6. getOutputMetadata

    IMessageMetadata* getOutputMetadata(StatusType* status)

    Возвращает метаданные значений выходных данных.

  7. execute

    ITransaction* execute(StatusType* status,
                          ITransaction* transaction,
                          IMessageMetadata* inMetadata,
                          void* inBuffer,
                          IMessageMetadata* outMetadata,
                          void* outBuffer)

    Выполняет любую инструкцию SQL, за исключением тех, что возвращаютнескольких строк данных. Частичный аналог isc_dsql_execute2() — вход ивыход XSLQDA заменены на входные и выходные сообщения с соответствующимибуферами.

  8. openCursor

    IResultSet* openCursor(StatusType* status,
                           ITransaction* transaction,
                           IMessageMetadata* inMetadata,
                           void* inBuffer,
                           IMessageMetadata* outMetadata,
                           unsigned flags)

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

  9. setCursorName

    void setCursorName(StatusType* status, const char* name)

    Заменяет isc_dsql_set_cursor_name().

  10. free

    void free(StatusType* status)

    Уничтожает оператор, освобождает интерфейс в случае успеха.

  11. getFlags

    unsigned getFlags(StatusType* status)

    Возвращает флаги, описывающие, как должен выполняться этот оператор,упрощенная замена метода getType().

  12. getTimeout

    unsigned getTimeout(StatusType* status)

    Возвращает таймаут выполнения SQL запроса в миллисекундах.

  13. setTimeout

    unsigned setTimeout(StatusType* status)

    Устанавливает таймаут выполнения SQL запроса в миллисекундах.

  14. createBatch

    IBatch* createBatch(StatusType* status,
                        IMessageMetadata* inMetadata,
                        unsigned parLength,
                        const unsigned char* par)

    Создает интерфейс IBatch для оператора SQL с входными параметрами, что позволяет выполнять этот операторс несколькими наборами параметров. Формат входных данных определяется параметром inMetadata, оставив его NULL,пакет использует формат по умолчанию из этого интерфейса. В функцию createBatch() можно передать блок параметров,что позволит настроить поведение пакета.

Константы, определенные интерфейсом IStatement

Флаги IAttachment::prepare():

  • PREPARE_PREFETCH_NONE — константа, чтобы пропускать флаги, значение0.

Следующие флаги могут быть объединены с помощью OR для полученияжелаемого эффекта:

  1. PREPARE_PREFETCH_TYPE

  2. PREPARE_PREFETCH_INPUT_PARAMETERS

  3. PREPARE_PREFETCH_OUTPUT_PARAMETERS

  4. PREPARE_PREFETCH_LEGACY_PLAN

  5. PREPARE_PREFETCH_DETAILED_PLAN

  6. PREPARE_PREFETCH_AFFECTED_RECORDS

  7. PREPARE_PREFETCH_FLAGS (флаги возвращаемые методом getFlags())

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

  1. PREPARE_PREFETCH_METADATA

  2. PREPARE_PREFETCH_ALL

Значения возвращаемые методом getFlags():

  1. FLAG_HAS_CURSOR — используйте openCursor() для выполнения этогооператора, а не execute()

  2. FLAG_REPEAT_EXECUTE — когда подготовленный оператор можетвыполняться много раз с разными параметрами.

Флаги передаваемые в openCursor():

  1. CURSOR_TYPE_SCROLLABLE — открывается двунаправленный курсор.

IStatus

Интерфейс IStatus — заменяет ISC_STATUS_ARRAY. Функциональностьрасширена — статус имеет отдельный доступ к векторам ошибок ипредупреждений, может содержать векторы неограниченной длины,самостоятельно хранит строки, используемые в векторах, не имеетнеобходимости в кольцевом буфере строк. В C++ IStatus всегдаиспользуется в оболочке состояния, C++ API предоставляет две разныеоболочки, имеющие различное поведение, когда ошибка возвращается извызова API. Интерфейс сведен к минимуму (методы, такие какпреобразование его в текст, перемещены в интерфейсIUtil), чтобы упростить его реализациюпользователями при необходимости.

  1. init

    void init()

    очищает интерфейс, устанавливая его в исходное состояние.

  2. getState

    unsigned getState()

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

  3. setErrors2

    void setErrors2(unsigned length, const intptr_t* value)

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

  4. setWarnings2

    void setWarnings2(unsigned length, const intptr_t* value)

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

  5. setErrors

    void setErrors(const intptr_t* value)

    устанавливает содержимое вектора ошибок, длина определяется контекстомзначения.

  6. setWarnings

    void setWarnings(const intptr_t* value)

    устанавливает содержимое вектора предупреждений, длина определяетсяконтекстом значения.

  7. getErrors

    const intptr_t* getErrors()

    возвращает вектор ошибок.

  8. getWarnings

    const intptr_t* getWarnings()

    возвращает вектор предупреждений.

  9. clone

    IStatus* clone()

    создаёт клон текущего интерфейса.

Константы определённые в IStatus

Флаги, возвращаемые методом getState():

  • STATE_WARNINGS

  • STATE_ERRORS

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

  • RESULT_ERROR

  • RESULT_OK

  • RESULT_NO_DATA

  • RESULT_SEGMENT

IBatchCompletionState

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

  1. getSize

    unsigned getSize(StatusType* status)

    Возвращает общее количество обработанных сообщений.

  2. getState

    int getState(StatusType* status, unsigned pos)

    Возвращает результат выполнения сообщения с номером pos. При любой ошибке с сообщением это константа EXECUTE_FAILED,возвращаемое значение в случае успеха зависит от наличия параметра RECORD_COUNTS при создании пакета.Когда она присутствует и имеет ненулевое значение, возвращается количество записей, вставленных, обновленных или удаленныхво время обработки конкретного сообщения, иначе возвращается константа SUCCESS_NO_INFO.

  3. findError

    unsigned findError(StatusType* status, unsigned pos)

    Находит следующее (начинающееся с pos) сообщение, обработка которого вызвала ошибку.При отсутствии такого сообщения возвращается константа NO_MORE_ERRORS. Количество векторов состояния,возвращаемых в этом интерфейсе, ограничено значением параметра DETAILED_ERRORS при создании пакета.

  4. getStatus

    void getStatus(StatusType* status, IStatus* to, unsigned pos)

    Возвращает подробную информацию (полный вектор состояния) об ошибке, которая произошла при обработке сообщения pos.Чтобы различать ошибки (в IBatch::execute() или в IBatchCompletionState::getStatus()), этот статус возвращаетсяв отдельном параметре to, в отличие от ошибок в этом вызове, которые помещаются в параметр status.

Специальные значения, возвращаемые getState():

  • EXECUTE_FAILED — при обработке этого сообщения произошла ошибка;

  • SUCCESS_NO_INFO — информация об обновлении записи не была собрана.

Специальное значение, возвращаемое findError():

  • NO_MORE_ERRORS – больше нет сообщений с ошибками в этом пакете.

ITimer

Интерфейс ITimer — пользовательский таймер. Интерфейс обратного вызова,который должен быть реализован пользователем для использования таймераFirebird.

  1. handler

    void handler()

    метод вызывается, когда таймер звонит (или когда сервер выключается).

ITimerControl

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

  1. start

    void start(StatusType* status, ITimer* timer, ISC_UINT64 microSeconds)

    запуск ITimer после сигнала (вмикросекундах, 10-6 секунд). Таймер будет разбужен только один разпосле этого вызова.

  2. stop

    void stop(StatusType* status, ITimer* timer)

    остановка ITimer. Не стоит останавливатьне запущенный таймер, что позволит избежать проблем с гонками междуstop() и сигналом таймером.

ITransaction

Интерфейс ITransaction — заменяет isc_tr_handle.

  1. getInfo

    void getInfo(StatusType* status,
                 unsigned itemsLength,
                 const unsigned char* items,
                 unsigned bufferLength,
                 unsigned char* buffer)

    заменяет isc_transaction_info().

  2. prepare

    void prepare(StatusType* status,
                 unsigned msgLength,
                 const unsigned char* message)

    заменяет isc_prepare_transaction2().

  3. commit

    void commit(StatusType* status)

    заменяет isc_commit_transaction().

  4. commitRetaining

    void commitRetaining(StatusType* status)

    заменяет isc_commit_retaining().

  5. rollback

    void rollback(StatusType* status)

    заменяет isc_rollback_transaction().

  6. rollbackRetaining

    void rollbackRetaining(StatusType* status)

    заменяет isc_rollback_retaining().

  7. disconnect

    void disconnect(StatusType* status)

    заменяет fb_disconnect_transaction().

  8. join

    ITransaction* join(StatusType* status, ITransaction* transaction)

    соединяет текущую транзакцию и транзакцию, переданную как параметр вединую распределённую транзакцию (с использованием Dtc). При успешномвыполнении текущая транзакция и транзакция переданная в качествепараметра освобождаются и больше не должны использоваться.

  9. validate

    ITransaction* validate(StatusType* status, IAttachment* attachment)

    этот метод используется для поддержки координатора распределенныхтранзакций.

  10. enterDtc

    ITransaction* enterDtc(StatusType* status)

    этот метод используется для поддержки координатора распределенныхтранзакций.

IVersionCallback

Интерфейс IVersionCallback — обратный вызов для IUtil::getFbVersion().

  1. callback

    void callback(StatusType* status, const char* text)

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

IUtil

Интерфейс IUtil — различные вспомогательные методы, требуемые здесь илитам.

  1. getFbVersion

    void getFbVersion(StatusType* status,
                      IAttachment* att,
                      IVersionCallback* callback)

    Строит длинный и красивый отчет о версии для firebird. Это можноувидеть в ISQL при вызове с ключом -Z.

  2. loadBlob

    void loadBlob(StatusType* status,
                  ISC_QUAD* blobId,
                  IAttachment* att,
                  ITransaction* tra,
                  const char* file,
                  FB_BOOLEAN txt)

    Загрузка BLOB из файла.

  3. dumpBlob

    void dumpBlob(StatusType* status,
                  ISC_QUAD* blobId,
                  IAttachment* att,
                  ITransaction* tra,
                  const char* file,
                  FB_BOOLEAN txt)

    Сохраняет BLOB в файл.

  4. getPerfCounters

    void getPerfCounters(StatusType* status,
                         IAttachment* att,
                         const char* countersSet,
                         ISC_INT64* counters)

    Получает статистику для данного подключения.

  5. executeCreateDatabase

    IAttachment* executeCreateDatabase(StatusType* status,
                                       unsigned stmtLength,
                                       const char* creatDBstatement,
                                       unsigned dialect,
                                       FB_BOOLEAN* stmtIsCreateDb)

    Выполняет инструкцию CREATE DATABASE …​ — трюк ISC с NULLдескриптором оператора не работает с интерфейсами.

  6. decodeDate

    void decodeDate(ISC_DATE date,
                    unsigned* year,
                    unsigned* month,
                    unsigned* day)

    Заменяет isc_decode_sql_date().

  7. decodeTime

    void decodeTime(ISC_TIME time,
                    unsigned* hours,
                    unsigned* minutes,
                    unsigned* seconds,
                    unsigned* fractions)

    Заменяет isc_decode_sql_time().

  8. encodeDate

    ISC_DATE encodeDate(unsigned year, unsigned month, unsigned day)

    Заменяет isc_encode_sql_date().

  9. encodeTime

    ISC_TIME encodeTime(unsigned hours,
                        unsigned minutes,
                        unsigned seconds,
                        unsigned fractions)

    Заменяет isc_encode_sql_time().

  10. formatStatus

    unsigned formatStatus(char* buffer, unsigned bufferSize, IStatus* status)

    Заменяет fb_interpret(). Размер буфера, переданного в этот метод, недолжен быть меньше 50 байт.

  11. getClientVersion

    unsigned getClientVersion()

    Возвращает целое число, содержащее основную версию в байте 0 и младшуюверсию в байте 1.

  12. getXpbBuilder

    IXpbBuilder* getXpbBuilder(StatusType* status,
                               unsigned kind,
                               const unsigned char* buf,
                               unsigned len)

    Возвращает интерфейс IXpbBuilder.Допустимые kind перечислены в IXpbBuilder.

  13. setOffsets

    unsigned setOffsets(StatusType* status,
                        IMessageMetadata* metadata,
                        IOffsetsCallback* callback)

    Устанавливает допустимые смещения вIMessageMetadata. Выполняетвызовы для обратного вызова вIOffsetsCallback для каждогополя/параметра.

  14. getDecFloat16

    IDecFloat16* getDecFloat16(StatusType* status)

    Возвращает интерфейс IDecFloat16.

  15. getDecFloat34

    IDecFloat34* getDecFloat34(StatusType* status)

    Возвращает интерфейс IDecFloat34.

  16. decodeTimeTz

    void decodeTimeTz(StatusType* status,
                      const ISC_TIME_TZ* timeTz,
                      unsigned* hours, unsigned* minutes, unsigned* seconds, unsigned* fractions,
                      unsigned timeZoneBufferLength, char* timeZoneBuffer)

    Декодирует время с часовым поясом.

  17. decodeTimeStampTz

    void decodeTimeStampTz(StatusType* status,
                           const ISC_TIMESTAMP_TZ* timeStampTz,
                           unsigned* year, unsigned* month, unsigned* day,
                           unsigned* hours, unsigned* minutes, unsigned* seconds, unsigned* fractions,
                           unsigned timeZoneBufferLength, char* timeZoneBuffer)

    Декодирует временную метку (дату-время) с часовым поясом.

  18. encodeTimeTz

    void encodeTimeTz(StatusType* status,
                      ISC_TIME_TZ* timeTz,
                      unsigned hours, unsigned minutes, unsigned seconds, unsigned fractions,
                      const char* timeZone)

    Кодирует время с часовым поясом.

  19. encodeTimeStampTz

    void encodeTimeStampTz(StatusType* status,
                           ISC_TIMESTAMP_TZ* timeStampTz,
                           unsigned year, unsigned month, unsigned day,
                           unsigned hours, unsigned minutes, unsigned seconds, unsigned fractions,
                           const char* timeZone)

    Кодирует временную метку (дату-время) с часовым поясом.

  20. getInt128

    IInt128* getInt128(StatusType* status)

    Возвращает интерфейс IInt128.

  21. decodeTimeTzEx

    void decodeTimeTzEx(StatusType* status,
                        const ISC_TIME_TZ_EX* timeTz,
                        unsigned* hours, unsigned* minutes, unsigned* seconds, unsigned* fractions,
                        unsigned timeZoneBufferLength, char* timeZoneBuffer)

    Декодирует время в расширенном формате с часовым поясом.

  22. decodeTimeStampTzEx

    void decodeTimeStampTzEx(StatusType* status,
                             const ISC_TIMESTAMP_TZ_EX* timeStampTz,
                             unsigned* year, unsigned* month, unsigned* day, unsigned* hours,
                             unsigned* minutes, unsigned* seconds, unsigned* fractions,
                             unsigned timeZoneBufferLength, char* timeZoneBuffer)

    Декодирует временную метку (дату-время) в расширенном формате с часовым поясом.

IXpbBuilder

Интерфейс IXpbBuilder

  1. clear

    void clear(StatusType* status)

    Сбрасывает построитель в пустое состояние.

  2. removeCurrent

    void removeCurrent(StatusType* status)

    Удаляет текущий clumplet.

  3. insertInt

    void insertInt(StatusType* status, unsigned char tag, int value)

    Вставляет clumplet со значением, представляющим целое число в сетевомформате.

  4. insertBigInt

    void insertBigInt(StatusType* status, unsigned char tag, ISC_INT64 value)

    Вставляет clumplet со значением, представляющим 64-битное целое число всетевом формате.

  5. insertBytes

    void insertBytes(StatusType* status, unsigned char tag, const void* bytes, unsigned length)

    Вставляет clumplet со значением, содержащим переданные байты.

  6. insertTag

    void insertTag(StatusType* status, unsigned char tag)

    Вставляет clumplet без значения.

  7. isEof

    FB_BOOLEAN isEof(StatusType* status)

    Проверяет, нет ли текущего clumplet.

  8. moveNext

    void moveNext(StatusType* status)

    Переходит к следующему clumplet.

  9. rewind

    void rewind(StatusType* status)

    Переходит к первому clumplet.

  10. findFirst

    FB_BOOLEAN findFirst(StatusType* status, unsigned char tag)

    Находит первый clumplet с данным тегом.

  11. findNext

    FB_BOOLEAN findNext(StatusType* status)

    Находит следующий clumplet с заданным тегом.

  12. getTag

    unsigned char getTag(StatusType* status)

    Возвращает тег для текущего clumplet.

  13. getLength

    unsigned getLength(StatusType* status)

    Возвращает длину текущего значения clumplet.

  14. getInt

    int getInt(StatusType* status)

    Возвращает значение текущего clumplet как целое.

  15. getBigInt

    SC_INT64 getBigInt(StatusType* status)

    Возвращает значение текущего clumplet как 64-битное целое число.

  16. getString

    const char* getString(StatusType* status)

    Возвращает значение текущего clumplet как указатель на нуль-терминальнуюстроку (указатель действителен до следующего вызова этого метода).

  17. getBytes

    const unsigned char* getBytes(StatusType* status)

    Возвращает значение текущего clumplet как указатель на unsigned char.

  18. getBufferLength

    unsigned getBufferLength(StatusType* status)

    Возвращает длину блока параметров.

  19. getBuffer

    const unsigned char* getBuffer(StatusType* status)

    Возвращает указатель на блок параметров.

Константы, определенные интерфейсом IXpbBuilder

Допустимые типы построителей:

  • BATCH (IBatch parameters block)

  • BPB (BLOB parameters block)

  • DPB (database attachment parameters block)

  • SPB_ATTACH (service attachment parameters block)

  • SPB_START (start service parameters)

  • SPB_SEND (send items in IService::query())

  • SPB_RECEIVE (receive items in IService::query())

  • SPB_RESPONSE (response from IService::query())

  • TPB (transaction parameters block)

Плагин шифрования данных передаваемых по сети

Алгоритмы, выполняющие шифрование данных для разных целей, хорошоизвестны на протяжении многих лет. Единственной "маленькой" типичнойпроблемой остается то, где можно получить секретный ключ, который будетиспользоваться этим алгоритмом. К счастью для шифрования сетевоготрафика есть одно хорошее решение — уникальный ключ шифрования долженбыть сгенерирован плагином аутентификации. По крайней мере, по умолчаниюплагин SRP может создать такой ключ. Этот ключ устойчив к атакам, в томчисле с помощью "человека в середине" (man-in-the-middle). Поэтому дляплагина шифрования сетевого трафика был выбран следующий способпредоставления ключей: получать его от плагина проверки подлинности(аутентификации). (В случае, если используемый плагин аутентификации неможет предоставить ключ, псевдоплагин может быть добавлен в спискиAuthClient и AuthServer для создания ключей, что-то вроде двухасимметричных пар приватного и публичного.)

ICryptKey

Интерфейс ICryptKey используется для хранения ключа, предоставленногоплагином аутентификации, и передает его в плагин шифрования сетевоготрафика. Этот интерфейс следует использовать следующим образом: когдаплагин аутентификации сервера или клиента готов предоставить ключ, то онзапрашивает IServerBlock илиIClientBlock для создания новогоинтерфейса ICryptKey и хранит в нем ключ. Подходящий дляIWireCryptPlugin тип ключабудет выбран Firebird и передан этому интерфейсу.

  1. setSymmetric

    void setSymmetric(StatusType* status,
                      const char* type,
                      unsigned keyLength,
                      const void* key)

    сохраняет симметричный ключ заданного типа.

  2. setAsymmetric

    void setAsymmetric(StatusType* status,
                       const char* type,
                       unsigned encryptKeyLength,
                       const void* encryptKey,
                       unsigned decryptKeyLength,
                       const void* decryptKey)

    сохраняет пару асимметричных ключей заданного типа.

  3. getEncryptKey

    const void* getEncryptKey(unsigned* length)

    возвращает ключ для шифрования.

  4. getDecryptKey

    const void* getDecryptKey(unsigned* length))

    возвращает ключ для дешифрирования (в случае симметричного ключаполучается тот же результат, что и getEncryptKey()).

IWireCryptPlugin

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

  1. getKnownTypes

    const char* getKnownTypes(StatusType* status)

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

  2. setKey

    void setKey(StatusType* status, ICryptKey* key)

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

  3. encrypt

    void encrypt(StatusType* status,
                 unsigned length,
                 const void* from,
                 void* to)

    шифрует пакет, который должен быть отправлен по сети.

  4. decrypt

    void decrypt(StatusType* status,
                 unsigned length,
                 const void* from,
                 void* to)

    расшифровывает пакет, полученный из сети.

Плагин аутентификации на серверной стороне

Плагин аутентификации содержит две требуемые части — клиентскую исерверную, а также может содержать связанную с ним третью часть —менеджер пользователей. В процессе аутентификации клиент Firebirdвызывает клиентский плагин и отправляет сгенерированные им данные насервер, затем сервер вызывает серверный плагин и отправляетсгенерированные им данные клиенту. Этот процесс повторяется до тех пор,пока оба плагина возвращают код AUTH_MORE_DATA. AUTH_SUCCESS,возвращенный на стороне сервера, означает успешную аутентификацию,AUTH_FAILED с любой стороны — немедленное прерывание итеративногопроцесса и отказ, сообщаемый клиенту, AUTH_CONTINUE означает, чтодолжен быть проверен следующий плагин из списка настроенных плагиновпроверки подлинности.

Нет выделенных примеров плагинов для аутентификации, но в исходных кодахfirebird в каталоге src/auth можно найти плагин AuthDbg, с помощьюкоторого можно учиться на тривиальном примере (без сложных вычисленийкак, например, в Srp, и без вызовов сумасшедших функций WinAPI, таких,как в AuthSspi), как клиентская и серверная сторона выполняютаутентификацию (рукопожатие).

IAuth

Интерфейс IAuth не содержит методов, только некоторые константы,определяющие коды, возвращаются из метода authenticate() вIClient иIServer.

  • AUTH_FAILED

  • AUTH_SUCCESS

  • AUTH_MORE_DATA

  • AUTH_CONTINUE

IWriter

Интерфейс IWriter — записывает блок параметров аутентификации.

  1. reset

    void reset()

    очищает целевой блок.

  2. add

    void add(StatusType* status, const char* name)

    добавляет имя логина.

  3. setType

    void setType(StatusType* status, const char* value)

    устанавливает тип добавленного логина (пользователь, роль, группа ит.д.).

  4. setDb

    void setDb(StatusType* status, const char* value)

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

IBlob

Интерфейс IBlob заменяет isc_blob_handle.

  1. getInfo

    void getInfo(StatusType* status,
                 unsigned itemsLength,
                 const unsigned char* items,
                 unsigned bufferLength,
                 unsigned char* buffer)

    заменяет isc_blob_info().

  2. getSegment

    int getSegment(StatusType* status,
                   unsigned bufferLength,
                   void* buffer,
                   unsigned* segmentLength)

    заменяет isc_get_segment(). В отличие от него никогда не возвращаютсяошибки isc_segstr_eof и isc_segment (которые на самом деле неявляются ошибками), вместо этого возвращаются коды завершенияIStatus::RESULT_NO_DATA и IStatus::RESULT_SEGMENT, обычно возвращаетIStatus::RESULT_OK.

  3. putSegment

    void putSegment(StatusType* status,
                    unsigned length,
                    const void* buffer)

    заменяет isc_put_segment().

  4. cancel

    void cancel(StatusType* status)

    заменяет isc_cancel_blob(). В случае успеха освобождает интерфейс.

  5. close

    void close(StatusType* status)

    заменяет isc_close_blob(). В случае успеха освобождает интерфейс.

  6. seek

    int seek(StatusType* status,
             int mode,
             int offset)

    заменяет isc_seek_blob().

IServerBlock

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

  1. getLogin

    const char* getLogin()

    возвращает имя пользователя, переданное от клиента.

  2. getData

    const unsigned char* getData(unsigned* length)

    возвращает данные аутентификации, переданные от клиента.

  3. putData

    void putData(StatusType* status, unsigned length, const void* data)

    передаёт данные аутентификации клиенту.

  4. newKey

    ICryptKey* newKey(StatusType* status)

    создаёт новый ключ шифрования и добавляет его в список доступных дляплагинов шифрования сетевого трафика.

IServer

Интерфейс IServer является основным интерфейсом серверной части плагинааутентификации.

  1. authenticate

    int authenticate(StatusType* status,
                     IServerBlock* sBlock,
                     IWriter* writerInterface)

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

  2. setDbCryptCallback

    void setDbCryptCallback(StatusType* status, ICryptKeyCallback* cryptCallback)

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

Плагин аутентификации на клиентской стороне

IClientBlock

Интерфейс IClientBlock используется клиентской стороной модуляаутентификации для обмена данными с сервером.

  1. getLogin

    const char* getLogin()

    Возвращает имя пользователя, если оно присутствует в DPB.

  2. getPassword

    const char* getPassword()

    Возвращает пароль, если он присутствует в DPB.

  3. getData

    const unsigned char* getData(unsigned* length)

    Возвращает данные аутентификации, переданные с сервера.

  4. putData

    void putData(StatusType* status, unsigned length, const void* data)

    Передаёт данные аутентификации на сервер.

  5. newKey

    ICryptKey* newKey(StatusType* status)

    Создаёт новый ключ шифрования и добавляет его в список доступных дляплагинов шифрования сетевого трафика

  6. getAuthBlock

    IAuthBlock* getAuthBlock(StatusType* status)

IClient

Интерфейс IClient является основным интерфейсом клиентской сторонымодуля аутентификации.

  1. authenticate

    int authenticate(StatusType* status,
                     IClientBlock* cBlock)

    выполняет один этап аутентификации. Обмен данными с серверомосуществляется с использованием интерфейса cBlock. Возможные значениявозврата определяются в интерфейсе IAuth. AUTH_SUCCESS обрабатываетсяклиентской стороной как AUTH_MORE_DATA (т.е. клиент отправляетсгенерированные данные на сервер и ждет ответа от него).

Плагин управления пользователями

Этот плагин активно связан с серверной частью проверки подлинности — онподготавливает список пользователей для плагина аутентификации. Длякаждого плагина проверки подлинности требуется менеджер пользователей —некоторые из них могут получить доступ к списку пользователей, созданныхс использованием не Firebird программного обеспечения (например,AuthSspi). Запись, описывающая пользователя, состоит из несколькихполей, и поддерживать выполнение нескольких операций, таких какдобавление пользователя, изменение пользователя, получение спискапользователей и т. д. Плагин должен уметь интерпретировать команды,полученные в интерфейсе IUser.

IUserField

Интерфейс IUserField не используется как автономный интерфейс, онявляется базовым для ICharUserField и IIntUserField.

  1. entered

    int entered()

    возвращает ненулевое значение, если было введено (присвоено) значениедля поля.

  2. specified

    int specified()

    возвращает ненулевое значение, если для поля было присвоено значениеNULL.

  3. setEntered

    void setEntered(StatusType* status, int newValue)

    устанавливает entered флаг в 0 или ненулевое значение для поля. Нетспособа назначить NULL для поля, потому что он никогда не требуется.NULL, если они используются, назначаются реализациями интерфейсами и,следовательно, имеют полный доступ к их внутренним элементам.

ICharUserField

Интерфейс ICharUserField:

  1. get

    const char* get()

    возвращает значение поля как C-строку (\0 терминальную).

  2. set

    void set(StatusType* status, const char* newValue)

    присваивает значение полю. Устанавливает флаг entered в true.

IIntUserField

Интерфейс IIntUserField:

  1. get

    int get()

    возвращает значение поля.

  2. set

    void set(StatusType* status, int newValue)

    присваивает значение полю. Устанавливает флаг entered в true.

IUser

Интерфейс IUser — это список методов доступа к полям, включенным взапись о пользователе.

  1. operation

    unsigned operation()

    код операции (см. список ниже).

  2. userName

    ICharUserField* userName()

    имя пользователя.

  3. password

    ICharUserField* password()

    пароль.

  4. firstName

    ICharUserField* firstName()

    это и 2 следующие компоненты полного имени пользователя.

  5. lastName

    ICharUserField* lastName()
  6. middleName

    ICharUserField* middleName()
  7. comment

    ICharUserField* comment()

    комментарий (из SQL оператора COMMENT ON USER IS …).

  8. attributes

    ICharUserField* attributes()

    теги в форме tag1=val1, tag2=val2, …, tagN=valN. Val может бытьпустым, что означает, что тег будет удален.

  9. active

    IIntUserField* active()

    изменяет настройку ACTIVE/INACTIVE для пользователя.

  10. admin

    IIntUserField* admin()

    устанавливает/отменяет права администратора для пользователя.

  11. clear

    void clear(StatusType* status)

    устанавливает, что все поля не введены и не указаны.

Константы, определенные пользовательским интерфейсом — действующие кодыопераций.

  • OP_USER_ADD — добавление пользователя.

  • OP_USER_MODIFY — редактирование пользователя.

  • OP_USER_DELETE — удаление пользователя.

  • OP_USER_DISPLAY — отображение пользователя.

  • OP_USER_SET_MAP — включение отображения администраторов Windows нароль RDB$ADMIN.

  • OP_USER_DROP_MAP — выключение отображения администраторов Windows нароль RDB$ADMIN.

IListUsers

Интерфейс IListUsers — это обратный вызов, используемый плагиномпроверки подлинности при запросе списка пользователей. Плагин заполняетинтерфейс IUser для всех элементов всписке пользователей один за другим и для каждого пользователя вызываетметод list() этого интерфейса.

  1. list

    void list(StatusType* status, IUser* user)

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

ILogonInfo

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

  1. name

    const char* name()

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

  2. role

    const char* role()

    возвращает активную роль текущего подключения.

  3. networkProtocol

    const char* networkProtocol()

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

  4. remoteAddress

    const char* remoteAddress()

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

  5. authBlock

    const unsigned char* authBlock(unsigned* length)

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

IConfig

Интерфейс IConfig — общий интерфейс файла конфигурации.

  1. find

    IConfigEntry* find(StatusType* status, const char* name)

    Находит запись по имени.

  2. findValue

    IConfigEntry* findValue(StatusType* status, const char* name, const char* value)

    Находит запись по имени и значению

  3. findPos

    IConfigEntry* findPos(StatusType* status, const char* name, unsigned pos)

    Находит запись по имени и позиции. Если файл конфигурации содержитстроки:

    Db=DBA
    Db=DBB
    Db=DBC

    вызов findPos(status, "Db", 2) вернет запись со значением DBB.

IManagement

Интерфейс IManagement является основным интерфейсом плагина управленияпользователями.

  1. start

    void start(StatusType* status, ILogonInfo* logonInfo)

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

  2. execute

    int execute(StatusType* status, IUser* user, IListUsers* callback)

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

  3. commit

    void commit(StatusType* status)

    подтверждает изменения, выполненные вызовами метода execute().

  4. rollback

    void rollback(StatusType* status)

    отменяет изменения, выполненные вызовами метода execute().

Плагин шифрования базы данных

Возможность шифрования базы данных присутствовала в Firebird со времёнInterbase, но соответствующие места в коде были закомментированы.Реализация была сомнительной — ключ шифрования всегда отправлялся отклиента в DPB, не было сделано попыток скрыть его от внешнего мира, и непредлагалось путей для шифрования существующих баз данных. Firebird 3.0решает большинство проблем, за исключением, вероятно, худшей — какуправлять ключами шифрования. Мы предлагаем различные типы решений, ноони требуют усилий в плагинах, т. е. нет красивого способа работы сключами как, например, для плагинов шифрования сетевого трафика.

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

ICryptKeyCallback

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

  1. callback

    unsigned callback(unsigned dataLength,
                      const void* data,
                      unsigned bufferLength,
                      void* buffer)

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

IDbCryptInfo

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

  1. getDatabaseFullPath

    const char* getDatabaseFullPath(StatusType* status)

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

IDbCryptPlugin

Интерфейс IDbCryptPlugin является основным интерфейсом плагинашифрования базы данных.

  1. setKey

    void setKey(StatusType* status,
                unsigned length,
                IKeyHolderPlugin** sources,
                const char* keyName)

    используется для предоставления информации плагину шифрования базыданных о ключе шифрования. Firebird никогда не передает ключи для этоготипа плагина напрямую. Вместо этого массивIKeyHolderPlugins заданнойдлины передается в плагин шифрования, который должен получить от одногоиз них интерфейсICryptKeyCallback и затемполучить ключ, используя его. Параметр keyName — это имя ключа, котороебыло введено в операторе ALTER DATABASE ENCRYPT …​.

  2. encrypt

    void encrypt(StatusType* status,
                 unsigned length,
                 const void* from,
                 void* to)

    шифрует данные перед записью блока в файл базы данных

  3. decrypt

    void decrypt(StatusType* status,
                 unsigned length,
                 const void* from,
                 void* to)

    расшифровывает данные после чтения блока из файла базы данных.

  4. setInfo

    void setInfo(StatusType* status,
                 IDbCryptInfo* info)

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

Хранитель ключа для плагина шифрования базы данных

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

IKeyHolderPlugin

Интерфейс IKeyHolderPlugin является основным интерфейсом для плагинахранения ключей шифрования.

  1. keyCallback

    int keyCallback(StatusType* status,
                    ICryptKeyCallback* callback)

    используется для передачи интерфейсаICryptKeyCallback вподключение (если он предоставляется пользователем с вызовомIProvider::setDbCryptCallback()). Этот вызов всегда выполняется в моментподключения к базе данных, и некоторые держатели ключа могут отклонитьподключение, если не был предоставлен удовлетворительный ключ.

  2. keyHandle

    ICryptKeyCallback* keyHandle(StatusType* status,
                                 const char* keyName)

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

  3. useOnlyOwnKeys

    FB_BOOLEAN useOnlyOwnKeys(StatusType* status)

    информирует Firebird о том, будет ли использоваться ключ,предоставленный другим держателем ключа, или нет. Имеет смысл только дляSuperServer — только он может делиться ключами шифрования базы данныхмежду подключениями. Возвращая FB_TRUE из этого метода, принудительнозаставляет Firebird убедиться, что этот конкретный держатель ключа (и,следовательно, связанное с ним подключение) предоставляет правильныйключ шифрования, прежде чем позволить ему работать с базой данных.

  4. chainHandle

    ICryptKeyCallback* chainHandle(StatusType* status)

    поддержка цепочки держателей ключей. В некоторых случаях ключ долженпроходить через более чем один держатель ключа, прежде чем он достигнетплагина шифрования базы данных. Это необходимо (например) для поддержкиEXECUTE STATEMENT в зашифрованной базе данных. Это всего лишь пример —цепочки также используются в некоторых других случаях. Интерфейсобратного вызова, возвращенный этим методом, может отличаться отвозвращаемого функцией keyHandle() (см. выше). Как правило, он должениметь возможность дублировать ключи один в один, полученные изIKeyHolderPlugin при вызове функции keyCallback().

IConfigManager

Интерфейс IConfigManager — общий интерфейс для доступа к различнымобъектам конфигурации.

  1. getDirectory

    const char* getDirectory(unsigned code)

    Возвращает местоположение соответствующего каталога в текущем экземпляреFirebird. См. коды каталогов для этого вызова ниже.

  2. getFirebirdConf

    IFirebirdConf* getFirebirdConf()

    Возвращает интерфейс для доступа к значениям конфигурации по умолчанию(из firebird.conf).

  3. getDatabaseConf

    IFirebirdConf* getDatabaseConf(const char* dbName)

    Возвращает интерфейс для доступа к конфигурации, специфичной для базыданных (берёт в расчёт firebird.conf и соответствующую частьdatabase.conf).

  4. getPluginConfig

    IConfig* getPluginConfig(const char* configuredPlugin)

    Возвращает интерфейс для доступа к конфигурации именованного плагина.

  5. getInstallDirectory

    const char* getInstallDirectory()

    Возвращает каталог, в котором установлен firebird.

  6. getRootDirectory

    const char* getRootDirectory()

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

  7. getDefaultSecurityDb

    const char* getDefaultSecurityDb()

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

Коды каталогов:

  • DIR_BIN — bin (утилиты наподобие isql, gbak, gstat);

  • DIR_SBIN — sbin (fbguard и firebird сервер);

  • DIR_CONF — каталог файлов конфигурации (firebird.conf,databases.conf, plugins.conf);

  • DIR_LIB — lib (fbclient, ib_util);

  • DIR_INC — include (ibase.h, firebird/Interfaces.h);

  • DIR_DOC — каталог документации;

  • DIR_UDF — UDF (ib_udf, fbudf);

  • DIR_SAMPLE — каталог примеров;

  • DIR_SAMPLEDB — каталог, где расположена база данных примеров(employee.fdb);

  • DIR_HELP — qli help (help.fdb);

  • DIR_INTL — каталог библиотек интернационализации (fbintl);

  • DIR_MISC — различные файлы (как манифест деинсталлятора и другое);

  • DIR_SECDB — каталог, где расположена база данных безопасности(securityN.fdb);

  • DIR_MSG — каталог, где расположен файл сообщений (firebird.msg);

  • DIR_LOG — каталог, где расположен лог файл (firebird.log);

  • DIR_GUARD — каталог, где расположена блокировка хранителя (fb_guard);

  • DIR_PLUGINS — директория плагинов ([lib]Engine12.\{dll|so}).

IConfigEntry

Интерфейс IConfigEntry — представляет запись (Key = Values с возможнымиподзаголовками (подзаписями)) в файле конфигурации firebird.

  1. getName

    const char* getName()

    Возвращает имя ключа.

  2. getValue

    const char* getValue()

    Возвращает значение в качестве символьной строки.

  3. getIntValue

    ISC_INT64 getIntValue()

    Обрабатывает значение как целое и возвращает его.

  4. getBoolValue

    FB_BOOLEAN getBoolValue()

    Обрабатывает значение как boolean и возвращает его.

  5. getSubConfig

    IConfig* getSubConfig(StatusType* status)

    Рассматривает подзаголовки как отдельный файл конфигурации и возвращаетинтерфейс IConfig для него.

IDecFloat16

Интерфейс IDecFloat16 помогает работать с типом данных DECFLOAT(16).

  1. toBcd

    void toBcd(const FB_DEC16* from, int* sign, unsigned char* bcd, int* exp)

    Преобразует десятичное значение с плавающей запятой в BCD.

  2. toString

    void toString(StatusType* status, const FB_DEC16* from, unsigned bufferLength, char* buffer)

    Преобразует десятичное значение с плавающей запятой в строку.

  3. fromBcd

    void fromBcd(int sign, const unsigned char* bcd, int exp, FB_DEC16* to)

    Собирает десятичное значение с плавающей запятой из BCD.

  4. fromString

    void fromString(StatusType* status, const char* from, FB_DEC16* to)

    Собирает десятичное значение с плавающей запятой из строки.

IDecFloat34

Интерфейс IDecFloat34 помогает работать с типом данных DECFLOAT(34).

  1. toBcd

    void toBcd(const FB_DEC34* from, int* sign, unsigned char* bcd, int* exp)

    Преобразует десятичное значение с плавающей запятой в BCD.

  2. toString

    void toString(StatusType* status, const FB_DEC34* from, unsigned bufferLength, char* buffer)

    Преобразует десятичное значение с плавающей запятой в строку.

  3. fromBcd

    void fromBcd(int sign, const unsigned char* bcd, int exp, FB_DEC34* to)

    Собирает десятичное значение с плавающей запятой из BCD.

  4. fromString

    void fromString(StatusType* status, const char* from, FB_DEC34* to)

    Собирает десятичное значение с плавающей запятой из строки.

Не интерфейсные объекты, используемые в API

Note

Они находятся в специальном заголовке Message.h C++

Следующие 3 класса используются для представления типов DATE, TIME иTIMESTAMP (datetime) при использовании макроса FB_MESSAGE. Членыструктуры данных, представляющие статическое сообщение, соответствуютполям типов FB_DATE/FB_TIME/ FB_TIMESTAMP, будут иметь тип одного изэтих классов. Для получения доступа к полям даты и времени в статическихсообщениях необходимо знать методы и члены класса (которые достаточносамо описательны, чтобы не описывать их здесь).

FbDate

Методы класса FbDate:

  1. decode

    void decode(IUtil* util,
                unsigned* year,
                unsigned* month,
                unsigned* day)
  2. getYear

    unsigned getYear(IUtil* util)
  3. getMonth

    unsigned getMonth(IUtil* util)
  4. getDay

    unsigned getDay(IUtil* util)
  5. encode

    void encode(IUtil* util,
                unsigned year,
                unsigned month,
                unsigned day)

FbTime

Методы класса FbTime:

  1. decode

    void decode(IUtil* util,
                unsigned* hours,
                unsigned* minutes,
                unsigned* seconds,
                unsigned* fractions)
  2. getHours

    unsigned getHours(IUtil* util)
  3. getMinutes

    unsigned getMinutes(IUtil* util)
  4. getSeconds

    unsigned getSeconds(IUtil* util)
  5. getFractions

    unsigned getFractions(IUtil* util)
  6. encode

    void encode(IUtil* util,
                unsigned hours,
                unsigned minutes,
                unsigned seconds,
                unsigned fractions)

FbTimestamp

Члены класса `FbTimestamp `:

  1. date

    FbDate date;
  2. time

    FbTime time;

FbChar и FbVarChar

Следующие два шаблона используются в статических сообщениях дляпредставления полей CHAR(N) и VARCHAR(N).

template <unsigned N>
struct FbChar
{
    char str[N];
};
template <unsigned N>
struct FbVarChar
{
    ISC_USHORT length;
    char str[N];
    void set(const char* s);
};

Заключение

В этом документе отсутствуют три типа плагинов — ExternalEngine, Trace и Replicator.Информация о них будет доступна в следующем выпуске.