Merge remote-tracking branch 'origin/5.14' into 5.15

Change-Id: I69238f23882deebeaad46e4fdcf899ab22cc2b8f
This commit is contained in:
Qt Forward Merge Bot 2019-12-12 01:01:03 +01:00
commit 11d7788c18
22 changed files with 460 additions and 141 deletions

View File

@ -1481,6 +1481,10 @@ Configure with '-qreal float' to create a build that is binary-compatible with 5
"type": "error", "type": "error",
"condition": "!features.stl", "condition": "!features.stl",
"message": "Qt requires a compliant STL library." "message": "Qt requires a compliant STL library."
},
{
"type": "emccVersion",
"condition": "config.wasm"
} }
], ],

View File

@ -1226,6 +1226,12 @@ defineReplace(qtConfOutputPostProcess_publicPro) {
"QT_RELEASE_DATE = $$config.input.qt_release_date" "QT_RELEASE_DATE = $$config.input.qt_release_date"
} }
wasm: {
qt_emcc_version = $$qtSystemEmccVersion()
output += \
"QT_EMCC_VERSION = $$qt_emcc_version"
}
return($$output) return($$output)
} }
@ -1258,6 +1264,12 @@ defineReplace(qtConfOutputPostProcess_publicHeader) {
!isEmpty(config.input.qt_libinfix): \ !isEmpty(config.input.qt_libinfix): \
output += "$${LITERAL_HASH}define QT_LIBINFIX \"$$eval(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) return($$output)
} }
@ -1340,6 +1352,14 @@ defineTest(qtConfReport_buildMode) {
qtConfReportPadded($$1, $$build_mode) 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 # ensure pristine environment for configuration
discard_from($$[QT_HOST_DATA/get]/mkspecs/qconfig.pri) discard_from($$[QT_HOST_DATA/get]/mkspecs/qconfig.pri)
discard_from($$[QT_HOST_DATA/get]/mkspecs/qmodule.pri) discard_from($$[QT_HOST_DATA/get]/mkspecs/qmodule.pri)

View File

@ -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, -root):!isEmpty(QMAKE_RESOURCE_ROOT):QMAKE_RESOURCE_FLAGS += -root $$QMAKE_RESOURCE_ROOT
!contains(QMAKE_RESOURCE_FLAGS, -name): QMAKE_RESOURCE_FLAGS += -name ${QMAKE_FILE_BASE} !contains(QMAKE_RESOURCE_FLAGS, -name): QMAKE_RESOURCE_FLAGS += -name ${QMAKE_FILE_BASE}
# http://www.w3.org/TR/xml/#syntax
defineReplace(xml_escape) {
1 ~= s,&,&,
1 ~= s,\',',
1 ~= s,\",",
1 ~= s,<,&lt;,
1 ~= s,>,&gt;,
return($$1)
}
load(resources_functions) load(resources_functions)
qtFlattenResources() qtFlattenResources()

View File

@ -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) { 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) { for(resource, RESOURCES) {
# Regular case of user qrc file # Regular case of user qrc file
contains(resource, ".*\\.qrc$"): \ contains(resource, ".*\\.qrc$"): \
@ -7,10 +23,10 @@ defineTest(qtFlattenResources) {
# Fallback for stand-alone files/directories # Fallback for stand-alone files/directories
!defined($${resource}.files, var) { !defined($${resource}.files, var) {
!equals(resource, qmake_immediate) { !equals(resource, $$immediate) {
!exists($$absolute_path($$resource, $$_PRO_FILE_PWD_)): \ !exists($$absolute_path($$resource, $$_PRO_FILE_PWD_)): \
warning("Failure to find: $$resource") warning("Failure to find: $$resource")
qmake_immediate.files += $$resource $${immediate}.files += $$resource
OTHER_FILES *= $$resource OTHER_FILES *= $$resource
} }
RESOURCES -= $$resource RESOURCES -= $$resource
@ -56,8 +72,9 @@ defineTest(qtFlattenResources) {
RESOURCES -= $$resource RESOURCES -= $$resource
RESOURCES += $$resource_file RESOURCES += $$resource_file
} }
export(QMAKE_RESOURCES_IMMEDIATE_NR)
export(RESOURCES) export(RESOURCES)
export(OTHER_FILES) export(OTHER_FILES)
export(qmake_immediate.files) export($${immediate}.files)
return(true) return(true)
} }

View File

@ -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.")
}
}
}

