FirebirdSQL logo
 Интерфейсы FirebirdНаписание плагинов 

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

Прежде всего нам нужно получить доступ к интерфейсу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) примеры и попытатьсязапустить их.

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

Чтобы начать пользоваться сервисами (службами), прежде всего необходимоподключиться к менеджеру сервисов. Это делается с помощью метода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);