Borrar y reiniciar consultas preparadas

Dado que las consultas preparadas pueden ejecutarse varias veces, en un bucle para diferentes valores de parámetros, es necesario restablecer la consulta al estado inicial en cada iteración. De ello se encarga la función DatabaseReset. Pero no tiene sentido llamarlo si la consulta preparada se ejecuta una vez.

bool DatabaseReset(int request)

La función restablece las estructuras internas de consulta compiladas al estado inicial, de forma similar a cuando se llama a DatabasePrepare. Sin embargo, DatabaseReset no recompila la consulta, por lo que es muy rápido.

También es importante que la función no anule las vinculaciones de datos ya establecidas en la consulta si se ha realizado alguna. Así, si es necesario, puede cambiar el valor de un único parámetro o de un número reducido de ellos. Entonces, después de llamar a DatabaseReset, puede simplemente llamar a las funciones DatabaseBind sólo para los parámetros modificados.

En el momento de escribir el libro, la API de MQL5 no proporcionaba una función para restablecer el enlace de datos, un análogo de la función sqlite_clear_bindings en la distribución estándar SQLite.

En el parámetro request, especifique el manejador válido de la consulta obtenido anteriormente de DatabasePrepare. Si se pasa un manejador de la consulta que se ha eliminado previamente con DatabaseFinalize (ver más abajo), se devolverá un error.

La función devuelve un indicador de éxito (true) o de error (false).

El principio general de trabajo con consultas recurrentes se muestra en el pseudocódigo que se muestra más abajo. Las funciones DatabaseBind y DatabaseRead se describirán en las secciones siguientes y se «empaquetarán» en clases 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);

Una vez que la consulta preparada ya no sea necesaria, deberá liberar los recursos informáticos que ocupa mediante DatabaseFinalize.

void DatabaseFinalize(int request)

La función elimina la consulta con el manejador especificado, creada en DatabasePrepare.

Si se pasa un descriptor incorrecto, la función registrará ERR_DATABASE_INVALID_HANDLE en _LastError.

Al cerrar la base de datos con DatabaseClose, todos los gestores de consulta creados para él se eliminan e invalidan automáticamente.

Vamos a complementar nuestra capa ORM (DBSQLite.mqh) con una nueva clase DBQuery para trabajar con consultas preparadas. Por ahora, sólo contendrá la funcionalidad de inicialización y desinicialización inherente al concepto RAII, pero pronto la ampliaremos.

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);
   }
   ...
};

En la clase DBSQLite, iniciamos la preparación de la solicitud en el método prepare creando una instancia de DBQuery. Todos los objetos de consulta se almacenarán en el array interno queries en forma de punteros automáticos, lo que permite que el código de llamada no siga su eliminación explícita.

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