QSqlResult: fix parsing of bound SQL statements

Parsing for bound SQL parameters now handles identifier quoting using
double quotes (") and square brackets ([]).

The following has only 1 bound value but previously 2 were detected:
SELECT 1 AS "A?b[?']]]de?ghi", ?

Task-number: QTBUG-27159
Change-Id: Icfd02187e1126ff3b5ed11df8d4e599f574e61bf
Reviewed-by: Mark Brand <mabrand@mabrand.nl>
This commit is contained in:
Israel Lins 2013-02-07 16:18:05 -03:00 committed by The Qt Project
parent c8e34ed678
commit 776c488b6f
3 changed files with 93 additions and 21 deletions

View File

@ -87,19 +87,35 @@ QString QSqlResultPrivate::positionalToNamedBinding(const QString &query, QStrin
QString result; QString result;
result.reserve(n * 5 / 4); result.reserve(n * 5 / 4);
bool inQuote = false; QChar closingQuote;
int count = 0; int count = 0;
for (int i = 0; i < n; ++i) { for (int i = 0; i < n; ++i) {
QChar ch = query.at(i); QChar ch = query.at(i);
if (ch == QLatin1Char('?') && !inQuote) { if (!closingQuote.isNull()) {
if (ch == closingQuote) {
if (closingQuote == QLatin1Char(']')
&& i + 1 < n && query.at(i + 1) == closingQuote) {
// consume the extra character. don't close.
++i;
result += ch;
} else {
closingQuote = QChar();
}
}
result += ch;
} else {
if (ch == QLatin1Char('?')) {
result += fieldSerialFunc(count++); result += fieldSerialFunc(count++);
} else { } else {
if (ch == QLatin1Char('\'')) if (ch == QLatin1Char('\'') || ch == QLatin1Char('"') || ch == QLatin1Char('`'))
inQuote = !inQuote; closingQuote = ch;
else if (ch == QLatin1Char('['))
closingQuote = QLatin1Char(']');
result += ch; result += ch;
} }
} }
}
result.squeeze(); result.squeeze();
return result; return result;
} }
@ -110,13 +126,27 @@ QString QSqlResultPrivate::namedToPositionalBinding(const QString &query)
QString result; QString result;
result.reserve(n); result.reserve(n);
bool inQuote = false; QChar closingQuote;
int count = 0; int count = 0;
int i = 0; int i = 0;
while (i < n) { while (i < n) {
QChar ch = query.at(i); QChar ch = query.at(i);
if (ch == QLatin1Char(':') && !inQuote if (!closingQuote.isNull()) {
if (ch == closingQuote) {
if (closingQuote == QLatin1Char(']')
&& i + 1 < n && query.at(i + 1) == closingQuote) {
// consume the extra character. don't close.
++i;
result += ch;
} else {
closingQuote = QChar();
}
}
result += ch;
++i;
} else {
if (ch == QLatin1Char(':')
&& (i == 0 || query.at(i - 1) != QLatin1Char(':')) && (i == 0 || query.at(i - 1) != QLatin1Char(':'))
&& (i + 1 < n && qIsAlnum(query.at(i + 1)))) { && (i + 1 < n && qIsAlnum(query.at(i + 1)))) {
int pos = i + 2; int pos = i + 2;
@ -128,12 +158,15 @@ QString QSqlResultPrivate::namedToPositionalBinding(const QString &query)
result += QLatin1Char('?'); result += QLatin1Char('?');
i = pos; i = pos;
} else { } else {
if (ch == QLatin1Char('\'')) if (ch == QLatin1Char('\'') || ch == QLatin1Char('"') || ch == QLatin1Char('`'))
inQuote = !inQuote; closingQuote = ch;
else if (ch == QLatin1Char('['))
closingQuote = QLatin1Char(']');
result += ch; result += ch;
++i; ++i;
} }
} }
}
result.squeeze(); result.squeeze();
values.resize(holders.size()); values.resize(holders.size());
return result; return result;

View File

@ -58,6 +58,11 @@ public:
return QSqlResult::savePrepare(sqlquery); return QSqlResult::savePrepare(sqlquery);
} }
QVector<QVariant> boundValues() const
{
return QSqlResult::boundValues();
}
protected: protected:
QVariant data(int /* index */) { return QVariant(); } QVariant data(int /* index */) { return QVariant(); }
bool isNull(int /* index */) { return false; } bool isNull(int /* index */) { return false; }

View File

@ -53,6 +53,7 @@ public:
private slots: private slots:
void positionalToNamedBinding(); void positionalToNamedBinding();
void parseOfBoundValues();
}; };
@ -66,6 +67,39 @@ void tst_QSqlResult::positionalToNamedBinding()
TestSqlDriverResult result(&testDriver); TestSqlDriverResult result(&testDriver);
QString query("INSERT INTO MYTABLE (ID, NAME, BIRTH) VALUES(?, ?, ?)"); QString query("INSERT INTO MYTABLE (ID, NAME, BIRTH) VALUES(?, ?, ?)");
QVERIFY(result.savePrepare(query)); QVERIFY(result.savePrepare(query));
QCOMPARE(result.boundValues().count(), 3);
}
void tst_QSqlResult::parseOfBoundValues()
{
TestSqlDriver testDriver;
TestSqlDriverResult result(&testDriver);
QVERIFY(result.savePrepare("SELECT :1 AS \":2\""));
QCOMPARE(result.boundValues().count(), 1);
QVERIFY(result.savePrepare("SELECT :1 AS ':2'"));
QCOMPARE(result.boundValues().count(), 1);
QVERIFY(result.savePrepare("SELECT :1 AS [:2]"));
QCOMPARE(result.boundValues().count(), 1);
QVERIFY(result.savePrepare("SELECT :1 AS [:2]]]"));
QCOMPARE(result.boundValues().count(), 1);
QVERIFY(result.savePrepare("SELECT :1 AS [:2]]]]]"));
QCOMPARE(result.boundValues().count(), 1);
QVERIFY(result.savePrepare("SELECT ? AS \"?\""));
QCOMPARE(result.boundValues().count(), 1);
QVERIFY(result.savePrepare("SELECT ? AS '?'"));
QCOMPARE(result.boundValues().count(), 1);
QVERIFY(result.savePrepare("SELECT ? AS [?]"));
QCOMPARE(result.boundValues().count(), 1);
QVERIFY(result.savePrepare("SELECT ? AS \"'?\""));
QCOMPARE(result.boundValues().count(), 1);
QVERIFY(result.savePrepare("SELECT ? AS '?\"'"));
QCOMPARE(result.boundValues().count(), 1);
QVERIFY(result.savePrepare("SELECT ? AS '?''?'"));
QCOMPARE(result.boundValues().count(), 1);
QVERIFY(result.savePrepare("SELECT ? AS [\"?']"));
QCOMPARE(result.boundValues().count(), 1);
} }
QTEST_MAIN( tst_QSqlResult ) QTEST_MAIN( tst_QSqlResult )