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