删除和重置预置查询

由于预置查询可以多次执行,在针对不同参数值的循环中,需要在每次迭代时将查询重置到初始状态。这是由 DatabaseReset 函数完成的。但如果预置查询只执行一次,则调用它没有意义。

bool DatabaseReset(int request)

该函数将内部编译的查询结构重置到初始状态,类似于调用 DatabasePrepare。但是,DatabaseReset 不会重新编译查询,因此速度非常快。

同样重要的是,如果已进行任何数据绑定,该函数不会重置查询中已建立的数据绑定。因此,如果需要,你可以只更改一个或少量参数的值。然后,在调用 DatabaseReset 之后,你只需为已更改的参数调用 DatabaseBind 函数即可。

在撰写本书时,MQL5 API 没有提供重置数据绑定的函数,即标准 SQLite 发行版中类似于 sqlite_clear_bindings 函数的函数。

request 参数中,指定先前从 DatabasePrepare 获取的查询的有效句柄。如果你传递先前已用 DatabaseFinalize(见下文)移除的查询句柄,将返回错误。

该函数返回操作状态标识:成功 (true) 或错误 (false)。

处理重复查询的一般原则如下面的伪代码所示。DatabaseBindDatabaseRead 函数将在后续章节中描述,并将被“封装”到 ORM 类中。

struct Data                                       // structure example
{
   long count;
   double value;
   string comment;
};
Data data[];
...                                               // getting data array
int r =
     DatabasePrepare(db"INSERT... (?, ?, ?)")); // compile query with parameters
for(int i = 0i < ArraySize(data); ++i)          // data loop
{
   DatabaseBind(r0data[i].count);             // make data binding to parameters
   DatabaseBind(r1data[i].value);
   DatabaseBind(r2data[i].comment);
   DatabaseRead(r);                               // execute request
   ...                                            // analyze or save results
   DatabaseReset(r);                              // initial state at each iteration
}
DatabaseFinalize(r);

当不再需要预置查询后,你应该使用 DatabaseFinalize 释放它所占用的计算机资源。

void DatabaseFinalize(int request)

该函数删除在 DatabasePrepare中创建的、具有指定句柄的查询。

如果传递了不正确的描述符,该函数会将 ERR_DATABASE_INVALID_HANDLE 记录到 _LastError

使用 DatabaseClose关闭数据库时,为其创建的所有查询句柄都会自动移除并失效。

我们通过一个新的 DBQuery 类来补充我们的 ORM 层 (DBSQLite.mqh),用于处理预置查询。目前,它只包含 RAII 概念固有的初始化和反初始化功能,但我们很快会对其进行扩展。

class DBQuery
{
protected:
   const string sql;  // query
   const int db;      // database handle (constructor argument)
   const int handle;  // prepared request 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 类中,我们通过创建 DBQuery 的实例,在 prepare 方法中初始化请求的准备工作。所有查询对象都将以自动指针的形式存储在内部数组 queries 中,这使得调用代码无需关注它们的显式删除。

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