Открытие курсора и извлечение данных из него
Единственный способ получить строки данных, возвращаемых оператором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
.