diff options
author | Matt Newell <newellm@blur.com> | 2012-03-26 10:46:22 -0700 |
---|---|---|
committer | Qt by Nokia <qt-info@nokia.com> | 2012-03-30 09:31:03 +0200 |
commit | cff46983a8823fda13cafa2c8774153525f0d4d1 (patch) | |
tree | 00c6625f23cfa64d9173ad1cd3662786cb025cfe /src/sql/kernel/qsqlresult.cpp | |
parent | 3ec88b355b5c9649d128fcf18cb2abc39ac0e770 (diff) |
Allow named bind values to be used multiple times per query
Prepared queries should be able to use a name parameter more than
once. Currently this will result in undefined behavior and crashes.
This patch fixes the bug and implements the needed test case.
Task-number: QTBUG-6420
Change-Id: I07d6537e432a9b2781e9ef3d9f597bceb054527e
Reviewed-by: Andy Shaw <andy.shaw@digia.com>
Reviewed-by: Honglei Zhang <honglei.zhang@nokia.com>
Diffstat (limited to 'src/sql/kernel/qsqlresult.cpp')
-rw-r--r-- | src/sql/kernel/qsqlresult.cpp | 77 |
1 files changed, 42 insertions, 35 deletions
diff --git a/src/sql/kernel/qsqlresult.cpp b/src/sql/kernel/qsqlresult.cpp index 16df1b8b06..f9dbae306b 100644 --- a/src/sql/kernel/qsqlresult.cpp +++ b/src/sql/kernel/qsqlresult.cpp @@ -114,16 +114,18 @@ public: QString executedQuery; QHash<int, QSql::ParamType> types; QVector<QVariant> values; - typedef QHash<QString, int> IndexMap; + typedef QHash<QString, QList<int> > IndexMap; IndexMap indexes; typedef QVector<QHolder> QHolderVector; QHolderVector holders; }; +static QString qFieldSerial(int); + QString QSqlResultPrivate::holderAt(int index) const { - return indexes.key(index); + return holders.size() > index ? holders.at(index).holderName : qFieldSerial(index); } // return a unique id for bound names @@ -159,6 +161,8 @@ QString QSqlResultPrivate::positionalToNamedBinding() for (int i = 0; i < n; ++i) { QChar ch = sql.at(i); if (ch == QLatin1Char('?') && !inQuote) { + // Update the holder position since we are changing the holder name lengths + holders[count].holderPos = result.size(); result += qFieldSerial(count++); } else { if (ch == QLatin1Char('\'')) @@ -188,7 +192,9 @@ QString QSqlResultPrivate::namedToPositionalBinding() int pos = i + 2; while (pos < n && qIsAlnum(sql.at(pos))) ++pos; - indexes[sql.mid(i, pos - i)] = count++; + QString holder(sql.mid(i, pos - i)); + indexes[holder].append(count++); + holders.append(QHolder(holder, i)); result += QLatin1Char('?'); i = pos; } else { @@ -199,6 +205,7 @@ QString QSqlResultPrivate::namedToPositionalBinding() } } result.squeeze(); + values.resize(holders.size()); return result; } @@ -610,27 +617,32 @@ bool QSqlResult::savePrepare(const QString& query) */ bool QSqlResult::prepare(const QString& query) { - int n = query.size(); + if (d->holders.isEmpty()) { + int n = query.size(); - bool inQuote = false; - int i = 0; - - while (i < n) { - QChar ch = query.at(i); - if (ch == QLatin1Char(':') && !inQuote - && (i == 0 || query.at(i - 1) != QLatin1Char(':')) - && (i + 1 < n && qIsAlnum(query.at(i + 1)))) { - int pos = i + 2; - while (pos < n && qIsAlnum(query.at(pos))) - ++pos; + bool inQuote = false; + int i = 0; - d->holders.append(QHolder(query.mid(i, pos - i), i)); - i = pos; - } else { - if (ch == QLatin1Char('\'')) - inQuote = !inQuote; - ++i; + while (i < n) { + QChar ch = query.at(i); + if (ch == QLatin1Char(':') && !inQuote + && (i == 0 || query.at(i - 1) != QLatin1Char(':')) + && (i + 1 < n && qIsAlnum(query.at(i + 1)))) { + int pos = i + 2; + while (pos < n && qIsAlnum(query.at(pos))) + ++pos; + + QString holder(query.mid(i, pos - i)); + d->indexes[holder].append(d->holders.size()); + d->holders.append(QHolder(holder, i)); + i = pos; + } else { + if (ch == QLatin1Char('\'')) + inQuote = !inQuote; + ++i; + } } + d->values.resize(d->holders.size()); } d->sql = query; return true; // fake prepares should always succeed @@ -653,7 +665,7 @@ bool QSqlResult::exec() QString holder; for (i = d->holders.count() - 1; i >= 0; --i) { holder = d->holders.at(i).holderName; - val = d->values.value(d->indexes.value(holder)); + val = d->values.value(d->indexes.value(holder).value(0,-1)); QSqlField f(QLatin1String(""), val.type()); f.setValue(val); query = query.replace(d->holders.at(i).holderPos, @@ -697,7 +709,7 @@ bool QSqlResult::exec() void QSqlResult::bindValue(int index, const QVariant& val, QSql::ParamType paramType) { d->binds = PositionalBinding; - d->indexes[qFieldSerial(index)] = index; + d->indexes[qFieldSerial(index)].append(index); if (d->values.count() <= index) d->values.resize(index + 1); d->values[index] = val; @@ -727,19 +739,14 @@ void QSqlResult::bindValue(const QString& placeholder, const QVariant& val, d->binds = NamedBinding; // if the index has already been set when doing emulated named // bindings - don't reset it - int idx = d->indexes.value(placeholder, -1); - if (idx >= 0) { + QList<int> indexes = d->indexes.value(placeholder); + foreach (int idx, indexes) { if (d->values.count() <= idx) d->values.resize(idx + 1); d->values[idx] = val; - } else { - d->values.append(val); - idx = d->values.count() - 1; - d->indexes[placeholder] = idx; + if (paramType != QSql::In || !d->types.isEmpty()) + d->types[idx] = paramType; } - - if (paramType != QSql::In || !d->types.isEmpty()) - d->types[idx] = paramType; } /*! @@ -776,8 +783,8 @@ QVariant QSqlResult::boundValue(int index) const */ QVariant QSqlResult::boundValue(const QString& placeholder) const { - int idx = d->indexes.value(placeholder, -1); - return d->values.value(idx); + QList<int> indexes = d->indexes.value(placeholder); + return d->values.value(indexes.value(0,-1)); } /*! @@ -798,7 +805,7 @@ QSql::ParamType QSqlResult::bindValueType(int index) const */ QSql::ParamType QSqlResult::bindValueType(const QString& placeholder) const { - return d->types.value(d->indexes.value(placeholder, -1), QSql::In); + return d->types.value(d->indexes.value(placeholder).value(0,-1), QSql::In); } /*! |