Как получить последний подготовленный и выполненный запрос, используя QsqlQuery? - PullRequest
15 голосов
/ 25 апреля 2011

Я делаю вставку:

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 ]

8 голосов
/ 17 мая 2012

Лучшая функция (на основе исходного кода Qt: http://qt.gitorious.org/qt/qt/blobs/4.7/src/sql/kernel/qsqlresult.cpp#line644).

Эта функция должна обрабатывать почти все случаи: этот код не работает с БД Oracle при использовании привязки имен (это единственная БД, котораявстроенная поддержка привязки имени => executeQuery () не возвращает запрос с «?», а исходный запрос ...)

Чтобы иметь возможность поддерживать встроенную привязку имени базы данных, ключи связанных значений должныбыть отсортирован по длине, а затем перебрать отсортированную карту ...

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;
}
4 голосов
/ 25 марта 2012

Альтернатива предложенному lightstep состоит в том, чтобы подготовить строки запроса, а затем вызвать функцию, которая сначала записывает запрос в журнал и только затем вызывает real execute ().Я лично использую QString :: arg () и "% number" в качестве аргументов для создания строки запроса вместо bindValue ().

Давайте подведем итоги:

Solution # 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);
1 голос
/ 20 мая 2016

Вы должны перебрать элементы в обратном порядке, чтобы получить правильный результат.

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;
}
0 голосов
/ 16 августа 2017

Если пользователь базы данных обладает правами «SUPER», регистрация может быть установлена ​​во время выполнения.Я нашел вдохновение для этого ответа в этом посте: Как показать последние запросы, выполненные на 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");

Выполненные запросы сохраняются в таблице "general_log" базы данных "mysql".Таблица «general_log» покажет подготовленные без переменных, а также запросы с заполненными переменными.Я не пробовал, но возможно установить переменную сеанса MySQL "sql_log_off", и тогда пользователю не нужны права "SUPER".См. Документация по MySQL .

. Работает только с MySQL> = 5.1.12.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...