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

Выполнение оператора 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++.