diff options
-rw-r--r-- | src/sql/drivers/psql/qsql_psql.cpp | 43 | ||||
-rw-r--r-- | tests/auto/sql/kernel/qsqldatabase/tst_databases.h | 2 | ||||
-rw-r--r-- | tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp | 42 |
3 files changed, 60 insertions, 27 deletions
diff --git a/src/sql/drivers/psql/qsql_psql.cpp b/src/sql/drivers/psql/qsql_psql.cpp index e76dcc26a0..4268ea06f6 100644 --- a/src/sql/drivers/psql/qsql_psql.cpp +++ b/src/sql/drivers/psql/qsql_psql.cpp @@ -426,11 +426,8 @@ QVariant QPSQLResult::data(int i) #ifndef QT_NO_DATESTRING if (str.isEmpty()) return QVariant(QTime()); - if (str.at(str.length() - 3) == QLatin1Char('+') || str.at(str.length() - 3) == QLatin1Char('-')) - // strip the timezone - // TODO: fix this when timestamp support comes into QDateTime - return QVariant(QTime::fromString(str.left(str.length() - 3), Qt::ISODate)); - return QVariant(QTime::fromString(str, Qt::ISODate)); + else + return QVariant(QTime::fromString(str, Qt::ISODate)); #else return QVariant(str); #endif @@ -438,19 +435,13 @@ QVariant QPSQLResult::data(int i) case QVariant::DateTime: { QString dtval = QString::fromLatin1(val); #ifndef QT_NO_DATESTRING - if (dtval.length() < 10) - return QVariant(QDateTime()); - // remove the timezone - // TODO: fix this when timestamp support comes into QDateTime - if (dtval.at(dtval.length() - 3) == QLatin1Char('+') || dtval.at(dtval.length() - 3) == QLatin1Char('-')) - dtval.chop(3); - // milliseconds are sometimes returned with 2 digits only - if (dtval.at(dtval.length() - 3).isPunct()) - dtval += QLatin1Char('0'); - if (dtval.isEmpty()) + if (dtval.length() < 10) { return QVariant(QDateTime()); - else - return QVariant(QDateTime::fromString(dtval, Qt::ISODate)); + } else { + QChar sign = dtval[dtval.size() - 3]; + if (sign == QLatin1Char('-') || sign == QLatin1Char('+')) dtval += QLatin1String(":00"); + return QVariant(QDateTime::fromString(dtval, Qt::ISODate).toLocalTime()); + } #else return QVariant(dtval); #endif @@ -536,6 +527,11 @@ QSqlRecord QPSQLResult::record() const int precision = PQfmod(d->result, i); switch (ptype) { + case QTIMESTAMPOID: + case QTIMESTAMPTZOID: + precision = 3; + break; + case QNUMERICOID: if (precision != -1) { len = (precision >> 16); @@ -1263,15 +1259,10 @@ QString QPSQLDriver::formatValue(const QSqlField &field, bool trimStrings) const case QVariant::DateTime: #ifndef QT_NO_DATESTRING if (field.value().toDateTime().isValid()) { - QDate dt = field.value().toDateTime().date(); - QTime tm = field.value().toDateTime().time(); - // msecs need to be right aligned otherwise psql interprets them wrong - r = QLatin1Char('\'') + QString::number(dt.year()) + QLatin1Char('-') - + QString::number(dt.month()).rightJustified(2, QLatin1Char('0')) + QLatin1Char('-') - + QString::number(dt.day()).rightJustified(2, QLatin1Char('0')) + QLatin1Char(' ') - + tm.toString() + QLatin1Char('.') - + QString::number(tm.msec()).rightJustified(3, QLatin1Char('0')) - + QLatin1Char('\''); + // we force the value to be considered with a timezone information, and we force it to be UTC + // this is safe since postgresql stores only the UTC value and not the timezone offset (only used + // while parsing), so we have correct behavior in both case of with timezone and without tz + r = QLatin1String("TIMESTAMP WITH TIME ZONE ") + QLatin1Char('\'') + field.value().toDateTime().toUTC().toString(QLatin1String("yyyy-MM-ddThh:mm:ss.zzz")) + QLatin1Char('Z') + QLatin1Char('\''); } else { r = QLatin1String("NULL"); } diff --git a/tests/auto/sql/kernel/qsqldatabase/tst_databases.h b/tests/auto/sql/kernel/qsqldatabase/tst_databases.h index af6dc6b9f4..8b080324d5 100644 --- a/tests/auto/sql/kernel/qsqldatabase/tst_databases.h +++ b/tests/auto/sql/kernel/qsqldatabase/tst_databases.h @@ -493,7 +493,7 @@ public: { const QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db); if (dbType == QSqlDriver::PostgreSQL) - return QLatin1String("timestamp"); + return QLatin1String("timestamptz"); if (dbType == QSqlDriver::Oracle && getOraVersion(db) >= 9) return QLatin1String("timestamp(0)"); return QLatin1String("datetime"); diff --git a/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp b/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp index f5d71a75cf..1943b6ec92 100644 --- a/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp +++ b/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp @@ -224,6 +224,9 @@ private slots: void QTBUG_2192_data() { generic_data(); } void QTBUG_2192(); + void QTBUG_36211_data() { generic_data("QPSQL"); } + void QTBUG_36211(); + void sqlite_constraint_data() { generic_data("QSQLITE"); } void sqlite_constraint(); @@ -3558,6 +3561,45 @@ void tst_QSqlQuery::QTBUG_2192() } } +void tst_QSqlQuery::QTBUG_36211() +{ + QFETCH( QString, dbName ); + QSqlDatabase db = QSqlDatabase::database( dbName ); + CHECK_DATABASE( db ); + if (tst_Databases::getDatabaseType(db) == QSqlDriver::PostgreSQL) { + const QString tableName(qTableName("bug36211", __FILE__, db)); + tst_Databases::safeDropTable( db, tableName ); + + QSqlQuery q(db); + QVERIFY_SQL(q, exec(QString("CREATE TABLE %1 (dtwtz timestamptz, dtwotz timestamp)").arg(tableName))); + + QTimeZone l_tzBrazil("BRT"); + QTimeZone l_tzChina("CST"); + QDateTime dt = QDateTime(QDate(2014, 10, 30), QTime(14, 12, 02, 357)); + QVERIFY_SQL(q, prepare("INSERT INTO " + tableName + " (dtwtz, dtwotz) VALUES (:dt, :dt)")); + q.bindValue(":dt", dt); + QVERIFY_SQL(q, exec()); + q.bindValue(":dt", dt.toTimeZone(l_tzBrazil)); + QVERIFY_SQL(q, exec()); + q.bindValue(":dt", dt.toTimeZone(l_tzChina)); + QVERIFY_SQL(q, exec()); + + QVERIFY_SQL(q, exec("SELECT dtwtz, dtwotz FROM " + tableName)); + + for (int i = 0; i < 3; ++i) { + QVERIFY_SQL(q, next()); + + for (int j = 0; j < 2; ++j) { + // Check if retrieved value preserves reported precision + int precision = qMax(0, q.record().field(j).precision()); + int diff = qAbs(q.value(j).toDateTime().msecsTo(dt)); + int keep = qMin(1000, (int)qPow(10.0, precision)); + QVERIFY(diff <= 1000 - keep); + } + } + } +} + void tst_QSqlQuery::oraOCINumber() { QFETCH( QString, dbName ); |