Как получить последний подготовленный и выполненный запрос с помощью 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 (?, ?)"
.
Как мне получить выполненный запрос с фактическими привязанными значениями, которые были использованы?
Ответы
Ответ 1
Альтернативой тому, что предлагается lightstep, является подготовка строк запроса, а затем вызов функции, которая сначала записывает запрос в журнал и только затем вызывает реальный запуск(). Я лично использую 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);
Ответ 2
Лучшая функция (вдохновленная исходным кодом Qt: http://qt.gitorious.org/qt/qt/blobs/4.7/src/sql/kernel/qsqlresult.cpp#line644).
Эта функция должна обрабатывать почти все случаи: этот код не работает с Oracle DB при использовании связывания имен (это единственная СУБД, которая изначально поддерживает имя Binding = > executeQuery(), не возвращает запрос с помощью??, но исходный запрос...)
Чтобы иметь возможность поддерживать встроенную поддержку. Имя Binding для 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;
}
Ответ 3
Вам нужно перебирать элементы в обратном порядке, чтобы получить правильный результат.
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;
}
Ответ 4
Если пользователь базы данных имеет права "SUPER", журнал может быть установлен во время выполнения. Я нашел вдохновение для этого ответа в этом сообщении: Как показать последние запросы, выполненные в MySQL?
Добавьте следующий код перед оператором подготовки:
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.