From 1ba0ce0cc1ab94f1397114ac8024a60dc7dfbdd8 Mon Sep 17 00:00:00 2001 From: Honglei Zhang Date: Wed, 30 Nov 2011 13:26:22 +0200 Subject: QSQLITE support only one statement at a time SQLite driver support only one statement at a time. This fix makes the exec and prepare call failed if more than one statements are given. This is bug fix for QTBUG-21884. Also the behaviour is documented in the API specification. Task-number: QTBUG-21884 Reviewed-by: Charles Yin --- src/sql/drivers/sqlite/qsql_sqlite.cpp | 11 ++++++-- src/sql/kernel/qsqlquery.cpp | 6 +++++ tests/auto/qsqlquery/tst_qsqlquery.cpp | 47 ++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 2 deletions(-) diff --git a/src/sql/drivers/sqlite/qsql_sqlite.cpp b/src/sql/drivers/sqlite/qsql_sqlite.cpp index 9fba6d6..8294a55 100644 --- a/src/sql/drivers/sqlite/qsql_sqlite.cpp +++ b/src/sql/drivers/sqlite/qsql_sqlite.cpp @@ -322,12 +322,14 @@ bool QSQLiteResult::prepare(const QString &query) setSelect(false); + const void *pzTail = NULL; + #if (SQLITE_VERSION_NUMBER >= 3003011) int res = sqlite3_prepare16_v2(d->access, query.constData(), (query.size() + 1) * sizeof(QChar), - &d->stmt, 0); + &d->stmt, &pzTail); #else int res = sqlite3_prepare16(d->access, query.constData(), (query.size() + 1) * sizeof(QChar), - &d->stmt, 0); + &d->stmt, &pzTail); #endif if (res != SQLITE_OK) { @@ -335,6 +337,11 @@ bool QSQLiteResult::prepare(const QString &query) "Unable to execute statement"), QSqlError::StatementError, res)); d->finalize(); return false; + } else if (pzTail && !QString(reinterpret_cast(pzTail)).trimmed().isEmpty()) { + setLastError(qMakeError(d->access, QCoreApplication::translate("QSQLiteResult", + "Unable to execute multiple statements at a time"), QSqlError::StatementError, SQLITE_MISUSE)); + d->finalize(); + return false; } return true; } diff --git a/src/sql/kernel/qsqlquery.cpp b/src/sql/kernel/qsqlquery.cpp index 361730a..753e4f8 100644 --- a/src/sql/kernel/qsqlquery.cpp +++ b/src/sql/kernel/qsqlquery.cpp @@ -338,6 +338,9 @@ bool QSqlQuery::isNull(int field) const Note that the last error for this query is reset when exec() is called. + For SQLite, the query string can contain only one statement at a time. + If more than one statements is give, the function returns false. + Example: \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 34 @@ -887,6 +890,9 @@ void QSqlQuery::clear() syntactically wrong query succeeds, but every consecutive exec() will fail. + For SQLite, the query string can contain only one statement at a time. + If more than one statements are give, the function returns false. + Example: \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 9 diff --git a/tests/auto/qsqlquery/tst_qsqlquery.cpp b/tests/auto/qsqlquery/tst_qsqlquery.cpp index 592b49a..3cbdb63 100644 --- a/tests/auto/qsqlquery/tst_qsqlquery.cpp +++ b/tests/auto/qsqlquery/tst_qsqlquery.cpp @@ -213,6 +213,8 @@ private slots: void QTBUG_5765(); void QTBUG_14132_data() { generic_data("QOCI"); } void QTBUG_14132(); + void QTBUG_21884_data() { generic_data("QSQLITE"); } + void QTBUG_21884(); void sqlite_constraint_data() { generic_data("QSQLITE"); } void sqlite_constraint(); @@ -328,6 +330,7 @@ void tst_QSqlQuery::dropTestTables( QSqlDatabase db ) << qTableName("bug6421", __FILE__).toUpper() << qTableName("bug5765", __FILE__) << qTableName("bug6852", __FILE__) + << qTableName("bug21884", __FILE__) << qTableName( "qtest_lockedtable", __FILE__ ) << qTableName( "Planet", __FILE__ ) << qTableName( "task_250026", __FILE__ ) @@ -3104,6 +3107,50 @@ void tst_QSqlQuery::QTBUG_5765() QCOMPARE(q.value(0).toInt(), 123); } +/** +* This test case tests multiple statements in one execution. +* Sqlite driver doesn't support multiple statement at one time. +* If more than one statement is given, the exec or prepare function +* return failure to the client. +*/ +void tst_QSqlQuery::QTBUG_21884() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + QSqlQuery q(db); + + QStringList stList; + QString tableName(qTableName("bug21884", __FILE__ )); + stList << "create table " + tableName + "(id integer primary key, note string)"; + stList << "select * from " + tableName + ";"; + stList << "select * from " + tableName + "; \t\n\r"; + stList << "drop table " + tableName; + + + foreach (const QString& st, stList) { + QVERIFY_SQL(q, exec(st)); + } + + foreach (const QString& st, stList) { + QVERIFY_SQL(q, prepare(st)); + QVERIFY_SQL(q, exec()); + } + + stList.clear(); + stList << "create table " + tableName + "(id integer primary key); select * from " + tableName; + stList << "create table " + tableName + "(id integer primary key); syntax error!;"; + stList << "create table " + tableName + "(id integer primary key);;"; + stList << "create table " + tableName + "(id integer primary key);\'\"\a\b\b\v"; + + foreach (const QString&st , stList) { + QVERIFY2(!q.prepare(st), qPrintable(QString("the statement is expected to fail! ") + st)); + QVERIFY2(!q.exec(st), qPrintable(QString("the statement is expected to fail! ") + st)); + } +} + + void tst_QSqlQuery::oraOCINumber() { QFETCH( QString, dbName ); -- cgit v0.12 From 67d146092a6c11f1ca3c622ae0737202ca292a58 Mon Sep 17 00:00:00 2001 From: Shane Kearns Date: Wed, 30 Nov 2011 13:39:23 +0000 Subject: Backport SMP safe initialisation of QNetworkConfigurationManager The original commit in Qt5 uses new atomics API that is not in Qt4. I have replaced with equivalent calls. Also the init/cleanup functions are different, so those are renamed. Reviewed-by: mread Task-number: ou1cimx1#946677 Original commit message follows: Author: Thiago Macieira Date: Wed Jul 6 00:06:15 2011 +0200 Fix the QNetworkConnectionManagerPrivate initialisation code. The current code was meant to be a thread-safe initialisation that also ran a couple of extra steps. But it wasn't. While it's ok to call qAddPostRoutine(), the call to updateConfigurations() was thread-unsafe. It is possible that another thread got the pointer to the Private before updateConfigurations() finished. So instead protect the initialisation with a mutex. It's possible that the value of the pointer becomes visible to other processors before the other contained values, so use atomics here. To call qAddPostRoutine safely from the main thread, use the trick of deleteLater() (which is thread-safe) in another thread connecting to a slot. --- src/network/bearer/qnetworkconfigmanager.cpp | 66 ++++++++++++++++++---------- src/network/bearer/qnetworkconfigmanager_p.h | 3 ++ 2 files changed, 47 insertions(+), 22 deletions(-) diff --git a/src/network/bearer/qnetworkconfigmanager.cpp b/src/network/bearer/qnetworkconfigmanager.cpp index 8065025..da09a9c 100644 --- a/src/network/bearer/qnetworkconfigmanager.cpp +++ b/src/network/bearer/qnetworkconfigmanager.cpp @@ -46,34 +46,56 @@ #include #include +#include +#include +#include #ifndef QT_NO_BEARERMANAGEMENT QT_BEGIN_NAMESPACE -#define Q_GLOBAL_STATIC_QAPP_DESTRUCTION(TYPE, NAME) \ - static QGlobalStatic this_##NAME \ - = { Q_BASIC_ATOMIC_INITIALIZER(0), false }; \ - static void NAME##_cleanup() \ - { \ - this_##NAME.pointer->cleanup(); \ - this_##NAME.pointer = 0; \ - } \ - static TYPE *NAME() \ - { \ - if (!this_##NAME.pointer) { \ - TYPE *x = new TYPE; \ - if (!this_##NAME.pointer.testAndSetOrdered(0, x)) \ - delete x; \ - else { \ - qAddPostRoutine(NAME##_cleanup); \ - this_##NAME.pointer->initialize(); \ - } \ - } \ - return this_##NAME.pointer; \ - } +static QBasicAtomicPointer connManager_ptr; +Q_GLOBAL_STATIC(QMutex, connManager_mutex) + +static void connManager_cleanup() +{ + // this is not atomic or thread-safe! + if(connManager_ptr) + connManager_ptr->cleanup(); + connManager_ptr = 0; +} + +void QNetworkConfigurationManagerPrivate::addPostRoutine() +{ + qAddPostRoutine(connManager_cleanup); +} -Q_GLOBAL_STATIC_QAPP_DESTRUCTION(QNetworkConfigurationManagerPrivate, connManager); +static QNetworkConfigurationManagerPrivate *connManager() +{ + QNetworkConfigurationManagerPrivate *ptr = connManager_ptr.fetchAndAddAcquire(0); + if (!ptr) { + QMutexLocker locker(connManager_mutex()); + if (!(ptr = connManager_ptr.fetchAndAddAcquire(0))) { + ptr = new QNetworkConfigurationManagerPrivate; + + if (QCoreApplicationPrivate::mainThread() == QThread::currentThread()) { + // right thread or no main thread yet + ptr->addPostRoutine(); + ptr->initialize(); + } else { + // wrong thread, we need to make the main thread do this + QObject *obj = new QObject; + QObject::connect(obj, SIGNAL(destroyed()), ptr, SLOT(addPostRoutine()), Qt::DirectConnection); + ptr->initialize(); // this moves us to the right thread + obj->moveToThread(QCoreApplicationPrivate::mainThread()); + obj->deleteLater(); + } + + connManager_ptr.fetchAndStoreRelease(ptr); + } + } + return ptr; +} QNetworkConfigurationManagerPrivate *qNetworkConfigurationManagerPrivate() { diff --git a/src/network/bearer/qnetworkconfigmanager_p.h b/src/network/bearer/qnetworkconfigmanager_p.h index 56f66b4..ded1701 100644 --- a/src/network/bearer/qnetworkconfigmanager_p.h +++ b/src/network/bearer/qnetworkconfigmanager_p.h @@ -94,6 +94,8 @@ public: public Q_SLOTS: void updateConfigurations(); + static void addPostRoutine(); + Q_SIGNALS: void configurationAdded(const QNetworkConfiguration &config); void configurationRemoved(const QNetworkConfiguration &config); @@ -108,6 +110,7 @@ private Q_SLOTS: void pollEngines(); + private: Q_INVOKABLE void startPolling(); QTimer *pollTimer; -- cgit v0.12 From 70c1e9589bf390bc3010663f133d87a789f17c3e Mon Sep 17 00:00:00 2001 From: Ralf Habacker Date: Wed, 30 Nov 2011 20:21:24 +0100 Subject: fixed error generating wrong introspection string in header output file When using dbus introspection files with CRLF encoding the CR is not handled and results into a CR code at a wrong location in the output file Task-number: QTBUG-17634 Merge-request: 1478 Reviewed-by: thiago --- tools/qdbus/qdbusxml2cpp/qdbusxml2cpp.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/qdbus/qdbusxml2cpp/qdbusxml2cpp.cpp b/tools/qdbus/qdbusxml2cpp/qdbusxml2cpp.cpp index bc617a9..9c49183 100644 --- a/tools/qdbus/qdbusxml2cpp/qdbusxml2cpp.cpp +++ b/tools/qdbus/qdbusxml2cpp/qdbusxml2cpp.cpp @@ -466,11 +466,13 @@ static QString stringify(const QString &data) int i; for (i = 0; i < data.length(); ++i) { retval += QLatin1Char('\"'); - for ( ; i < data.length() && data[i] != QLatin1Char('\n'); ++i) + for ( ; i < data.length() && data[i] != QLatin1Char('\n') && data[i] != QLatin1Char('\r'); ++i) if (data[i] == QLatin1Char('\"')) retval += QLatin1String("\\\""); else retval += data[i]; + if (data[i] == QLatin1Char('\r') && data[i+1] == QLatin1Char('\n')) + i++; retval += QLatin1String("\\n\"\n"); } return retval; -- cgit v0.12 From 27c322e0f88fa0cccba8cf914655cacb5dae51de Mon Sep 17 00:00:00 2001 From: Takumi ASAKI Date: Wed, 30 Nov 2011 20:29:29 +0100 Subject: Qt Linguist: Fix crashes Fix crash when select translation after close and open file. This patches includes refactoring of b68b59f252930538cc124b31decc990ab57bea20 Merge-request: 1480 Reviewed-by: Oswald Buddenhagen --- tools/linguist/linguist/messageeditor.cpp | 12 ++++++++---- tools/linguist/linguist/messageeditor.h | 2 +- tools/linguist/linguist/messageeditorwidgets.cpp | 9 ++++++--- tools/linguist/linguist/messageeditorwidgets.h | 5 ++++- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/tools/linguist/linguist/messageeditor.cpp b/tools/linguist/linguist/messageeditor.cpp index 87c2f4a..2888290 100644 --- a/tools/linguist/linguist/messageeditor.cpp +++ b/tools/linguist/linguist/messageeditor.cpp @@ -262,7 +262,6 @@ void MessageEditor::addPluralForm(int model, const QString &label, bool writable { FormMultiWidget *transEditor = new FormMultiWidget(label); connect(transEditor, SIGNAL(editorCreated(QTextEdit*)), SLOT(editorCreated(QTextEdit*))); - connect(transEditor, SIGNAL(editorDeleted(QTextEdit*)), SLOT(editorDeleted(QTextEdit*))); transEditor->setEditingEnabled(writable); transEditor->setHideWhenEmpty(!writable); if (!m_editors[model].transTexts.isEmpty()) @@ -299,9 +298,9 @@ void MessageEditor::editorCreated(QTextEdit *te) } } -void MessageEditor::editorDeleted(QTextEdit *te) +void MessageEditor::editorDestroyed() { - if (m_selectionHolder == te) + if (m_selectionHolder == sender()) resetSelection(); } @@ -352,9 +351,13 @@ static void clearSelection(QTextEdit *t) void MessageEditor::selectionChanged(QTextEdit *te) { if (te != m_selectionHolder) { - if (m_selectionHolder) + if (m_selectionHolder) { clearSelection(m_selectionHolder); + disconnect(this, SLOT(editorDestroyed())); + } m_selectionHolder = (te->textCursor().hasSelection() ? te : 0); + if (FormatTextEdit *fte = qobject_cast(m_selectionHolder)) + connect(fte, SIGNAL(editorDestroyed()), SLOT(editorDestroyed())); updateCanCutCopy(); } } @@ -371,6 +374,7 @@ void MessageEditor::resetSelection() { if (m_selectionHolder) { clearSelection(m_selectionHolder); + disconnect(this, SLOT(editorDestroyed())); m_selectionHolder = 0; updateCanCutCopy(); } diff --git a/tools/linguist/linguist/messageeditor.h b/tools/linguist/linguist/messageeditor.h index 21b3405..0fbf658 100644 --- a/tools/linguist/linguist/messageeditor.h +++ b/tools/linguist/linguist/messageeditor.h @@ -114,7 +114,7 @@ public slots: private slots: void editorCreated(QTextEdit *); - void editorDeleted(QTextEdit *); + void editorDestroyed(); void selectionChanged(QTextEdit *); void resetHoverSelection(); void emitTranslationChanged(QTextEdit *); diff --git a/tools/linguist/linguist/messageeditorwidgets.cpp b/tools/linguist/linguist/messageeditorwidgets.cpp index 29df673..042ef3d 100644 --- a/tools/linguist/linguist/messageeditorwidgets.cpp +++ b/tools/linguist/linguist/messageeditorwidgets.cpp @@ -130,6 +130,11 @@ FormatTextEdit::FormatTextEdit(QWidget *parent) m_highlighter = new MessageHighlighter(this); } +FormatTextEdit::~FormatTextEdit() +{ + emit editorDestroyed(); +} + void FormatTextEdit::setEditable(bool editable) { // save default frame style @@ -362,11 +367,9 @@ void FormMultiWidget::setTranslation(const QString &text, bool userAction) QStringList texts = text.split(QChar(Translator::BinaryVariantSeparator), QString::KeepEmptyParts); while (m_editors.count() > texts.count()) { - FormatTextEdit *editor = m_editors.takeLast(); - emit editorDeleted(editor); delete m_minusButtons.takeLast(); delete m_plusButtons.takeLast(); - delete editor; + delete m_editors.takeLast(); } while (m_editors.count() < texts.count()) addEditor(m_editors.count()); diff --git a/tools/linguist/linguist/messageeditorwidgets.h b/tools/linguist/linguist/messageeditorwidgets.h index 1f6f1f5..c6d9201 100644 --- a/tools/linguist/linguist/messageeditorwidgets.h +++ b/tools/linguist/linguist/messageeditorwidgets.h @@ -91,8 +91,12 @@ class FormatTextEdit : public ExpandingTextEdit Q_OBJECT public: FormatTextEdit(QWidget *parent = 0); + ~FormatTextEdit(); void setEditable(bool editable); +signals: + void editorDestroyed(); + public slots: void setPlainText(const QString & text, bool userAction); @@ -150,7 +154,6 @@ public: signals: void editorCreated(QTextEdit *); - void editorDeleted(QTextEdit *); void textChanged(QTextEdit *); void selectionChanged(QTextEdit *); void cursorPositionChanged(); -- cgit v0.12 From e20eaed5c1968e32eca97cf449fa588cfab35a5d Mon Sep 17 00:00:00 2001 From: Sami Rosendahl Date: Wed, 30 Nov 2011 20:36:56 +0100 Subject: Fix stack overwrite in QDBusDemarshaller QDBusArgument extraction operators and QDBusDemarshaller that implements the extraction do not check the type of the extracted value. Helper function template qIterGet in qdbusdemarshaller.cpp that is used for extracting basic data types only reserves space from the stack for the expected type as specified by client. If the actual type in the DBus parameter is larger stack will be overwritten in the helper function by at most 7 bytes (expected one byte, received dbus_uint_64_t of size 8 bytes). The fix always reserves space for the largest basic type dbus_uint64_t readable by dbus_message_iter_get_basic API. See also http://dbus.freedesktop.org/doc/api/html/group__DBusMessage.html#ga41c23a05e552d0574d0444d4693d18ab PMO 280456 Task-number: QTBUG-22735 Merge-request: 1469 Reviewed-by: thiago --- src/dbus/qdbusdemarshaller.cpp | 24 +++++++- tests/auto/qdbusmarshall/tst_qdbusmarshall.cpp | 82 ++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 3 deletions(-) diff --git a/src/dbus/qdbusdemarshaller.cpp b/src/dbus/qdbusdemarshaller.cpp index d9bb5b5..4103552 100644 --- a/src/dbus/qdbusdemarshaller.cpp +++ b/src/dbus/qdbusdemarshaller.cpp @@ -48,10 +48,28 @@ QT_BEGIN_NAMESPACE template static inline T qIterGet(DBusMessageIter *it) { - T t; - q_dbus_message_iter_get_basic(it, &t); + // Use a union of expected and largest type q_dbus_message_iter_get_basic + // will return to ensure reading the wrong basic type does not result in + // stack overwrite + union { + // The value to be extracted + T t; + // Largest type that q_dbus_message_iter_get_basic will return + // according to dbus_message_iter_get_basic API documentation + dbus_uint64_t maxValue; + // A pointer to ensure no stack overwrite in case there is a platform + // where sizeof(void*) > sizeof(dbus_uint64_t) + void* ptr; + } value; + + // Initialize the value in case a narrower type is extracted to it. + // Note that the result of extracting a narrower type in place of a wider + // one and vice-versa will be platform-dependent. + value.t = T(); + + q_dbus_message_iter_get_basic(it, &value); q_dbus_message_iter_next(it); - return t; + return value.t; } QDBusDemarshaller::~QDBusDemarshaller() diff --git a/tests/auto/qdbusmarshall/tst_qdbusmarshall.cpp b/tests/auto/qdbusmarshall/tst_qdbusmarshall.cpp index cca212e..9754a84 100644 --- a/tests/auto/qdbusmarshall/tst_qdbusmarshall.cpp +++ b/tests/auto/qdbusmarshall/tst_qdbusmarshall.cpp @@ -93,6 +93,9 @@ private slots: void receiveUnknownType_data(); void receiveUnknownType(); + void demarshallPrimitives_data(); + void demarshallPrimitives(); + private: int fileDescriptorForTest(); @@ -1168,5 +1171,84 @@ void tst_QDBusMarshall::receiveUnknownType() #endif } +void tst_QDBusMarshall::demarshallPrimitives_data() +{ + sendBasic_data(); +} + +template +QVariant demarshallPrimitiveAs(const QDBusArgument& dbusArg) +{ + T val; + dbusArg >> val; + return qVariantFromValue(val); +} + +QVariant demarshallPrimitiveAs(int typeIndex, const QDBusArgument& dbusArg) +{ + switch (typeIndex) { + case 0: + return demarshallPrimitiveAs(dbusArg); + case 1: + return demarshallPrimitiveAs(dbusArg); + case 2: + return demarshallPrimitiveAs(dbusArg); + case 3: + return demarshallPrimitiveAs(dbusArg); + case 4: + return demarshallPrimitiveAs(dbusArg); + case 5: + return demarshallPrimitiveAs(dbusArg); + case 6: + return demarshallPrimitiveAs(dbusArg); + case 7: + return demarshallPrimitiveAs(dbusArg); + case 8: + return demarshallPrimitiveAs(dbusArg); + default: + return QVariant(); + } +} + +void tst_QDBusMarshall::demarshallPrimitives() +{ + QFETCH(QVariant, value); + QFETCH(QString, sig); + + QDBusConnection con = QDBusConnection::sessionBus(); + + QVERIFY(con.isConnected()); + + // Demarshall each test data value to all primitive types to test + // demarshalling to the wrong type does not cause a crash + for (int typeIndex = 0; true; ++typeIndex) { + QDBusMessage msg = QDBusMessage::createMethodCall(serviceName, objectPath, + interfaceName, "ping"); + QDBusArgument sendArg; + sendArg.beginStructure(); + sendArg.appendVariant(value); + sendArg.endStructure(); + msg.setArguments(QVariantList() << qVariantFromValue(sendArg)); + QDBusMessage reply = con.call(msg); + + const QDBusArgument receiveArg = qvariant_cast(reply.arguments().at(0)); + receiveArg.beginStructure(); + QCOMPARE(receiveArg.currentSignature(), sig); + + const QVariant receiveValue = demarshallPrimitiveAs(typeIndex, receiveArg); + if (receiveValue.type() == value.type()) { + // Value type is the same, compare the values + QCOMPARE(receiveValue, value); + QVERIFY(receiveArg.atEnd()); + } + + receiveArg.endStructure(); + QVERIFY(receiveArg.atEnd()); + + if (!receiveValue.isValid()) + break; + } +} + QTEST_MAIN(tst_QDBusMarshall) #include "tst_qdbusmarshall.moc" -- cgit v0.12