Ideal solution would be patching Qt (since 2011 it's not changed in Qt 5 nor Qt 6):
The method:
QString QSqlQuery::executedQuery() const
should call (in implementation for SQLite) function:
char *sqlite3_expanded_sql(sqlite3_stmt *pStmt);
We can choose named or positioned variant based on value of
QSqlResult::BindingSyntax QSqlResult::bindingSyntax() const
but it's [protected] - available only for DB backend creators.
Internally, in QSqlResult binding syntax changed to 'positioned' in case of calling one of two methods:
void addBindValue(const QVariant &val, QSql::ParamType paramType = QSql::In)
void bindValue(int pos, const QVariant &val, QSql::ParamType paramType = QSql::In)
So we can create a subclass of QSqlQuery, override those methods and keep track of bind syntax. Also can override executedQuery() to choose appropriate formatting function.
With current QT, we can manually call one of two functions, because we know which type of binding we used in our code:
QString formatNamedSqlQuery(const QSqlQuery *query)
{
QString qstr = query->executedQuery();
QMap<QString, QVariant> vals = query->boundValues();
QMap<QString, QVariant>::const_iterator i;
for(i = vals.constBegin() ; i != vals.constEnd() ; ++i) {
const QVariant &var = i.value();
QSqlField field(QLatin1String(""), var.type());
if (var.isNull()) {
field.clear();
} else {
field.setValue(var);
}
QString formatV = query->driver()->formatValue(field);
if (formatV.size() > 100)
formatV = formatV.left(100);
qstr.replace(i.key(), formatV);
}
return qstr;
}
QString formatPositionedSqlQuery(const QSqlQuery *query)
{
QString qstr = query->lastQuery();
QList<QVariant> list = query->boundValues().values();
QString val;
int i = 0;
int idx = 0;
for (idx = 0; idx < list.count(); ++idx) {
i = qstr.indexOf(QLatin1Char('?'), i);
if (i == -1)
continue;
QVariant var = list.value(idx);
QSqlField f(QLatin1String(""), QVariant::Type(var.userType()));
if (var.isNull())
f.clear();
else
f.setValue(var);
val = query->driver()->formatValue(f);
qstr = qstr.replace(i, 1, val);
i += val.length();
}
return qstr;
}
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; }str.replace(it.key(), it.value().isNull() ? "NULL" : "'" + it.value().toString() + "'")