SQL/Postgres: Fix support for nan, inf and -inf

Postgresql needs a special value for nan and +/- inf. This was
considered during insert but not during select.
Also remove some pre-c++11 inf/nan - handling and replace it with
Qt equivalents.

Change-Id: I044ca58e9cf673f4b100b05a0d8e25c8a9c29ec5
Reviewed-by: Robert Szefner <robertsz27@interia.pl>
Reviewed-by: Andy Shaw <andy.shaw@qt.io>
This commit is contained in:
Christian Ehrlicher 2018-02-07 20:12:33 +01:00
parent 70cfe551b2
commit 3185b40d5d
2 changed files with 55 additions and 38 deletions

View File

@ -57,29 +57,7 @@
#include <libpq-fe.h>
#include <pg_config.h>
#include <stdlib.h>
#include <math.h>
// below code taken from an example at http://www.gnu.org/software/hello/manual/autoconf/Function-Portability.html
#ifndef isnan
# define isnan(x) \
(sizeof (x) == sizeof (long double) ? isnan_ld (x) \
: sizeof (x) == sizeof (double) ? isnan_d (x) \
: isnan_f (x))
static inline int isnan_f (float x) { return x != x; }
static inline int isnan_d (double x) { return x != x; }
static inline int isnan_ld (long double x) { return x != x; }
#endif
#ifndef isinf
# define isinf(x) \
(sizeof (x) == sizeof (long double) ? isinf_ld (x) \
: sizeof (x) == sizeof (double) ? isinf_d (x) \
: isinf_f (x))
static inline int isinf_f (float x) { return isnan (x - x); }
static inline int isinf_d (double x) { return isnan (x - x); }
static inline int isinf_ld (long double x) { return isnan (x - x); }
#endif
#include <cmath>
// workaround for postgres defining their OIDs in a private header file
#define QBOOLOID 16
@ -135,7 +113,7 @@ static const StatementId InvalidStatementId = 0;
class QPSQLResultPrivate;
class QPSQLResult: public QSqlResult
class QPSQLResult final : public QSqlResult
{
Q_DECLARE_PRIVATE(QPSQLResult)
@ -164,7 +142,7 @@ protected:
bool exec() override;
};
class QPSQLDriverPrivate : public QSqlDriverPrivate
class QPSQLDriverPrivate final : public QSqlDriverPrivate
{
Q_DECLARE_PUBLIC(QPSQLDriver)
public:
@ -671,7 +649,7 @@ QVariant QPSQLResult::data(int i)
return QString::fromLatin1(val).toULongLong();
case QVariant::Int:
return atoi(val);
case QVariant::Double:
case QVariant::Double: {
if (ptype == QNUMERICOID) {
if (numericalPrecisionPolicy() != QSql::HighPrecision) {
QVariant retval;
@ -689,7 +667,12 @@ QVariant QPSQLResult::data(int i)
}
return QString::fromLatin1(val);
}
if (qstricmp(val, "Infinity") == 0)
return qInf();
if (qstricmp(val, "-Infinity") == 0)
return -qInf();
return QString::fromLatin1(val).toDouble();
}
case QVariant::Date:
if (val[0] == '\0') {
return QVariant(QDate());
@ -1497,18 +1480,10 @@ QSqlRecord QPSQLDriver::record(const QString& tablename) const
template <class FloatType>
inline void assignSpecialPsqlFloatValue(FloatType val, QString *target)
{
if (isnan(val)) {
*target = QLatin1String("'NaN'");
} else {
switch (isinf(val)) {
case 1:
*target = QLatin1String("'Infinity'");
break;
case -1:
*target = QLatin1String("'-Infinity'");
break;
}
}
if (qIsNaN(val))
*target = QStringLiteral("'NaN'");
else if (qIsInf(val))
*target = (val < 0) ? QStringLiteral("'-Infinity'") : QStringLiteral("'Infinity'");
}
QString QPSQLDriver::formatValue(const QSqlField &field, bool trimStrings) const

View File

@ -126,6 +126,8 @@ private slots:
void formatValueTrimStrings();
void precisionPolicy_data() { generic_data(); }
void precisionPolicy();
void infinityAndNan_data() { generic_data(); }
void infinityAndNan();
void multipleThreads_data() { generic_data(); }
void multipleThreads();
@ -1467,6 +1469,46 @@ void tst_QSqlDatabase::precisionPolicy()
db.setNumericalPrecisionPolicy(oldPrecision);
}
void tst_QSqlDatabase::infinityAndNan()
{
QFETCH(QString, dbName);
QSqlDatabase db = QSqlDatabase::database(dbName);
CHECK_DATABASE(db);
if (tst_Databases::getDatabaseType(db) != QSqlDriver::PostgreSQL)
QSKIP("checking for infinity/nan currently only works for PostgreSQL");
QSqlQuery q(db);
const QString tableName(qTableName("infititytest", __FILE__, db));
tst_Databases::safeDropTables(db, {tableName});
QVERIFY_SQL(q, exec(QString("CREATE TABLE %1 (id smallint, val double precision)").arg(tableName)));
QVERIFY_SQL(q, prepare(QString("INSERT INTO %1 VALUES (?, ?)").arg(tableName)));
q.bindValue(0, 1);
q.bindValue(1, qQNaN());
QVERIFY_SQL(q, exec());
q.bindValue(0, 2);
q.bindValue(1, qInf());
QVERIFY_SQL(q, exec());
q.bindValue(0, 3);
q.bindValue(1, -qInf());
QVERIFY_SQL(q, exec());
QVERIFY_SQL(q, exec(QString("SELECT val FROM %1 ORDER BY id").arg(tableName)));
QVERIFY_SQL(q, next());
QVERIFY(qIsNaN(q.value(0).toDouble()));
QVERIFY_SQL(q, next());
QVERIFY(qIsInf(q.value(0).toDouble()));
QVERIFY(q.value(0).toDouble() > 0);
QVERIFY_SQL(q, next());
QVERIFY(qIsInf(q.value(0).toDouble()));
QVERIFY(q.value(0).toDouble() < 0);
}
// This test needs a ODBC data source containing MYSQL in it's name
void tst_QSqlDatabase::mysqlOdbc_unsignedIntegers()
{