FirebirdSQL logo

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

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