View File

@ -1,8 +1,11 @@
# DESTDIR will be empty if not set in the app .pro file; make sure it has a value # DESTDIR will be empty if not set in the app .pro file; make sure it has a value
isEmpty(DESTDIR): DESTDIR = $$OUT_PWD isEmpty(DESTDIR): DESTDIR = $$OUT_PWD
exists($$QMAKE_QT_CONFIG) { exists($$QMAKE_QT_CONFIG) {
## this may be subject to change
qtConfig(thread) { qtConfig(thread) {
EMCC_THREAD_LFLAGS += -s USE_PTHREADS=1 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 # Pass --source-map-base on the linker line. This informs the
# browser where to find the source files when debugging. # browser where to find the source files when debugging.
WASM_SOURCE_MAP_BASE = http://localhost:8000/ WASM_SOURCE_MAP_BASE = http://localhost:8000/

View File

@ -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)

View File

@ -131679,6 +131679,7 @@ SQLITE_PRIVATE int sqlite3Select(
*/ */
if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct
&& sqlite3ExprListCompare(sSort.pOrderBy, pEList, -1)==0 && sqlite3ExprListCompare(sSort.pOrderBy, pEList, -1)==0
&& p->pWin==0
){ ){
p->selFlags &= ~SF_Distinct; p->selFlags &= ~SF_Distinct;
pGroupBy = p->pGroupBy = sqlite3ExprListDup(db, pEList, 0); pGroupBy = p->pGroupBy = sqlite3ExprListDup(db, pEList, 0);

View File

@ -84,10 +84,10 @@ QT_BEGIN_NAMESPACE
must start and stop the timer in its thread; it is not possible to must start and stop the timer in its thread; it is not possible to
start a timer from another thread. start a timer from another thread.
As a special case, a QTimer with a timeout of 0 will time out as As a special case, a QTimer with a timeout of 0 will time out as soon as
soon as all the events in the window system's event queue have possible, though the ordering between zero timers and other sources of
been processed. This can be used to do heavy work while providing events is unspecified. Zero timers can be used to do some work while still
a snappy user interface: providing a snappy user interface:
\snippet timers/timers.cpp 4 \snippet timers/timers.cpp 4
\snippet timers/timers.cpp 5 \snippet timers/timers.cpp 5

View File

@ -4534,15 +4534,24 @@ QSequentialIterable::const_iterator QSequentialIterable::end() const
return it; 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. Returns the element at position \a idx in the container.
*/ */
QVariant QSequentialIterable::at(int idx) const QVariant QSequentialIterable::at(int idx) const
{ {
const QtMetaTypePrivate::VariantData d = m_impl.at(idx); const QtMetaTypePrivate::VariantData d = m_impl.at(idx);
if (d.metaTypeId == qMetaTypeId<QVariant>()) return variantFromVariantDataHelper(d);
return *reinterpret_cast<const QVariant*>(d.data);
return QVariant(d.metaTypeId, d.data, d.flags);
} }
/*! /*!
@ -4619,9 +4628,7 @@ QSequentialIterable::const_iterator::operator=(const const_iterator &other)
const QVariant QSequentialIterable::const_iterator::operator*() const const QVariant QSequentialIterable::const_iterator::operator*() const
{ {
const QtMetaTypePrivate::VariantData d = m_impl.getCurrent(); const QtMetaTypePrivate::VariantData d = m_impl.getCurrent();
if (d.metaTypeId == qMetaTypeId<QVariant>()) return variantFromVariantDataHelper(d);
return *reinterpret_cast<const QVariant*>(d.data);
return QVariant(d.metaTypeId, d.data, d.flags);
} }
/*! /*!
@ -4953,10 +4960,7 @@ QAssociativeIterable::const_iterator::operator=(const const_iterator &other)
const QVariant QAssociativeIterable::const_iterator::operator*() const const QVariant QAssociativeIterable::const_iterator::operator*() const
{ {
const QtMetaTypePrivate::VariantData d = m_impl.getCurrentValue(); const QtMetaTypePrivate::VariantData d = m_impl.getCurrentValue();
QVariant v(d.metaTypeId, d.data, d.flags); return variantFromVariantDataHelper(d);
if (d.metaTypeId == qMetaTypeId<QVariant>())
return *reinterpret_cast<const QVariant*>(d.data);
return v;
} }
/*! /*!
@ -4965,10 +4969,7 @@ const QVariant QAssociativeIterable::const_iterator::operator*() const
const QVariant QAssociativeIterable::const_iterator::key() const const QVariant QAssociativeIterable::const_iterator::key() const
{ {
const QtMetaTypePrivate::VariantData d = m_impl.getCurrentKey(); const QtMetaTypePrivate::VariantData d = m_impl.getCurrentKey();
QVariant v(d.metaTypeId, d.data, d.flags); return variantFromVariantDataHelper(d);
if (d.metaTypeId == qMetaTypeId<QVariant>())
return *reinterpret_cast<const QVariant*>(d.data);
return v;
} }
/*! /*!
@ -4976,11 +4977,7 @@ const QVariant QAssociativeIterable::const_iterator::key() const
*/ */
const QVariant QAssociativeIterable::const_iterator::value() const const QVariant QAssociativeIterable::const_iterator::value() const
{ {
const QtMetaTypePrivate::VariantData d = m_impl.getCurrentValue(); return operator*();
QVariant v(d.metaTypeId, d.data, d.flags);
if (d.metaTypeId == qMetaTypeId<QVariant>())
return *reinterpret_cast<const QVariant*>(d.data);
return v;
} }
/*! /*!

View File

@ -105,6 +105,11 @@ inline T *v_cast(QVariant::Private *d, T * = nullptr)
#endif #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 //a simple template that avoids to allocate 2 memory chunks when creating a QVariant
template <class T> class QVariantPrivateSharedEx : public QVariant::PrivateShared template <class T> class QVariantPrivateSharedEx : public QVariant::PrivateShared

View File

@ -1626,6 +1626,29 @@ qint64 QDate::daysTo(const QDate &d) const
*/ */
#if QT_CONFIG(datestring) #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 Returns the QDate represented by the \a string, using the
\a format given, or an invalid date if the string cannot be \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); return QDate(year, month, day);
} }
#endif // textdate #endif // textdate
case Qt::ISODate: { case Qt::ISODate:
// Semi-strict parsing, must be long enough and have non-numeric separators // Semi-strict parsing, must be long enough and have punctuators as separators
if (string.size() < 10 || string.at(4).isDigit() || string.at(7).isDigit() if (string.size() >= 10 && string.at(4).isPunct() && string.at(7).isPunct()
|| (string.size() > 10 && string.at(10).isDigit())) { && (string.size() == 10 || !string.at(10).isDigit())) {
return QDate(); QStringView view(string);
} const ParsedInt year = readInt(view.mid(0, 4));
const int year = string.midRef(0, 4).toInt(); const ParsedInt month = readInt(view.mid(5, 2));
if (year <= 0 || year > 9999) const ParsedInt day = readInt(view.mid(8, 2));
return QDate(); if (year.ok && year.value > 0 && year.value <= 9999 && month.ok && day.ok)
return QDate(year, string.midRef(5, 2).toInt(), string.midRef(8, 2).toInt()); return QDate(year.value, month.value, day.value);
} }
break;
} }
return QDate(); return QDate();
} }
@ -2331,17 +2355,15 @@ static QTime fromIsoTimeString(QStringView string, Qt::DateFormat format, bool *
*isMidnight24 = false; *isMidnight24 = false;
const int size = string.size(); const int size = string.size();
if (size < 5) if (size < 5 || string.at(2) != QLatin1Char(':'))
return QTime(); return QTime();
const QLocale C(QLocale::c()); ParsedInt hour = readInt(string.mid(0, 2));
bool ok = false; ParsedInt minute = readInt(string.mid(3, 2));
int hour = C.toInt(string.mid(0, 2), &ok); if (!hour.ok || !minute.ok)
if (!ok)
return QTime();
const int minute = C.toInt(string.mid(3, 2), &ok);
if (!ok)
return QTime(); return QTime();
// FIXME: ISO 8601 allows [,.]\d+ after hour, just as it does after minute
int second = 0; int second = 0;
int msec = 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. // will then be rounded up AND clamped to 999.
const QStringView minuteFractionStr = string.mid(6, qMin(qsizetype(5), string.size() - 6)); const QStringView minuteFractionStr = string.mid(6, qMin(qsizetype(5), string.size() - 6));
const long minuteFractionInt = C.toLong(minuteFractionStr, &ok); const ParsedInt parsed = readInt(minuteFractionStr);
if (!ok) if (!parsed.ok)
return QTime(); 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; second = std::floor(secondWithMs);
const float secondNoMs = std::floor(secondWithMs); const float secondFraction = secondWithMs - second;
const float secondFraction = secondWithMs - secondNoMs;
second = secondNoMs;
msec = qMin(qRound(secondFraction * 1000.0), 999); msec = qMin(qRound(secondFraction * 1000.0), 999);
} else { } else if (string.at(5) == QLatin1Char(':')) {
// HH:mm:ss or HH:mm:ss.zzz // HH:mm:ss or HH:mm:ss.zzz
second = C.toInt(string.mid(6, qMin(qsizetype(2), string.size() - 6)), &ok); const ParsedInt parsed = readInt(string.mid(6, qMin(qsizetype(2), string.size() - 6)));
if (!ok) if (!parsed.ok)
return QTime(); 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))); 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()) if (!msecStr.isEmpty() && !msecStr.at(0).isDigit())
return QTime(); return QTime();
// We do, however, want to ignore *trailing* spaces.
msecStr = msecStr.trimmed(); msecStr = msecStr.trimmed();
int msecInt = msecStr.isEmpty() ? 0 : C.toInt(msecStr, &ok); int msecInt = msecStr.isEmpty() ? 0 : QLocale::c().toInt(msecStr, &ok);
if (!ok) if (!ok)
return QTime(); return QTime();
const double secondFraction(msecInt / (std::pow(double(10), msecStr.size()))); const double secondFraction(msecInt / (std::pow(double(10), msecStr.size())));
msec = qMin(qRound(secondFraction * 1000.0), 999); 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; 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) if (isMidnight24)
*isMidnight24 = true; *isMidnight24 = true;
hour = 0; hour.value = 0;
} }
return QTime(hour, minute, second, msec); return QTime(hour.value, minute.value, second, msec);
} }
/*! /*!

View File

@ -1,5 +1,5 @@
Please note that the DB2, Oracle and TDS client drivers are not distributed Please note that the DB2, MySQL, Oracle and TDS client drivers are not
with the Qt Open Source Editions. distributed with the Qt Open Source Editions.
This is because the client libraries are distributed under a license which This is because the client libraries are distributed under a license which
is not compatible with the GPL license. is not compatible with the GPL license.

View File

@ -692,40 +692,24 @@ QVariant QPSQLResult::data(int i)
return dbl; return dbl;
} }
case QVariant::Date: case QVariant::Date:
if (val[0] == '\0') {
return QVariant(QDate());
} else {
#if QT_CONFIG(datestring) #if QT_CONFIG(datestring)
return QVariant(QDate::fromString(QString::fromLatin1(val), Qt::ISODate)); return QVariant(QDate::fromString(QString::fromLatin1(val), Qt::ISODate));
#else #else
return QVariant(QString::fromLatin1(val)); return QVariant(QString::fromLatin1(val));
#endif #endif
} case QVariant::Time:
case QVariant::Time: {
const QString str = QString::fromLatin1(val);
#if QT_CONFIG(datestring) #if QT_CONFIG(datestring)
if (str.isEmpty()) return QVariant(QTime::fromString(QString::fromLatin1(val), Qt::ISODate));
return QVariant(QTime());
else
return QVariant(QTime::fromString(str, Qt::ISODate));
#else #else
return QVariant(str); return QVariant(QString::fromLatin1(val));
#endif #endif
} case QVariant::DateTime:
case QVariant::DateTime: {
QString dtval = QString::fromLatin1(val);
#if QT_CONFIG(datestring) #if QT_CONFIG(datestring)
if (dtval.length() < 10) { return QVariant(QDateTime::fromString(QString::fromLatin1(val),
return QVariant(QDateTime()); Qt::ISODate).toLocalTime());
} else {
QChar sign = dtval[dtval.size() - 3];
if (sign == QLatin1Char('-') || sign == QLatin1Char('+')) dtval += QLatin1String(":00");
return QVariant(QDateTime::fromString(dtval, Qt::ISODate).toLocalTime());
}
#else #else
return QVariant(dtval); return QVariant(QString::fromLatin1(val));
#endif #endif
}
case QVariant::ByteArray: { case QVariant::ByteArray: {
size_t len; size_t len;
unsigned char *data = PQunescapeBytea((const unsigned char*)val, &len); unsigned char *data = PQunescapeBytea((const unsigned char*)val, &len);

View File

@ -70,7 +70,6 @@ BEGIN
END END
//! [1] //! [1]
//! [3] //! [3]
cd $QTDIR/qtbase/src/plugins/sqldrivers cd $QTDIR/qtbase/src/plugins/sqldrivers
qmake -- MYSQL_PREFIX=/usr/local qmake -- MYSQL_PREFIX=/usr/local
@ -86,14 +85,15 @@ make install
//! [5] //! [5]
cd %QTDIR%\qtbase\src\plugins\sqldrivers 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 sub-mysql
nmake install
//! [5] //! [5]
//! [6] //! [6]
cd $QTDIR/qtbase/src/plugins/sqldrivers 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 make sub-oci
//! [6] //! [6]
@ -142,6 +142,7 @@ make sub-psql
cd %QTDIR%\qtbase\src\plugins\sqldrivers cd %QTDIR%\qtbase\src\plugins\sqldrivers
qmake -- PSQL_INCDIR=C:/psql/include PSQL_LIBDIR=C:/psql/lib/ms qmake -- PSQL_INCDIR=C:/psql/include PSQL_LIBDIR=C:/psql/lib/ms
nmake sub-psql nmake sub-psql
nmake install
//! [15] //! [15]
@ -156,6 +157,7 @@ make sub-tds
cd %QTDIR%\qtbase\src\plugins\sqldrivers cd %QTDIR%\qtbase\src\plugins\sqldrivers
qmake qmake
nmake sub-tds nmake sub-tds
nmake install
//! [17] //! [17]
@ -168,8 +170,9 @@ make sub-db2
//! [20] //! [20]
cd %QTDIR%\qtbase\src\plugins\sqldrivers cd %QTDIR%\qtbase\src\plugins\sqldrivers
qmake -- "DB2_PREFIX=<DB2 home>/sqllib" qmake -- DB2_PREFIX="<DB2 home>/sqllib"
nmake sub-db2 nmake sub-db2
nmake install
//! [20] //! [20]
@ -184,6 +187,7 @@ make sub-sqlite
cd %QTDIR%\qtbase\src\plugins\sqldrivers cd %QTDIR%\qtbase\src\plugins\sqldrivers
qmake -- -system-sqlite SQLITE3_PREFIX=C:/SQLITE qmake -- -system-sqlite SQLITE3_PREFIX=C:/SQLITE
nmake sub-sqlite nmake sub-sqlite
nmake install
//! [23] //! [23]
@ -205,6 +209,7 @@ make sub-ibase
cd %QTDIR%\qtbase\src\plugins\sqldrivers cd %QTDIR%\qtbase\src\plugins\sqldrivers
qmake -- IBASE_INCDIR=C:/interbase/include qmake -- IBASE_INCDIR=C:/interbase/include
nmake sub-ibase nmake sub-ibase
nmake install
//! [29] //! [29]
@ -212,17 +217,18 @@ nmake sub-ibase
cd %QTDIR%\qtbase\src\plugins\sqldrivers cd %QTDIR%\qtbase\src\plugins\sqldrivers
qmake -- IBASE_INCDIR=C:/interbase/include IBASE_LIBS=-lfbclient qmake -- IBASE_INCDIR=C:/interbase/include IBASE_LIBS=-lfbclient
nmake sub-ibase nmake sub-ibase
nmake install
//! [30] //! [30]
//! [32] //! [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 make
//! [32] //! [32]
//! [33] //! [33]
cd $QTDIR/qtbase/src/plugins/sqldrivers 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 make sub-oci
//! [33] //! [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 // Call toLower() on the string so that it can be matched
QSqlRecord rec = database.record(tableString.toLower()); QSqlRecord rec = database.record(tableString.toLower());
//! [40] //! [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]

View File

@ -48,7 +48,7 @@
\header \li Driver name \li DBMS \header \li Driver name \li DBMS
\row \li \l{#QDB2}{QDB2} \li IBM DB2 (version 7.1 and above) \row \li \l{#QDB2}{QDB2} \li IBM DB2 (version 7.1 and above)
\row \li \l{#QIBASE}{QIBASE} \li Borland InterBase \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{#QOCI}{QOCI} \li Oracle Call Interface Driver
\row \li \l{#QODBC}{QODBC} \row \li \l{#QODBC}{QODBC}
\li Open Database Connectivity (ODBC) - Microsoft SQL Server and other \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. access to the API exposed by the DBMS, and is typically shipped with it.
Most installation programs also allow you to install "development Most installation programs also allow you to install "development
libraries", and these are what you need. These libraries are responsible 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 \note When using Qt under Open Source terms but with a proprietary
database, verify the client library's license compatibility with database, verify the client library's license compatibility with
@ -91,11 +92,21 @@
may be necessary to specify these paths using the \c *_INCDIR=, may be necessary to specify these paths using the \c *_INCDIR=,
\c *_LIBDIR=, or \c *_PREFIX= command-line options. For example, \c *_LIBDIR=, or \c *_PREFIX= command-line options. For example,
if your MySQL files are installed in \c /usr/local/mysql (or in if your MySQL files are installed in \c /usr/local/mysql (or in
\c{C:\mysql} on Windows), then pass the following parameter to \c{C:/Program Files/MySQL/MySQL Connector C 6.1} on Windows), then pass the
configure: \c MYSQL_PREFIX=/usr/local/mysql following parameter to configure: \c MYSQL_PREFIX=/usr/local/mysql
(or \c{MYSQL_PREFIX=C:\mysql} for Windows). (or \c{MYSQL_PREFIX="C:/Program Files/MySQL/MySQL Connector C 6.1"} for Windows).
The particulars for each driver are explained below. 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, Due to the practicalities of dealing with external dependencies,
only the SQLite3 plugin is shipped with binary builds of Qt. only the SQLite3 plugin is shipped with binary builds of Qt.
To be able to add additional drivers to the Qt installation To be able to add additional drivers to the Qt installation
@ -112,11 +123,11 @@
\section1 Driver Specifics \section1 Driver Specifics
\target QMYSQL \target QMYSQL
\section2 QMYSQL for MySQL 4 and higher \section2 QMYSQL for MySQL 5 and higher
\section3 QMYSQL Stored Procedure Support \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 API to control IN, OUT, and INOUT parameters. Therefore, parameters
have to be set and read using SQL commands instead of QSqlQuery::bindValue(). 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 \section3 How to Build the QMYSQL Plugin on Windows
You need to get the MySQL installation files. Run \c SETUP.EXE and You need to get the MySQL installation files (e.g.
choose "Custom Install". Install the "Libs & Include Files" Module. \e{mysql-installer-web-community-8.0.18.0.msi}). Run the installer,
Build the plugin as follows (here it is assumed that MySQL is select custom installation and install the MySQL C Connector
installed in \c{C:\MySQL}): 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 \snippet code/doc_src_sql-driver.qdoc 5
If you are not using a Microsoft compiler, replace \c nmake with \c If you are not using a Microsoft compiler, replace \c nmake with \c
mingw32-make in the line above. 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 \target QOCI
\section2 QOCI for the Oracle Call Interface (OCI) \section2 QOCI for the Oracle Call Interface (OCI)
@ -398,11 +425,6 @@
\snippet code/doc_src_sql-driver.qdoc 40 \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 \section3 QPSQL Forward-only query support
To use forward-only queries, you must build the QPSQL plugin with 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: 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}. \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 \target QTDS
\section2 QTDS for Sybase Adaptive Server \section2 QTDS for Sybase Adaptive Server

View File

@ -95,7 +95,7 @@ template<> inline char *toString(const QByteArray &ba)
template<> inline char *toString(const QBitArray &ba) template<> inline char *toString(const QBitArray &ba)
{ {
qsizetype size = ba.size(); 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) for (qsizetype i = 0; i < size; ++i)
str[i] = "01"[ba.testBit(i)]; str[i] = "01"[ba.testBit(i)];
str[size] = '\0'; str[size] = '\0';

View File

@ -1717,7 +1717,8 @@ bool scanImports(Options *options, QSet<QString> *usedDependencies)
qmlImportScanner += QLatin1String(" -qrcFiles"); qmlImportScanner += QLatin1String(" -qrcFiles");
for (const QString &qrcFile : options->qrcFiles) for (const QString &qrcFile : options->qrcFiles)
qmlImportScanner += QLatin1Char(' ') + shellQuote(qrcFile); qmlImportScanner += QLatin1Char(' ') + shellQuote(qrcFile);
} else { }
if (rootPath.isEmpty()) if (rootPath.isEmpty())
rootPath = QFileInfo(options->inputFileName).absolutePath(); rootPath = QFileInfo(options->inputFileName).absolutePath();
else else
@ -1725,8 +1726,8 @@ bool scanImports(Options *options, QSet<QString> *usedDependencies)
if (!rootPath.endsWith(QLatin1Char('/'))) if (!rootPath.endsWith(QLatin1Char('/')))
rootPath += QLatin1Char('/'); rootPath += QLatin1Char('/');
qmlImportScanner += QLatin1String(" -rootPath %1").arg(shellQuote(rootPath)); qmlImportScanner += QLatin1String(" -rootPath %1").arg(shellQuote(rootPath));
}
QStringList importPaths; QStringList importPaths;
importPaths += shellQuote(options->qtInstallDirectory + QLatin1String("/qml")); importPaths += shellQuote(options->qtInstallDirectory + QLatin1String("/qml"));

View File

@ -1478,12 +1478,18 @@ bool RCCResourceLibrary::writeInitializer()
writeString(" return 1;\n"); writeString(" return 1;\n");
writeString("}\n\n"); writeString("}\n\n");
writeByteArray(
"namespace {\n" writeString("namespace {\n"
" struct initializer {\n" " struct initializer {\n");
" initializer() { QT_RCC_MANGLE_NAMESPACE(" + initResources + ")(); }\n"
" ~initializer() { QT_RCC_MANGLE_NAMESPACE(" + cleanResources + ")(); }\n" if (m_useNameSpace) {
" } dummy;\n" 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"); "}\n");
} else if (m_format == Binary) { } else if (m_format == Binary) {

View File

@ -146,13 +146,13 @@ dialog.exec();
//! [14] //! [14]
//! [15] //! [15]
auto fileOpenCompleted = [](const QString &fileName, const QByteArray &fileContent) { auto fileContentReady = [](const QString &fileName, const QByteArray &fileContent) {
if (fileName.isEmpty()) { if (fileName.isEmpty()) {
// No file was selected // No file was selected
} else { } else {
// Use fileName and fileContent // Use fileName and fileContent
} }
} };
QFileDialog::getOpenFileContent("Images (*.png *.xpm *.jpg)", fileContentReady); QFileDialog::getOpenFileContent("Images (*.png *.xpm *.jpg)", fileContentReady);
//! [15] //! [15]

View File

@ -31,6 +31,7 @@
#include <QtTest/QtTest> #include <QtTest/QtTest>
#include <qvariant.h> #include <qvariant.h>
#include <QtCore/private/qvariant_p.h>
#include <qbitarray.h> #include <qbitarray.h>
#include <qbytearraylist.h> #include <qbytearraylist.h>
#include <qdatetime.h> #include <qdatetime.h>
@ -276,7 +277,8 @@ private slots:
void nullConvert(); void nullConvert();
void accessSequentialContainerKey(); void accessSequentialContainerKey();
void shouldDeleteVariantDataWorksForSequential();
void shouldDeleteVariantDataWorksForAssociative();
void fromStdVariant(); void fromStdVariant();
void qt4UuidDataStream(); void qt4UuidDataStream();
@ -4990,6 +4992,99 @@ void tst_QVariant::accessSequentialContainerKey()
QCOMPARE(nameResult, QStringLiteral("Seven")); 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() void tst_QVariant::fromStdVariant()
{ {
#if __has_include(<variant>) && __cplusplus >= 201703L #if __has_include(<variant>) && __cplusplus >= 201703L

View File

@ -2213,8 +2213,46 @@ void tst_QDateTime::fromStringDateFormat_data()
QTest::newRow("trailing space") // QTBUG-80445 QTest::newRow("trailing space") // QTBUG-80445
<< QString("2000-01-02 03:04:05.678 ") << QString("2000-01-02 03:04:05.678 ")
<< Qt::ISODate << QDateTime(QDate(2000, 1, 2), QTime(3, 4, 5, 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") QTest::newRow("space before millis")
<< QString("2000-01-02 03:04:05. 678") << Qt::ISODate << QDateTime(); << 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: // Normal usage:
QTest::newRow("ISO +01:00") << QString::fromLatin1("1987-02-13T13:24:51+01:00") QTest::newRow("ISO +01:00") << QString::fromLatin1("1987-02-13T13:24:51+01:00")