2011-04-25 22 views
15

estoy haciendo una inserción:¿Cómo obtener la última consulta preparada y ejecutada usando QsqlQuery?

QSqlQuery myQuery(db); 
myQuery.prepare("INSERT INTO mytable VALUES (:val1, :val2)"); 
myQuery.bindValue(":val1", 1); 
myQuery.bindValue(":val2", 2); 
myQuery.exec(); 

entonces necesito para obtener la consulta SQL ejecutada para propósitos de registro.

myQuery.executedQuery() devuelve "INSERT INTO mytable VALUES (?, ?)".

¿Cómo puedo obtener la consulta ejecutada con los valores reales encuadernados que se utilizaron?

+4

me ocurrió con esta solución: 'QString getLastExecutedQuery (const QSqlQuery y consulta) { QString cadena = query.lastQuery(); QMapIterator it (query.boundValues ​​()); mientras (it.hasNext()) { it.next(); str.replace (it.key(), it.value(). ToString()); } return str; } ' – lightstep

+2

se debe eliminar el comentario y añadirlo como una respuesta. –

+0

No puedo agregarlo como respuesta. ¡Uy! Su respuesta no pudo presentarse porque: Los nuevos usuarios no pueden responder a su propia pregunta durante 8 horas. Por favor use comentarios o edite su pregunta en su lugar. – lightstep

Respuesta

4

Una alternativa a lo que lightstep sugirió, es preparar cadenas de consulta y luego llamar a una función que primero escribe la consulta en el registro y solo luego llama a ejecutar real(). Yo personalmente uso QString :: arg() y "% number" para argumentos para hacer una cadena de consulta en lugar de bindValue().

Vayamos de resumir las cosas:

Solución # 1 (lightstep)

me ocurrió con esta solución:

QString getLastExecutedQuery(const QSqlQuery& query) 
{ 
QString str = query.lastQuery(); 
QMapIterator<QString, QVariant> it(query.boundValues()); 
while (it.hasNext()) 
{ 
    it.next(); 
    str.replace(it.key(),it.value().toString()); 
} 
return str; 
} 

Solución # 2 (ME):

// my helper function 

#define SQLDB_SHOW_QUERIES 
#define SQLDB_LOG_QUERIES 
#define SQLDB_LOG_FILENAME "sqlite.db.log" 

bool executeQuery(QSqlQuery& queryObject, const QString& query) 
{ 
bool result = true;; 
#ifdef SQLDB_SHOW_QUERIES 
std::cout<<query.toStdString()<<std::endl; 
#endif 
#ifdef SQLDB_LOG_QUERIES 
std::fstream fs_log; 
fs_log.open(SQLDB_LOG_FILENAME,std::ios::out|std::ios::app); 
if (fs_log.is_open()) 
{ 
    fs_log<<query.toUtf8().data()<<std::endl; 
} 
#endif 
result &= queryObject.exec(query); 
#ifdef SQLDB_SHOW_QUERIES 
if (!result) std::cout<<queryObject.lastError().text().toStdString()<<std::endl; 
std::cout<<std::endl; 
#endif 
#ifdef SQLDB_LOG_QUERIES 
if (fs_log.is_open()) 
{ 
    if (!result) fs_log<<queryObject.lastError().text().toUtf8().data()<<std::endl; 
    fs_log<<std::endl; 
    fs_log.close(); 
} 
#endif 
return result; 
} 

// your sample code 

QSqlQuery myQuery(db); 
QString query = QString("INSERT INTO mytable VALUES (%1,%2)") 
.arg(1).arg(2); 
executeQuery(myQuery,query); 
+8

Esta es una idea terrible que lo deja abierto a la inyección de SQL. – blarf

6

