summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorQt Forward Merge Bot <qt_forward_merge_bot@qt-project.org>2019-12-12 01:01:03 +0100
committerQt Forward Merge Bot <qt_forward_merge_bot@qt-project.org>2019-12-12 01:01:04 +0100
commit11d7788c18bf062c204782b3661e67b45e818819 (patch)
tree7e6df60892757c060ecf9d2698b893f8b93315e7
parentb3c0e9afa0041d4d45e47880732deda1dd1013b9 (diff)
parent3162345670ffe67a71a62abaeda0d8eb8ad0682e (diff)
Merge remote-tracking branch 'origin/5.14' into 5.15
-rw-r--r--configure.json4
-rw-r--r--configure.pri20
-rw-r--r--mkspecs/features/resources.prf10
-rw-r--r--mkspecs/features/resources_functions.prf25
-rw-r--r--mkspecs/features/wasm/default_pre.prf25
-rw-r--r--mkspecs/features/wasm/wasm.prf5
-rw-r--r--src/3rdparty/sqlite/patches/0001-Fix-CVE-2019-19244-in-SQLite.patch26
-rw-r--r--src/3rdparty/sqlite/sqlite3.c1
-rw-r--r--src/corelib/kernel/qtimer.cpp8
-rw-r--r--src/corelib/kernel/qvariant.cpp35
-rw-r--r--src/corelib/kernel/qvariant_p.h5
-rw-r--r--src/corelib/time/qdatetime.cpp104
-rw-r--r--src/plugins/sqldrivers/README4
-rw-r--r--src/plugins/sqldrivers/psql/qsql_psql.cpp34
-rw-r--r--src/sql/doc/snippets/code/doc_src_sql-driver.qdoc57
-rw-r--r--src/sql/doc/src/sql-driver.qdoc58
-rw-r--r--src/testlib/qtest.h2
-rw-r--r--src/tools/androiddeployqt/main.cpp19
-rw-r--r--src/tools/rcc/rcc.cpp20
-rw-r--r--src/widgets/doc/snippets/code/src_gui_dialogs_qfiledialog.cpp4
-rw-r--r--tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp97
-rw-r--r--tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp38
22 files changed, 460 insertions, 141 deletions
diff --git a/configure.json b/configure.json
index 105b51a81c..30c5dd0420 100644
--- a/configure.json
+++ b/configure.json
@@ -1481,6 +1481,10 @@ Configure with '-qreal float' to create a build that is binary-compatible with 5
"type": "error",
"condition": "!features.stl",
"message": "Qt requires a compliant STL library."
+ },
+ {
+ "type": "emccVersion",
+ "condition": "config.wasm"
}
],
diff --git a/configure.pri b/configure.pri
index fe8656acb5..5f54ea8388 100644
--- a/configure.pri
+++ b/configure.pri
@@ -1226,6 +1226,12 @@ defineReplace(qtConfOutputPostProcess_publicPro) {
"QT_RELEASE_DATE = $$config.input.qt_release_date"
}
+ wasm: {
+ qt_emcc_version = $$qtSystemEmccVersion()
+ output += \
+ "QT_EMCC_VERSION = $$qt_emcc_version"
+ }
+
return($$output)
}
@@ -1258,6 +1264,12 @@ defineReplace(qtConfOutputPostProcess_publicHeader) {
!isEmpty(config.input.qt_libinfix): \
output += "$${LITERAL_HASH}define QT_LIBINFIX \"$$eval(config.input.qt_libinfix)\""
+ wasm: {
+ qt_emcc_version = $$qtSystemEmccVersion()
+output += \
+ "$${LITERAL_HASH}define QT_EMCC_VERSION \"$$qt_emcc_version\""
+ }
+
return($$output)
}
@@ -1340,6 +1352,14 @@ defineTest(qtConfReport_buildMode) {
qtConfReportPadded($$1, $$build_mode)
}
+defineTest(qtConfReport_emccVersion) {
+ EMCC_VERSION = $$qtSystemEmccVersion()
+ REQ_VERSION = $$qtEmccRecommendedVersion()
+ !equals(EMCC_VERSION, $$REQ_VERSION) {
+ qtConfAddReport("You should use the recommended Emscripten version $$REQ_VERSION with this Qt. You have $$EMCC_VERSION $$QT_EMCC_VERSION")
+ }
+}
+
# ensure pristine environment for configuration
discard_from($$[QT_HOST_DATA/get]/mkspecs/qconfig.pri)
discard_from($$[QT_HOST_DATA/get]/mkspecs/qmodule.pri)
diff --git a/mkspecs/features/resources.prf b/mkspecs/features/resources.prf
index ca95768de8..cee0981db0 100644
--- a/mkspecs/features/resources.prf
+++ b/mkspecs/features/resources.prf
@@ -6,16 +6,6 @@ isEmpty(QMAKE_MOD_RCC):QMAKE_MOD_RCC = qrc
!contains(QMAKE_RESOURCE_FLAGS, -root):!isEmpty(QMAKE_RESOURCE_ROOT):QMAKE_RESOURCE_FLAGS += -root $$QMAKE_RESOURCE_ROOT
!contains(QMAKE_RESOURCE_FLAGS, -name): QMAKE_RESOURCE_FLAGS += -name ${QMAKE_FILE_BASE}
-# http://www.w3.org/TR/xml/#syntax
-defineReplace(xml_escape) {
- 1 ~= s,&,&amp;,
- 1 ~= s,\',&apos;,
- 1 ~= s,\",&quot;,
- 1 ~= s,<,&lt;,
- 1 ~= s,>,&gt;,
- return($$1)
-}
-
load(resources_functions)
qtFlattenResources()
diff --git a/mkspecs/features/resources_functions.prf b/mkspecs/features/resources_functions.prf
index f7fed9e524..b7a15fd1d9 100644
--- a/mkspecs/features/resources_functions.prf
+++ b/mkspecs/features/resources_functions.prf
@@ -1,5 +1,21 @@
+# http://www.w3.org/TR/xml/#syntax
+defineReplace(xml_escape) {
+ 1 ~= s,&,&amp;,
+ 1 ~= s,\',&apos;,
+ 1 ~= s,\",&quot;,
+ 1 ~= s,<,&lt;,
+ 1 ~= s,>,&gt;,
+ return($$1)
+}
+
defineTest(qtFlattenResources) {
- RESOURCES += qmake_immediate
+ immediate = qmake_immediate$$QMAKE_RESOURCES_IMMEDIATE_NR
+ defined(QMAKE_RESOURCES_IMMEDIATE_NR, var): \
+ QMAKE_RESOURCES_IMMEDIATE_NR = $$num_add($$QMAKE_RESOURCES_IMMEDIATE_NR, 1)
+ else: \
+ QMAKE_RESOURCES_IMMEDIATE_NR = 1
+
+ RESOURCES += $$immediate
for(resource, RESOURCES) {
# Regular case of user qrc file
contains(resource, ".*\\.qrc$"): \
@@ -7,10 +23,10 @@ defineTest(qtFlattenResources) {
# Fallback for stand-alone files/directories
!defined($${resource}.files, var) {
- !equals(resource, qmake_immediate) {
+ !equals(resource, $$immediate) {
!exists($$absolute_path($$resource, $$_PRO_FILE_PWD_)): \
warning("Failure to find: $$resource")
- qmake_immediate.files += $$resource
+ $${immediate}.files += $$resource
OTHER_FILES *= $$resource
}
RESOURCES -= $$resource
@@ -56,8 +72,9 @@ defineTest(qtFlattenResources) {
RESOURCES -= $$resource
RESOURCES += $$resource_file
}
+ export(QMAKE_RESOURCES_IMMEDIATE_NR)
export(RESOURCES)
export(OTHER_FILES)
- export(qmake_immediate.files)
+ export($${immediate}.files)
return(true)
}
diff --git a/mkspecs/features/wasm/default_pre.prf b/mkspecs/features/wasm/default_pre.prf
new file mode 100644
index 0000000000..982ef0498f
--- /dev/null
+++ b/mkspecs/features/wasm/default_pre.prf
@@ -0,0 +1,25 @@
+load(default_pre)
+
+defineReplace(qtEmccRecommendedVersion) {
+ return (1.38.27)
+}
+
+defineReplace(qtSystemEmccVersion) {
+ E_VERSION = $$system("emcc -v 2>&1 | perl -alne $$shell_quote($_ = $F[9]; s/://; print;) ")
+ return ($${E_VERSION})
+}
+
+defineTest(qtConfTest_emccVersion) {
+
+ REQ_VERSION = $$qtEmccRecommendedVersion()
+ EMCC_VERSION = $$qtSystemEmccVersion()
+
+ !defined(QT_EMCC_VERSION, var):!equals(EMCC_VERSION, $${REQ_VERSION}) {
+ warning ("You should use the recommended Emscripten version $$REQ_VERSION with this Qt. You have $${EMCC_VERSION} ")
+ }
+ contains(TEMPLATE, .*app) {
+ !equals(QT_EMCC_VERSION, $$EMCC_VERSION) {
+ warning("This Qt was built with Emscripten version $${QT_EMCC_VERSION}. You have $${EMCC_VERSION}. The difference may cause issues.")
+ }
+ }
+}
diff --git a/mkspecs/features/wasm/wasm.prf b/mkspecs/features/wasm/wasm.prf
index 5773e58010..2e886fc4a5 100644
--- a/mkspecs/features/wasm/wasm.prf
+++ b/mkspecs/features/wasm/wasm.prf
@@ -1,8 +1,11 @@
+
# DESTDIR will be empty if not set in the app .pro file; make sure it has a value
isEmpty(DESTDIR): DESTDIR = $$OUT_PWD
exists($$QMAKE_QT_CONFIG) {
+## this may be subject to change
+
qtConfig(thread) {
EMCC_THREAD_LFLAGS += -s USE_PTHREADS=1
@@ -109,6 +112,8 @@ contains(TEMPLATE, .*app) {
}
}
+qtConfTest_emccVersion()
+
# Pass --source-map-base on the linker line. This informs the
# browser where to find the source files when debugging.
WASM_SOURCE_MAP_BASE = http://localhost:8000/
diff --git a/src/3rdparty/sqlite/patches/0001-Fix-CVE-2019-19244-in-SQLite.patch b/src/3rdparty/sqlite/patches/0001-Fix-CVE-2019-19244-in-SQLite.patch
new file mode 100644
index 0000000000..9906292860
--- /dev/null
+++ b/src/3rdparty/sqlite/patches/0001-Fix-CVE-2019-19244-in-SQLite.patch
@@ -0,0 +1,26 @@
+From 676425e522e08eb0e7dfaacdac79a5de27542322 Mon Sep 17 00:00:00 2001
+From: Andy Shaw <andy.shaw@qt.io>
+Date: Wed, 11 Dec 2019 10:51:22 +0100
+Subject: [PATCH 53/53] Fix CVE-2019-19244 in SQLite
+
+Fixes: QTBUG-80635
+Change-Id: I718349e28ec76ea164dd50f2a985f2074dd6bdbd
+---
+ src/3rdparty/sqlite/sqlite3.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/3rdparty/sqlite/sqlite3.c b/src/3rdparty/sqlite/sqlite3.c
+index 8fd740b300..bd647ca1c2 100644
+--- a/src/3rdparty/sqlite/sqlite3.c
++++ b/src/3rdparty/sqlite/sqlite3.c
+@@ -131679,6 +131679,7 @@ SQLITE_PRIVATE int sqlite3Select(
+ */
+ if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct
+ && sqlite3ExprListCompare(sSort.pOrderBy, pEList, -1)==0
++ && p->pWin==0
+ ){
+ p->selFlags &= ~SF_Distinct;
+ pGroupBy = p->pGroupBy = sqlite3ExprListDup(db, pEList, 0);
+--
+2.21.0 (Apple Git-122.2)
+
diff --git a/src/3rdparty/sqlite/sqlite3.c b/src/3rdparty/sqlite/sqlite3.c
index 8fd740b300..bd647ca1c2 100644
--- a/src/3rdparty/sqlite/sqlite3.c
+++ b/src/3rdparty/sqlite/sqlite3.c
@@ -131679,6 +131679,7 @@ SQLITE_PRIVATE int sqlite3Select(
*/
if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct
&& sqlite3ExprListCompare(sSort.pOrderBy, pEList, -1)==0
+ && p->pWin==0
){
p->selFlags &= ~SF_Distinct;
pGroupBy = p->pGroupBy = sqlite3ExprListDup(db, pEList, 0);
diff --git a/src/corelib/kernel/qtimer.cpp b/src/corelib/kernel/qtimer.cpp
index f843fc4236..25ce0c032f 100644
--- a/src/corelib/kernel/qtimer.cpp
+++ b/src/corelib/kernel/qtimer.cpp
@@ -84,10 +84,10 @@ QT_BEGIN_NAMESPACE
must start and stop the timer in its thread; it is not possible to
start a timer from another thread.
- As a special case, a QTimer with a timeout of 0 will time out as
- soon as all the events in the window system's event queue have
- been processed. This can be used to do heavy work while providing
- a snappy user interface:
+ As a special case, a QTimer with a timeout of 0 will time out as soon as
+ possible, though the ordering between zero timers and other sources of
+ events is unspecified. Zero timers can be used to do some work while still
+ providing a snappy user interface:
\snippet timers/timers.cpp 4
\snippet timers/timers.cpp 5
diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp
index 455dd5069f..40a401361d 100644
--- a/src/corelib/kernel/qvariant.cpp
+++ b/src/corelib/kernel/qvariant.cpp
@@ -4534,15 +4534,24 @@ QSequentialIterable::const_iterator QSequentialIterable::end() const
return it;
}
+static const QVariant variantFromVariantDataHelper(const QtMetaTypePrivate::VariantData &d) {
+ QVariant v;
+ if (d.metaTypeId == qMetaTypeId<QVariant>())
+ v = *reinterpret_cast<const QVariant*>(d.data);
+ else
+ v = QVariant(d.metaTypeId, d.data, d.flags & ~QVariantConstructionFlags::ShouldDeleteVariantData);
+ if (d.flags & QVariantConstructionFlags::ShouldDeleteVariantData)
+ QMetaType::destroy(d.metaTypeId, const_cast<void *>(d.data));
+ return v;
+}
+
/*!
Returns the element at position \a idx in the container.
*/
QVariant QSequentialIterable::at(int idx) const
{
const QtMetaTypePrivate::VariantData d = m_impl.at(idx);
- if (d.metaTypeId == qMetaTypeId<QVariant>())
- return *reinterpret_cast<const QVariant*>(d.data);
- return QVariant(d.metaTypeId, d.data, d.flags);
+ return variantFromVariantDataHelper(d);
}
/*!
@@ -4619,9 +4628,7 @@ QSequentialIterable::const_iterator::operator=(const const_iterator &other)
const QVariant QSequentialIterable::const_iterator::operator*() const
{
const QtMetaTypePrivate::VariantData d = m_impl.getCurrent();
- if (d.metaTypeId == qMetaTypeId<QVariant>())
- return *reinterpret_cast<const QVariant*>(d.data);
- return QVariant(d.metaTypeId, d.data, d.flags);
+ return variantFromVariantDataHelper(d);
}
/*!
@@ -4953,10 +4960,7 @@ QAssociativeIterable::const_iterator::operator=(const const_iterator &other)
const QVariant QAssociativeIterable::const_iterator::operator*() const
{
const QtMetaTypePrivate::VariantData d = m_impl.getCurrentValue();
- QVariant v(d.metaTypeId, d.data, d.flags);
- if (d.metaTypeId == qMetaTypeId<QVariant>())
- return *reinterpret_cast<const QVariant*>(d.data);
- return v;
+ return variantFromVariantDataHelper(d);
}
/*!
@@ -4965,10 +4969,7 @@ const QVariant QAssociativeIterable::const_iterator::operator*() const
const QVariant QAssociativeIterable::const_iterator::key() const
{
const QtMetaTypePrivate::VariantData d = m_impl.getCurrentKey();
- QVariant v(d.metaTypeId, d.data, d.flags);
- if (d.metaTypeId == qMetaTypeId<QVariant>())
- return *reinterpret_cast<const QVariant*>(d.data);
- return v;
+ return variantFromVariantDataHelper(d);
}
/*!
@@ -4976,11 +4977,7 @@ const QVariant QAssociativeIterable::const_iterator::key() const
*/
const QVariant QAssociativeIterable::const_iterator::value() const
{
- const QtMetaTypePrivate::VariantData d = m_impl.getCurrentValue();
- QVariant v(d.metaTypeId, d.data, d.flags);
- if (d.metaTypeId == qMetaTypeId<QVariant>())
- return *reinterpret_cast<const QVariant*>(d.data);
- return v;
+ return operator*();
}
/*!
diff --git a/src/corelib/kernel/qvariant_p.h b/src/corelib/kernel/qvariant_p.h
index 3d87beac83..b8b63b5e6f 100644
--- a/src/corelib/kernel/qvariant_p.h
+++ b/src/corelib/kernel/qvariant_p.h
@@ -105,6 +105,11 @@ inline T *v_cast(QVariant::Private *d, T * = nullptr)
#endif
+enum QVariantConstructionFlags : uint {
+ Default = 0x0,
+ PointerType = 0x1,
+ ShouldDeleteVariantData = 0x2 // only used in Q*Iterable
+};
//a simple template that avoids to allocate 2 memory chunks when creating a QVariant
template <class T> class QVariantPrivateSharedEx : public QVariant::PrivateShared
diff --git a/src/corelib/time/qdatetime.cpp b/src/corelib/time/qdatetime.cpp
index d2d09ceee6..67d37f19d8 100644
--- a/src/corelib/time/qdatetime.cpp
+++ b/src/corelib/time/qdatetime.cpp
@@ -1626,6 +1626,29 @@ qint64 QDate::daysTo(const QDate &d) const
*/
#if QT_CONFIG(datestring)
+namespace {
+
+struct ParsedInt { int value = 0; bool ok = false; };
+
+/*
+ /internal
+
+ Read an int that must be the whole text. QStringRef::toInt() will ignore
+ spaces happily; but ISO date format should not.
+*/
+ParsedInt readInt(QStringView text)
+{
+ ParsedInt result;
+ for (const auto &ch : text) {
+ if (ch.isSpace())
+ return result;
+ }
+ result.value = QLocale::c().toInt(text, &result.ok);
+ return result;
+}
+
+}
+
/*!
Returns the QDate represented by the \a string, using the
\a format given, or an invalid date if the string cannot be
@@ -1677,17 +1700,18 @@ QDate QDate::fromString(const QString &string, Qt::DateFormat format)
return QDate(year, month, day);
}
#endif // textdate
- case Qt::ISODate: {
- // Semi-strict parsing, must be long enough and have non-numeric separators
- if (string.size() < 10 || string.at(4).isDigit() || string.at(7).isDigit()
- || (string.size() > 10 && string.at(10).isDigit())) {
- return QDate();
- }
- const int year = string.midRef(0, 4).toInt();
- if (year <= 0 || year > 9999)
- return QDate();
- return QDate(year, string.midRef(5, 2).toInt(), string.midRef(8, 2).toInt());
+ case Qt::ISODate:
+ // Semi-strict parsing, must be long enough and have punctuators as separators
+ if (string.size() >= 10 && string.at(4).isPunct() && string.at(7).isPunct()
+ && (string.size() == 10 || !string.at(10).isDigit())) {
+ QStringView view(string);
+ const ParsedInt year = readInt(view.mid(0, 4));
+ const ParsedInt month = readInt(view.mid(5, 2));
+ const ParsedInt day = readInt(view.mid(8, 2));
+ if (year.ok && year.value > 0 && year.value <= 9999 && month.ok && day.ok)
+ return QDate(year.value, month.value, day.value);
}
+ break;
}
return QDate();
}
@@ -2331,17 +2355,15 @@ static QTime fromIsoTimeString(QStringView string, Qt::DateFormat format, bool *
*isMidnight24 = false;
const int size = string.size();
- if (size < 5)
+ if (size < 5 || string.at(2) != QLatin1Char(':'))
return QTime();
- const QLocale C(QLocale::c());
- bool ok = false;
- int hour = C.toInt(string.mid(0, 2), &ok);
- if (!ok)
- return QTime();
- const int minute = C.toInt(string.mid(3, 2), &ok);
- if (!ok)
+ ParsedInt hour = readInt(string.mid(0, 2));
+ ParsedInt minute = readInt(string.mid(3, 2));
+ if (!hour.ok || !minute.ok)
return QTime();
+ // FIXME: ISO 8601 allows [,.]\d+ after hour, just as it does after minute
+
int second = 0;
int msec = 0;
@@ -2361,44 +2383,56 @@ static QTime fromIsoTimeString(QStringView string, Qt::DateFormat format, bool *
// will then be rounded up AND clamped to 999.
const QStringView minuteFractionStr = string.mid(6, qMin(qsizetype(5), string.size() - 6));
- const long minuteFractionInt = C.toLong(minuteFractionStr, &ok);
- if (!ok)
+ const ParsedInt parsed = readInt(minuteFractionStr);
+ if (!parsed.ok)
return QTime();
- const float minuteFraction = double(minuteFractionInt) / (std::pow(double(10), minuteFractionStr.size()));
+ const float secondWithMs
+ = double(parsed.value) * 60 / (std::pow(double(10), minuteFractionStr.size()));
- const float secondWithMs = minuteFraction * 60;
- const float secondNoMs = std::floor(secondWithMs);
- const float secondFraction = secondWithMs - secondNoMs;
- second = secondNoMs;
+ second = std::floor(secondWithMs);
+ const float secondFraction = secondWithMs - second;
msec = qMin(qRound(secondFraction * 1000.0), 999);
- } else {
+ } else if (string.at(5) == QLatin1Char(':')) {
// HH:mm:ss or HH:mm:ss.zzz
- second = C.toInt(string.mid(6, qMin(qsizetype(2), string.size() - 6)), &ok);
- if (!ok)
+ const ParsedInt parsed = readInt(string.mid(6, qMin(qsizetype(2), string.size() - 6)));
+ if (!parsed.ok)
return QTime();
- if (size > 8 && (string.at(8) == QLatin1Char(',') || string.at(8) == QLatin1Char('.'))) {
+ second = parsed.value;
+ if (size <= 8) {
+ // No fractional part to read
+ } else if (string.at(8) == QLatin1Char(',') || string.at(8) == QLatin1Char('.')) {
QStringView msecStr(string.mid(9, qMin(qsizetype(4), string.size() - 9)));
- // toInt() ignores leading spaces, so catch them before calling it
+ bool ok = true;
+ // Can't use readInt() here, as we *do* allow trailing space - but not leading:
if (!msecStr.isEmpty() && !msecStr.at(0).isDigit())
return QTime();
- // We do, however, want to ignore *trailing* spaces.
msecStr = msecStr.trimmed();
- int msecInt = msecStr.isEmpty() ? 0 : C.toInt(msecStr, &ok);
+ int msecInt = msecStr.isEmpty() ? 0 : QLocale::c().toInt(msecStr, &ok);
if (!ok)
return QTime();
const double secondFraction(msecInt / (std::pow(double(10), msecStr.size())));
msec = qMin(qRound(secondFraction * 1000.0), 999);
+ } else {
+#if QT_VERSION >= QT_VERSION_CHECK(6,0,0) // behavior change
+ // Stray cruft after date-time: tolerate trailing space, but nothing else.
+ for (const auto &ch : string.mid(8)) {
+ if (!ch.isSpace())
+ return QTime();
+ }
+#endif
}
+ } else {
+ return QTime();
}
const bool isISODate = format == Qt::ISODate || format == Qt::ISODateWithMs;
- if (isISODate && hour == 24 && minute == 0 && second == 0 && msec == 0) {
+ if (isISODate && hour.value == 24 && minute.value == 0 && second == 0 && msec == 0) {
if (isMidnight24)
*isMidnight24 = true;
- hour = 0;
+ hour.value = 0;
}
- return QTime(hour, minute, second, msec);
+ return QTime(hour.value, minute.value, second, msec);
}
/*!
diff --git a/src/plugins/sqldrivers/README b/src/plugins/sqldrivers/README
index 7000fb5613..26418c5e36 100644
--- a/src/plugins/sqldrivers/README
+++ b/src/plugins/sqldrivers/README
@@ -1,5 +1,5 @@
-Please note that the DB2, Oracle and TDS client drivers are not distributed
-with the Qt Open Source Editions.
+Please note that the DB2, MySQL, Oracle and TDS client drivers are not
+distributed with the Qt Open Source Editions.
This is because the client libraries are distributed under a license which
is not compatible with the GPL license.
diff --git a/src/plugins/sqldrivers/psql/qsql_psql.cpp b/src/plugins/sqldrivers/psql/qsql_psql.cpp
index 3d8ed29b54..d735d29fc7 100644
--- a/src/plugins/sqldrivers/psql/qsql_psql.cpp
+++ b/src/plugins/sqldrivers/psql/qsql_psql.cpp
@@ -692,40 +692,24 @@ QVariant QPSQLResult::data(int i)
return dbl;
}
case QVariant::Date:
- if (val[0] == '\0') {
- return QVariant(QDate());
- } else {
#if QT_CONFIG(datestring)
- return QVariant(QDate::fromString(QString::fromLatin1(val), Qt::ISODate));
+ return QVariant(QDate::fromString(QString::fromLatin1(val), Qt::ISODate));
#else
- return QVariant(QString::fromLatin1(val));
+ return QVariant(QString::fromLatin1(val));
#endif
- }
- case QVariant::Time: {
- const QString str = QString::fromLatin1(val);
+ case QVariant::Time:
#if QT_CONFIG(datestring)
- if (str.isEmpty())
- return QVariant(QTime());
- else
- return QVariant(QTime::fromString(str, Qt::ISODate));
+ return QVariant(QTime::fromString(QString::fromLatin1(val), Qt::ISODate));
#else
- return QVariant(str);
+ return QVariant(QString::fromLatin1(val));
#endif
- }
- case QVariant::DateTime: {
- QString dtval = QString::fromLatin1(val);
+ case QVariant::DateTime:
#if QT_CONFIG(datestring)
- if (dtval.length() < 10) {
- return QVariant(QDateTime());
- } else {
- QChar sign = dtval[dtval.size() - 3];
- if (sign == QLatin1Char('-') || sign == QLatin1Char('+')) dtval += QLatin1String(":00");
- return QVariant(QDateTime::fromString(dtval, Qt::ISODate).toLocalTime());
- }
+ return QVariant(QDateTime::fromString(QString::fromLatin1(val),
+ Qt::ISODate).toLocalTime());
#else
- return QVariant(dtval);
+ return QVariant(QString::fromLatin1(val));
#endif
- }
case QVariant::ByteArray: {
size_t len;
unsigned char *data = PQunescapeBytea((const unsigned char*)val, &len);
diff --git a/src/sql/doc/snippets/code/doc_src_sql-driver.qdoc b/src/sql/doc/snippets/code/doc_src_sql-driver.qdoc
index 9709deeccb..12a39d80b2 100644
--- a/src/sql/doc/snippets/code/doc_src_sql-driver.qdoc
+++ b/src/sql/doc/snippets/code/doc_src_sql-driver.qdoc
@@ -70,7 +70,6 @@ BEGIN
END
//! [1]
-
//! [3]
cd $QTDIR/qtbase/src/plugins/sqldrivers
qmake -- MYSQL_PREFIX=/usr/local
@@ -86,14 +85,15 @@ make install
//! [5]
cd %QTDIR%\qtbase\src\plugins\sqldrivers
-qmake -- MYSQL_INCDIR=C:/MySQL/include "MYSQL_LIBDIR=C:/MYSQL/MySQL Server <version>/lib/opt"
+qmake -- MYSQL_INCDIR="C:/Program Files/MySQL/MySQL Connector C 6.1/include" MYSQL_LIBDIR="C:/Program Files/MySQL/MySQL Connector C 6.1/lib"
nmake sub-mysql
+nmake install
//! [5]
//! [6]
cd $QTDIR/qtbase/src/plugins/sqldrivers
-qmake -- "OCI_INCDIR=$ORACLE_HOME/rdbms/public" OCI_LIBDIR=$ORACLE_HOME/lib "OCI_LIBS=-lclntsh -lwtc9"
+qmake -- OCI_INCDIR="$ORACLE_HOME/rdbms/public" OCI_LIBDIR="$ORACLE_HOME/lib" OCI_LIBS="-lclntsh -lwtc9"
make sub-oci
//! [6]
@@ -142,6 +142,7 @@ make sub-psql
cd %QTDIR%\qtbase\src\plugins\sqldrivers
qmake -- PSQL_INCDIR=C:/psql/include PSQL_LIBDIR=C:/psql/lib/ms
nmake sub-psql
+nmake install
//! [15]
@@ -156,6 +157,7 @@ make sub-tds
cd %QTDIR%\qtbase\src\plugins\sqldrivers
qmake
nmake sub-tds
+nmake install
//! [17]
@@ -168,8 +170,9 @@ make sub-db2
//! [20]
cd %QTDIR%\qtbase\src\plugins\sqldrivers
-qmake -- "DB2_PREFIX=<DB2 home>/sqllib"
+qmake -- DB2_PREFIX="<DB2 home>/sqllib"
nmake sub-db2
+nmake install
//! [20]
@@ -184,6 +187,7 @@ make sub-sqlite
cd %QTDIR%\qtbase\src\plugins\sqldrivers
qmake -- -system-sqlite SQLITE3_PREFIX=C:/SQLITE
nmake sub-sqlite
+nmake install
//! [23]
@@ -205,6 +209,7 @@ make sub-ibase
cd %QTDIR%\qtbase\src\plugins\sqldrivers
qmake -- IBASE_INCDIR=C:/interbase/include
nmake sub-ibase
+nmake install
//! [29]
@@ -212,17 +217,18 @@ nmake sub-ibase
cd %QTDIR%\qtbase\src\plugins\sqldrivers
qmake -- IBASE_INCDIR=C:/interbase/include IBASE_LIBS=-lfbclient
nmake sub-ibase
+nmake install
//! [30]
//! [32]
-configure OCI_INCDIR=/usr/include/oracle/10.1.0.3/client OCI_LIBDIR=/usr/lib/oracle/10.1.0.3/client/lib -R /usr/lib/oracle/10.1.0.3/client/lib "OCI_LIBS=-lclntsh -lnnz10"
+configure OCI_INCDIR=/usr/include/oracle/10.1.0.3/client OCI_LIBDIR=/usr/lib/oracle/10.1.0.3/client/lib -R /usr/lib/oracle/10.1.0.3/client/lib OCI_LIBS="-lclntsh -lnnz10"
make
//! [32]
//! [33]
cd $QTDIR/qtbase/src/plugins/sqldrivers
-qmake -- OCI_INCDIR=/usr/include/oracle/10.1.0.3/client OCI_LIBDIR=/usr/lib/oracle/10.1.0.3/client/lib "OCI_LIBS=-Wl,-rpath,/usr/lib/oracle/10.1.0.3/client/lib -lclntsh -lnnz10"
+qmake -- OCI_INCDIR=/usr/include/oracle/10.1.0.3/client OCI_LIBDIR=/usr/lib/oracle/10.1.0.3/client/lib OCI_LIBS="-Wl,-rpath,/usr/lib/oracle/10.1.0.3/client/lib -lclntsh -lnnz10"
make sub-oci
//! [33]
@@ -250,3 +256,42 @@ q.exec(QString("CREATE TABLE %1 (id INTEGER)").arg(tableString));
// Call toLower() on the string so that it can be matched
QSqlRecord rec = database.record(tableString.toLower());
//! [40]
+
+//! [41]
+C:\Qt5\5.13.2\Src\qtbase\src\plugins\sqldrivers>qmake -version
+QMake version 3.1
+Using Qt version 5.13.2 in C:/Qt5/5.13.2/mingw73_64/lib
+C:\Qt5\5.13.2\Src\qtbase\src\plugins\sqldrivers>qmake -- MYSQL_INCDIR="C:/Program Files/MySQL/MySQL Connector C 6.1/include" MYSQL_LIBDIR="C:/Program Files/MySQL/MySQL Connector C 6.1/lib"
+Info: creating stash file C:\Qt5\5.13.2\Src\qtbase\src\plugins\sqldrivers\.qmake.stash
+
+Running configuration tests...
+Checking for DB2 (IBM)... no
+Checking for InterBase... no
+Checking for MySQL... yes
+Checking for OCI (Oracle)... no
+Checking for ODBC... yes
+Checking for PostgreSQL... no
+Checking for SQLite (version 2)... no
+Checking for TDS (Sybase)... no
+Done running configuration tests.
+
+Configure summary:
+
+Qt Sql Drivers:
+ DB2 (IBM) .............................. no
+ InterBase .............................. no
+ MySql .................................. yes
+ OCI (Oracle) ........................... no
+ ODBC ................................... yes
+ PostgreSQL ............................. no
+ SQLite2 ................................ no
+ SQLite ................................. yes
+ Using system provided SQLite ......... no
+ TDS (Sybase) ........................... no
+
+Qt is now configured for building. Just run 'mingw32-make'.
+Once everything is built, you must run 'mingw32-make install'.
+Qt will be installed into 'C:\Qt5\5.13.2\mingw73_64'.
+
+Prior to reconfiguration, make sure you remove any leftovers from the previous build.
+//! [41]
diff --git a/src/sql/doc/src/sql-driver.qdoc b/src/sql/doc/src/sql-driver.qdoc
index c6ac4d17ff..9c26c4089c 100644
--- a/src/sql/doc/src/sql-driver.qdoc
+++ b/src/sql/doc/src/sql-driver.qdoc
@@ -48,7 +48,7 @@
\header \li Driver name \li DBMS
\row \li \l{#QDB2}{QDB2} \li IBM DB2 (version 7.1 and above)
\row \li \l{#QIBASE}{QIBASE} \li Borland InterBase
- \row \li \l{#QMYSQL}{QMYSQL} \li MySQL
+ \row \li \l{#QMYSQL}{QMYSQL} \li MySQL (version 5.0 and above)
\row \li \l{#QOCI}{QOCI} \li Oracle Call Interface Driver
\row \li \l{#QODBC}{QODBC}
\li Open Database Connectivity (ODBC) - Microsoft SQL Server and other
@@ -70,7 +70,8 @@
access to the API exposed by the DBMS, and is typically shipped with it.
Most installation programs also allow you to install "development
libraries", and these are what you need. These libraries are responsible
- for the low-level communication with the DBMS.
+ for the low-level communication with the DBMS. Also make sure to install
+ the correct database libraries for your Qt architecture (32 or 64 bit).
\note When using Qt under Open Source terms but with a proprietary
database, verify the client library's license compatibility with
@@ -91,11 +92,21 @@
may be necessary to specify these paths using the \c *_INCDIR=,
\c *_LIBDIR=, or \c *_PREFIX= command-line options. For example,
if your MySQL files are installed in \c /usr/local/mysql (or in
- \c{C:\mysql} on Windows), then pass the following parameter to
- configure: \c MYSQL_PREFIX=/usr/local/mysql
- (or \c{MYSQL_PREFIX=C:\mysql} for Windows).
+ \c{C:/Program Files/MySQL/MySQL Connector C 6.1} on Windows), then pass the
+ following parameter to configure: \c MYSQL_PREFIX=/usr/local/mysql
+ (or \c{MYSQL_PREFIX="C:/Program Files/MySQL/MySQL Connector C 6.1"} for Windows).
The particulars for each driver are explained below.
+ If something goes wrong and you want qmake to recheck your
+ available drivers, you must remove \e{config.cache} in
+ \e{<QTDIR>/qtbase/src/plugins/sqldrivers} - otherwise qmake will not
+ search for the available drivers again. If you encounter an error during
+ the qmake stage, open \e{config.log} to see what went wrong.
+
+ A typical qmake run (in this case to configure for MySQL) looks like this:
+
+ \snippet code/doc_src_sql-driver.qdoc 41
+
Due to the practicalities of dealing with external dependencies,
only the SQLite3 plugin is shipped with binary builds of Qt.
To be able to add additional drivers to the Qt installation
@@ -112,11 +123,11 @@
\section1 Driver Specifics
\target QMYSQL
- \section2 QMYSQL for MySQL 4 and higher
+ \section2 QMYSQL for MySQL 5 and higher
\section3 QMYSQL Stored Procedure Support
- MySQL 5 introduces stored procedure support at the SQL level, but no
+ MySQL 5 has stored procedure support at the SQL level, but no
API to control IN, OUT, and INOUT parameters. Therefore, parameters
have to be set and read using SQL commands instead of QSqlQuery::bindValue().
@@ -159,16 +170,32 @@
\section3 How to Build the QMYSQL Plugin on Windows
- You need to get the MySQL installation files. Run \c SETUP.EXE and
- choose "Custom Install". Install the "Libs & Include Files" Module.
- Build the plugin as follows (here it is assumed that MySQL is
- installed in \c{C:\MySQL}):
+ You need to get the MySQL installation files (e.g.
+ \e{mysql-installer-web-community-8.0.18.0.msi}). Run the installer,
+ select custom installation and install the MySQL C Connector
+ which matches your Qt installation (x86 or x64).
+ After installation make sure that the needed files are there:
+ \list
+ \li \c {<MySQL dir>/lib/libmysql.lib}
+ \li \c {<MySQL dir>/lib/libmysql.dll}
+ \li \c {<MySQL dir>/include/mysql.h}
+ \endlist
+
+ Build the plugin as follows (here it is assumed that the MySQL
+ C Connector is installed in
+ \c{C:/Program Files/MySQL/MySQL Connector C 6.1}):
\snippet code/doc_src_sql-driver.qdoc 5
If you are not using a Microsoft compiler, replace \c nmake with \c
mingw32-make in the line above.
+ When you distribute your application, remember to include libmysql.dll
+ in your installation package. It must be placed in the same folder
+ as the application executable. libmysql.dll additionally needs the
+ MSVC runtime libraries which can be installed with vcredist.exe
+ (\l {https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads}(vcredist.exe)
+
\target QOCI
\section2 QOCI for the Oracle Call Interface (OCI)
@@ -398,11 +425,6 @@
\snippet code/doc_src_sql-driver.qdoc 40
- \section3 QPSQL BLOB Support
-
- Binary Large Objects are supported through the \c BYTEA field type in
- PostgreSQL server versions >= 7.1.
-
\section3 QPSQL Forward-only query support
To use forward-only queries, you must build the QPSQL plugin with
@@ -463,6 +485,10 @@
Users of MinGW may wish to consult the following online document:
\l{http://www.postgresql.org/docs/current/static/installation-platform-notes.html#INSTALLATION-NOTES-MINGW}{PostgreSQL MinGW/Native Windows}.
+ When you distribute your application, remember to include libpq.dll
+ in your installation package. It must be placed in the same folder
+ as the application executable.
+
\target QTDS
\section2 QTDS for Sybase Adaptive Server
diff --git a/src/testlib/qtest.h b/src/testlib/qtest.h
index 27fe08e8f4..3b1ffb389e 100644
--- a/src/testlib/qtest.h
+++ b/src/testlib/qtest.h
@@ -95,7 +95,7 @@ template<> inline char *toString(const QByteArray &ba)
template<> inline char *toString(const QBitArray &ba)
{
qsizetype size = ba.size();
- char *str = static_cast<char *>(malloc(size + 1));
+ char *str = new char[size + 1];
for (qsizetype i = 0; i < size; ++i)
str[i] = "01"[ba.testBit(i)];
str[size] = '\0';
diff --git a/src/tools/androiddeployqt/main.cpp b/src/tools/androiddeployqt/main.cpp
index 550ed0832f..6fd32c2d29 100644
--- a/src/tools/androiddeployqt/main.cpp
+++ b/src/tools/androiddeployqt/main.cpp
@@ -1717,17 +1717,18 @@ bool scanImports(Options *options, QSet<QString> *usedDependencies)
qmlImportScanner += QLatin1String(" -qrcFiles");
for (const QString &qrcFile : options->qrcFiles)
qmlImportScanner += QLatin1Char(' ') + shellQuote(qrcFile);
- } else {
- if (rootPath.isEmpty())
- rootPath = QFileInfo(options->inputFileName).absolutePath();
- else
- rootPath = QFileInfo(rootPath).absoluteFilePath();
-
- if (!rootPath.endsWith(QLatin1Char('/')))
- rootPath += QLatin1Char('/');
- qmlImportScanner += QLatin1String(" -rootPath %1").arg(shellQuote(rootPath));
}
+ if (rootPath.isEmpty())
+ rootPath = QFileInfo(options->inputFileName).absolutePath();
+ else
+ rootPath = QFileInfo(rootPath).absoluteFilePath();
+
+ if (!rootPath.endsWith(QLatin1Char('/')))
+ rootPath += QLatin1Char('/');
+
+ qmlImportScanner += QLatin1String(" -rootPath %1").arg(shellQuote(rootPath));
+
QStringList importPaths;
importPaths += shellQuote(options->qtInstallDirectory + QLatin1String("/qml"));
if (!rootPath.isEmpty())
diff --git a/src/tools/rcc/rcc.cpp b/src/tools/rcc/rcc.cpp
index 9acbce25ff..7185219d34 100644
--- a/src/tools/rcc/rcc.cpp
+++ b/src/tools/rcc/rcc.cpp
@@ -1478,13 +1478,19 @@ bool RCCResourceLibrary::writeInitializer()
writeString(" return 1;\n");
writeString("}\n\n");
- writeByteArray(
- "namespace {\n"
- " struct initializer {\n"
- " initializer() { QT_RCC_MANGLE_NAMESPACE(" + initResources + ")(); }\n"
- " ~initializer() { QT_RCC_MANGLE_NAMESPACE(" + cleanResources + ")(); }\n"
- " } dummy;\n"
- "}\n");
+
+ writeString("namespace {\n"
+ " struct initializer {\n");
+
+ if (m_useNameSpace) {
+ writeByteArray(" initializer() { QT_RCC_MANGLE_NAMESPACE(" + initResources + ")(); }\n"
+ " ~initializer() { QT_RCC_MANGLE_NAMESPACE(" + cleanResources + ")(); }\n");
+ } else {
+ writeByteArray(" initializer() { " + initResources + "(); }\n"
+ " ~initializer() { " + cleanResources + "(); }\n");
+ }
+ writeString(" } dummy;\n"
+ "}\n");
} else if (m_format == Binary) {
int i = 4;
diff --git a/src/widgets/doc/snippets/code/src_gui_dialogs_qfiledialog.cpp b/src/widgets/doc/snippets/code/src_gui_dialogs_qfiledialog.cpp
index 7ccd827a04..4b4fb869f9 100644
--- a/src/widgets/doc/snippets/code/src_gui_dialogs_qfiledialog.cpp
+++ b/src/widgets/doc/snippets/code/src_gui_dialogs_qfiledialog.cpp
@@ -146,13 +146,13 @@ dialog.exec();
//! [14]
//! [15]
-auto fileOpenCompleted = [](const QString &fileName, const QByteArray &fileContent) {
+auto fileContentReady = [](const QString &fileName, const QByteArray &fileContent) {
if (fileName.isEmpty()) {
// No file was selected
} else {
// Use fileName and fileContent
}
-}
+};
QFileDialog::getOpenFileContent("Images (*.png *.xpm *.jpg)", fileContentReady);
//! [15]
diff --git a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp
index edb15a8db2..e719871128 100644
--- a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp
+++ b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp
@@ -31,6 +31,7 @@
#include <QtTest/QtTest>
#include <qvariant.h>
+#include <QtCore/private/qvariant_p.h>
#include <qbitarray.h>
#include <qbytearraylist.h>
#include <qdatetime.h>
@@ -276,7 +277,8 @@ private slots:
void nullConvert();
void accessSequentialContainerKey();
-
+ void shouldDeleteVariantDataWorksForSequential();
+ void shouldDeleteVariantDataWorksForAssociative();
void fromStdVariant();
void qt4UuidDataStream();
@@ -4990,6 +4992,99 @@ void tst_QVariant::accessSequentialContainerKey()
QCOMPARE(nameResult, QStringLiteral("Seven"));
}
+void tst_QVariant::shouldDeleteVariantDataWorksForSequential()
+{
+ QCOMPARE(instanceCount, 0);
+ {
+ QtMetaTypePrivate::QSequentialIterableImpl iterator {};
+ iterator._iteratorCapabilities = QtMetaTypePrivate::RandomAccessCapability |
+ QtMetaTypePrivate::BiDirectionalCapability |
+ QtMetaTypePrivate::ForwardCapability;
+ iterator._metaType_flags = QVariantConstructionFlags::ShouldDeleteVariantData;
+
+ iterator._size = [](const void *) {return 1;};
+ iterator._metaType_id = qMetaTypeId<MyType>();
+ iterator._moveToBegin = [](const void *, void **) {};
+ iterator._moveToEnd = [](const void *, void **) {};
+ iterator._advance = [](void **, int) {};
+ iterator._destroyIter = [](void **){};
+ iterator._equalIter = [](void * const *, void * const *){return true; /*all iterators are nullptr*/};
+ iterator._destroyIter = [](void **){};
+ iterator._at = [](const void *, int ) -> void const * {
+ MyType mytype {1, "eins"};
+ return QMetaType::create(qMetaTypeId<MyType>(), &mytype);
+ };
+ iterator._get = [](void * const *, int, uint) -> QtMetaTypePrivate::VariantData {
+ MyType mytype {2, "zwei"};
+ return {qMetaTypeId<MyType>(), QMetaType::create(qMetaTypeId<MyType>(), &mytype), QVariantConstructionFlags::ShouldDeleteVariantData};
+ };
+ QSequentialIterable iterable {iterator};
+ QVariant value1 = iterable.at(0);
+ QVERIFY(value1.canConvert<MyType>());
+ QCOMPARE(value1.value<MyType>().number, 1);
+ QVariant value2 = *iterable.begin();
+ QVERIFY(value2.canConvert<MyType>());
+ QCOMPARE(value2.value<MyType>().number, 2);
+ }
+ QCOMPARE(instanceCount, 0);
+}
+
+void tst_QVariant::shouldDeleteVariantDataWorksForAssociative()
+{
+ QCOMPARE(instanceCount, 0);
+ {
+ QtMetaTypePrivate::QAssociativeIterableImpl iterator {};
+ iterator._metaType_flags_key = QVariantConstructionFlags::ShouldDeleteVariantData;
+ iterator._metaType_flags_value = QVariantConstructionFlags::ShouldDeleteVariantData;
+
+ iterator._size = [](const void *) {return 1;};
+ iterator._metaType_id_value = qMetaTypeId<MyType>();
+ iterator._metaType_id_key = qMetaTypeId<MyType>();
+ iterator._begin = [](const void *, void **) {};
+ iterator._end = [](const void *, void **) {};
+ iterator._advance = [](void **, int) {};
+ iterator._destroyIter = [](void **){};
+ iterator._equalIter = [](void * const *, void * const *){return true; /*all iterators are nullptr*/};
+ iterator._destroyIter = [](void **){};
+ iterator._find = [](const void *, const void *, void **iterator ) -> void {
+ (*iterator) = reinterpret_cast<void *>(quintptr(42));
+ };
+ iterator._getKey = [](void * const *iterator, int, uint) -> QtMetaTypePrivate::VariantData {
+ MyType mytype {1, "key"};
+ if (reinterpret_cast<quintptr>(*iterator) == 42) {
+ mytype.number = 42;
+ mytype.text = "find_key";
+ }
+ return {qMetaTypeId<MyType>(), QMetaType::create(qMetaTypeId<MyType>(), &mytype), QVariantConstructionFlags::ShouldDeleteVariantData};
+ };
+ iterator._getValue = [](void * const *iterator, int, uint) -> QtMetaTypePrivate::VariantData {
+ MyType mytype {2, "value"};
+ if (reinterpret_cast<quintptr>(*iterator) == 42) {
+ mytype.number = 42;
+ mytype.text = "find_value";
+ }
+ return {qMetaTypeId<MyType>(), QMetaType::create(qMetaTypeId<MyType>(), &mytype), QVariantConstructionFlags::ShouldDeleteVariantData};
+ };
+ QAssociativeIterable iterable {iterator};
+ auto it = iterable.begin();
+ QVariant value1 = it.key();
+ QVERIFY(value1.canConvert<MyType>());
+ QCOMPARE(value1.value<MyType>().number, 1);
+ QCOMPARE(value1.value<MyType>().text, "key");
+ QVariant value2 = it.value();
+ QVERIFY(value2.canConvert<MyType>());
+ QCOMPARE(value2.value<MyType>().number, 2);
+ auto findIt = iterable.find(QVariant::fromValue(MyType {}));
+ value1 = findIt.key();
+ QCOMPARE(value1.value<MyType>().number, 42);
+ QCOMPARE(value1.value<MyType>().text, "find_key");
+ value2 = findIt.value();
+ QCOMPARE(value2.value<MyType>().number, 42);
+ QCOMPARE(value2.value<MyType>().text, "find_value");
+ }
+ QCOMPARE(instanceCount, 0);
+}
+
void tst_QVariant::fromStdVariant()
{
#if __has_include(<variant>) && __cplusplus >= 201703L
diff --git a/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp b/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp
index 7778542736..c03d112560 100644
--- a/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp
+++ b/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp
@@ -2213,8 +2213,46 @@ void tst_QDateTime::fromStringDateFormat_data()
QTest::newRow("trailing space") // QTBUG-80445
<< QString("2000-01-02 03:04:05.678 ")
<< Qt::ISODate << QDateTime(QDate(2000, 1, 2), QTime(3, 4, 5, 678));
+
+ // Invalid spaces (but keeping field widths correct):
QTest::newRow("space before millis")
<< QString("2000-01-02 03:04:05. 678") << Qt::ISODate << QDateTime();
+ QTest::newRow("space after seconds")
+ << QString("2000-01-02 03:04:5 .678") << Qt::ISODate << QDateTime();
+ QTest::newRow("space before seconds")
+ << QString("2000-01-02 03:04: 5.678") << Qt::ISODate << QDateTime();
+ QTest::newRow("space after minutes")
+ << QString("2000-01-02 03:4 :05.678") << Qt::ISODate << QDateTime();
+ QTest::newRow("space before minutes")
+ << QString("2000-01-02 03: 4:05.678") << Qt::ISODate << QDateTime();
+ QTest::newRow("space after hour")
+ << QString("2000-01-02 3 :04:05.678") << Qt::ISODate << QDateTime();
+ QTest::newRow("space before hour")
+ << QString("2000-01-02 3:04:05.678") << Qt::ISODate << QDateTime();
+ QTest::newRow("space after day")
+ << QString("2000-01-2 03:04:05.678") << Qt::ISODate << QDateTime();
+ QTest::newRow("space before day")
+ << QString("2000-01- 2 03:04:05.678") << Qt::ISODate << QDateTime();
+ QTest::newRow("space after month")
+ << QString("2000-1 -02 03:04:05.678") << Qt::ISODate << QDateTime();
+ QTest::newRow("space before month")
+ << QString("2000- 1-02 03:04:05.678") << Qt::ISODate << QDateTime();
+ QTest::newRow("space after year")
+ << QString("200 -01-02 03:04:05.678") << Qt::ISODate << QDateTime();
+
+ // Spaces as separators:
+ QTest::newRow("sec-milli space")
+ << QString("2000-01-02 03:04:05 678") << Qt::ISODate
+ // Should be invalid, but we ignore trailing cruft (in some cases)
+ << QDateTime(QDate(2000, 1, 2), QTime(3, 4, 5));
+ QTest::newRow("min-sec space")
+ << QString("2000-01-02 03:04 05.678") << Qt::ISODate << QDateTime();
+ QTest::newRow("hour-min space")
+ << QString("2000-01-02 03 04:05.678") << Qt::ISODate << QDateTime();
+ QTest::newRow("mon-day space")
+ << QString("2000-01 02 03:04:05.678") << Qt::ISODate << QDateTime();
+ QTest::newRow("year-mon space")
+ << QString("2000 01-02 03:04:05.678") << Qt::ISODate << QDateTime();
// Normal usage:
QTest::newRow("ISO +01:00") << QString::fromLatin1("1987-02-13T13:24:51+01:00")