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

Использование макросов 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);