Удаление и сброс подготовленных запросов

Поскольку подготовленные запросы могут выполняться многократно, в цикле для различных значений параметров, на каждой итерации требуется сбрасывать запрос в начальное состояние. Для этого предназначена функция DatabaseReset. Но её не имеет смысла вызывать, если подготовленный запрос выполняется однократно.

bool DatabaseReset(int request)

Функция приводит внутренние откомпилированные структуры запроса в начальное состояние, как после вызова DatabasePrepare. Однако DatabaseReset не компилирует запрос заново и потому выполняется очень быстро.

Также важно, что функция не сбрасывает уже установленные привязки данных в запросе, если они были сделаны. Таким образом, при необходимости можно менять значение только одного или малого числа параметров — достаточно после вызова DatabaseReset вызывать DatabaseBind-функции только для изменившихся параметров.

На момент написания книги MQL5 API не предоставляло функцию для сброса привязки данных, аналог функции sqlite_clear_bindings в стандартном дистрибутиве SQLite.

В параметре request следует указать действующий дескриптор запроса, полученный ранее из DatabasePrepare. Если же передать дескриптор запроса, который был перед этим удален с помощью DatabaseFinalize (см. ниже), это приведёт к ошибке.

Функция возвращает признак успеха (true) или ошибки (false).

Общий принцип работы с повторяющимися запросами показан в следующем псевдо-коде. Часть из примененных функций — DatabaseBind, DatabaseRead — описана в следующих разделах и будет "упакована" в классы ORM.

struct Data                                       // пример структуры
{
   long count;
   double value;
   string comment;
};
Data data[];
...                                               // получаем массив данных
int r =
     DatabasePrepare(db"INSERT... (?, ?, ?)")); // компилируем запрос с параметрами
for(int i = 0i < ArraySize(data); ++i)          // цикл по данным
{
   DatabaseBind(r0data[i].count);             // делаем привязку данных к параметрам
   DatabaseBind(r1data[i].value);
   DatabaseBind(r2data[i].comment);
   DatabaseRead(r);                               // выполняем запрос
   ...                                            // анализ или сохранение результатов
   DatabaseReset(r);                              // на каждой итерации начальное состояние
}
DatabaseFinalize(r);

После того как необходимость в подготовленном запросе отпала, следует освободить занимаемые им ресурсы компьютера с помощью DatabaseFinalize.

void DatabaseFinalize(int request)

Функция удаляет запрос с указанным дескриптором, созданный в DatabasePrepare.

В случае передачи некорректного дескриптора функция выставит в _LastError ошибку ERR_DATABASE_INVALID_HANDLE.

При закрытии базы с помощью DatabaseClose все дескрипторы запросов, созданные для неё, автоматически удаляются и становятся недействительными.

Дополним наш ORM-слой (DBSQLite.mqh) новым классом DBQuery для работы с подготовленными запросами. Пока в нем будет лишь функционал инициализации и деинициализации, присущий концепции RAII, но скоро мы его расширим.

class DBQuery
{
protected:
   const string sql;  // запрос
   const int db;      // дескриптор базы данных (аргумент конструктора)
   const int handle;  // дескриптор подготовленного запроса
   
public:
   DBQuery(const int ownerconst string s): db(owner), sql(s),
      handle(PRTF(DatabasePrepare(dbsql)))
   {
   }
   
   ~DBQuery()
   {
      DatabaseFinalize(handle);
   }
   
   bool isValid() const
   {
      return handle != INVALID_HANDLE;
   }
   
   virtual bool reset()
   {
      return DatabaseReset(handle);
   }
   ...
};

В классе DBSQLite инициируем подготовку запроса в методе prepare за счет создания экземпляра DBQuery. Все объекты запросов будем сохранять во внутреннем массиве queries в виде автоуказателей, что позволяет вызывающему коду не следить за их явным удалением.

class DBSQLite
{
   ...
protected:
   AutoPtr<DBQueryqueries[];
public:
   DBQuery *prepare(const string sql)
   {
      return PUSH(queriesnew DBQuery(handlesql));
   }
   ...
};