2011-04-25 118 views
15

我做一個插入:如何使用QsqlQuery獲取最後準備和執行的查詢?

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

然後我需要進行日誌記錄所執行的SQL查詢。

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

如何使用實際綁定值執行查詢?

+4

我提出了以下解決方法: '的QString getLastExecutedQuery(常量QSqlQuery&查詢) { QString的STR = query.lastQuery(); QMapIterator it(query.boundValues()); (it.hasNext()) { it.next(); str.replace(it.key(),it.value()。toString()); } return str; }' – lightstep 2011-04-25 11:02:45

+2

您應該刪除評論並將其添加爲答案。 – 2011-04-25 11:06:11

+0

我無法將其添加爲答案。 糟糕!您的答案無法提交,因爲: 新用戶無法回答自己的問題8小時。請使用評論,或者編輯您的問題。 – lightstep 2011-04-25 11:50:28

回答

4

lightstep建議的替代方法是準備查詢字符串,然後調用首先將查詢寫入日誌並僅調用實際execute()的函數。我個人使用QString :: arg()和「%number」來創建查詢字符串而不是bindValue()。

讓我們和東西:

解決方案#1(lightstep

我想出了這個解決辦法:

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

解決方案#2(我):

// 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

這是一個可怕的想法,讓你打開SQL注入。 – blarf 2015-09-23 05:56:52

6

更好的功能(受Qt源代碼的啓發:http://qt.gitorious.org/qt/qt/blobs/4.7/src/sql/kernel/qsqlresult.cpp#line644)。

該函數應該可以處理幾乎所有的情況:當使用名稱綁定(這是唯一支持本地名稱綁定=> executionQuery()的數據庫不會返回含'?'的查詢時,此代碼不適用於Oracle DB原來的查詢......)

爲了能夠支持原生支持名稱綁定DB的,綁定值的鑰匙必須由長短進行排序,然後對有序地圖環......

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

編輯:我發現前一個函數的錯誤,如果'?'存在於引用的字符串中,'?'由下一個可用值取代。該錯誤已經存在於Qt源代碼中。 這個功能應該可以解決這個問題(可能是提高了很多,但這個想法是有)

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

你要遍歷以相反的順序,以獲得正確的結果的元素。

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

固定溶液#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

如果數據庫用戶具有 「SUPER」 權利,記錄可以在運行期間設置。我找到了一些靈​​感這個答案在這篇文章:How to show the last queries executed on MySQL?

添加以下代碼在準備語句的前面:

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

添加了準備,bindValue和exec語句後,下面的代碼:

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

執行的查詢存儲在數據庫「mysql」的表「general_log」中。 「general_log」表格將顯示沒有變量的準備以及填充變量的查詢。我沒有嘗試,但可以設置MySQL會話變量「sql_log_off」,並且用戶不需要「超級」權限。請參閱MySQL documentation

它只適用於MySQL> = 5.1.12。