Una mejor función (inspirada en el código fuente de Qt: http://qt.gitorious.org/qt/qt/blobs/4.7/src/sql/kernel/qsqlresult.cpp#line644).

Esta función debería manejar casi todos los casos: Este código no funciona con Oracle DB cuando se usa Enlace de nombre (Este es el único DB que admite nativamente Enlace de nombre => executeQuery() no devuelve la consulta con '?' Pero la consulta original ...)

para ser capaz de soportar el apoyo Nombre nativo unión de DB, las claves de valores de la envolvente debe ser ordenada por la longitud, a continuación, un bucle sobre el mapa ordenada ...

QString getLastExecutedQuery(const QSqlQuery& query) 
{ 
    QString sql = query.executedQuery(); 
    const int nbBindValues = query.boundValues().size(); 

    for(int i = 0, j = 0; j < nbBindValues; ++j) 
    { 
     i = sql.indexOf(QLatin1Char('?'), i); 
     if (i <= 0) 
     { 
      break; 
     } 
     const QVariant &var = query.boundValue(j); 
     QSqlField field(QLatin1String(""), var.type()); 
     if (var.isNull()) 
     { 
      field.clear(); 
     } 
     else 
     { 
      field.setValue(var); 
     } 
     QString formatV = query.driver()->formatValue(field); 
     sql.replace(i, 1, formatV); 
     i += formatV.length(); 
    } 

    return sql; 
} 

Editar: encontré un error en la función anterior, si un '?' existe dentro de una cadena citada, el '?' es reemplazado por el siguiente valor disponible. El error ya existe en el código fuente de Qt. Esta función debería solucionar este problema (se podría mejorar mucho, pero la idea está ahí)

QString getLastExecutedQuery(const QSqlQuery& query) 
{ 
    QString sql = query.executedQuery(); 
    int nbBindValues = query.boundValues().size(); 

    for(int i = 0, j = 0; j < nbBindValues;) 
    { 
     int s = sql.indexOf(QLatin1Char('\''), i); 
     i = sql.indexOf(QLatin1Char('?'), i); 
     if (i < 1) 
     { 
      break; 
     } 

     if(s < i && s > 0) 
     { 
      i = sql.indexOf(QLatin1Char('\''), s + 1) + 1; 
      if(i < 2) 
      { 
       break; 
      } 
     } 
     else 
     { 
      const QVariant &var = query.boundValue(j); 
      QSqlField field(QLatin1String(""), var.type()); 
      if (var.isNull()) 
      { 
       field.clear(); 
      } 
      else 
      { 
       field.setValue(var); 
      } 
      QString formatV = query.driver()->formatValue(field); 
      sql.replace(i, 1, formatV); 
      i += formatV.length(); 
      ++j; 
     } 
    } 

    return sql; 
} 
1

Tienes que iterar sobre los elementos en orden inverso para obtener el resultado correcto.

Example: 
Query: " :a :aa " 
query.bindValue(":a",1); 
query.bindValue(":aa",1); 
getLastExecutedQuery will return: "1 1a" 

solución Corregido # 1 (lightstep)

QString getLastExecutedQuery(const QSqlQuery& query) 
{ 
    QString str = query.lastQuery(); 
    QMapIterator<QString, QVariant> it(query.boundValues()); 

    it.toBack(); 

    while (it.hasPrevious()) 
    { 
     it.previous(); 
     str.replace(it.key(),it.value().toString()); 
    } 
    return str; 
} 
0

Si el usuario base de datos tiene derechos "SUPER", el registro se puede establecer durante el tiempo de ejecución.He encontrado algo de inspiración para esta respuesta en este post: How to show the last queries executed on MySQL?

Agregue el código siguiente en la parte delantera de la sentencia PREPARE:

QSqlQuery query("SET GLOBAL log_output = 'TABLE'"); 
query.exec("SET GLOBAL general_log = 'ON'"); 

Agregue el código siguiente después de la preparación, de declaraciones bindValue y exec:

query.exec("SET GLOBAL general_log = 0"); 

las consultas ejecutadas se almacenan en la tabla "general_log" de las bases de datos "mysql". La tabla "general_log" mostrará el preparado sin variables, así como las consultas con las variables rellenas. No lo probé, pero podría ser posible configurar la variable de sesión de MySQL "sql_log_off" y que el usuario no necesita derechos "SUPER". Ver MySQL documentation.

sólo funciona con MySQL> = 5.1.12.

Cuestiones relacionadas