【问题标题】:How to get last prepared and executed query using QsqlQuery ?如何使用 QsqlQuery 获取最后准备和执行的查询?
【发布时间】:2011-08-12 06:06:34
【问题描述】:

我正在插入:

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 (?, ?)"

如何使用实际使用的绑定值执行查询?

【问题讨论】:

  • 我想出了这个解决方法: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; }
  • 您应该删除评论并将其添加为答案。
  • 我无法将其添加为答案。哎呀!无法提交您的答案,因为:新用户在 8 小时内无法回答自己的问题。请改用 cmets,或编辑您的问题。
  • 您可以稍后添加,如果没有发布更好的解决方案,您也可以接受自己的答案。
  • 到目前为止工作完美。谢谢!

标签: c++ qt sqlite


【解决方案1】:

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

【讨论】:

  • 这是一个糟糕的想法,让您对 SQL 注入持开放态度。
【解决方案2】:

更好的功能(灵感来自 Qt 源代码:http://qt.gitorious.org/qt/qt/blobs/4.7/src/sql/kernel/qsqlresult.cpp#line644)。

此函数应处理几乎所有情况:此代码在使用名称绑定时不适用于 Oracle DB(这是唯一原生支持名称绑定的数据库 => executedQuery() 不返回带有“?”的查询,但是原始查询...)

为了能够支持原生支持DB的Name Binding,绑定值的key必须按照长度排序,然后遍历排序后的map...

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

【讨论】:

    【解决方案3】:

    您必须以相反的顺序迭代元素以获得正确的结果。

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

    固定解决方案 #1(轻步)

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

    【讨论】:

      【解决方案4】:

      如果数据库用户具有“SUPER”权限,则可以在运行时设置日志记录。我在这篇文章中找到了这个答案的一些灵感:How to show the last queries executed on MySQL?

      在prepare语句前添加如下代码:

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

      在prepare、bindValue和exec语句之后添加以下代码:

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

      执行的查询存储在数据库“mysql”的表“general_log”中。 “general_log”表将显示准备好的没有变量以及带有填充变量的查询。我没有尝试过,但可以设置 MySQL 会话变量“sql_log_off”,而不是用户不需要“SUPER”权限。见MySQL documentation

      它仅适用于 MySQL >= 5.1.12。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-06-16
        • 1970-01-01
        • 2010-11-29
        • 1970-01-01
        • 1970-01-01
        • 2016-07-29
        相关资源
        最近更新 更多