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:
parent
70cfe551b2
commit
3185b40d5d
@ -57,29 +57,7 @@
|
|||||||
#include <libpq-fe.h>
|
#include <libpq-fe.h>
|
||||||
#include <pg_config.h>
|
#include <pg_config.h>
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <cmath>
|
||||||
#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
|
|
||||||
|
|
||||||
|
|
||||||
// workaround for postgres defining their OIDs in a private header file
|
// workaround for postgres defining their OIDs in a private header file
|
||||||
#define QBOOLOID 16
|
#define QBOOLOID 16
|
||||||
@ -135,7 +113,7 @@ static const StatementId InvalidStatementId = 0;
|
|||||||
|
|
||||||
class QPSQLResultPrivate;
|
class QPSQLResultPrivate;
|
||||||
|
|
||||||
class QPSQLResult: public QSqlResult
|
class QPSQLResult final : public QSqlResult
|
||||||
{
|
{
|
||||||
Q_DECLARE_PRIVATE(QPSQLResult)
|
Q_DECLARE_PRIVATE(QPSQLResult)
|
||||||
|
|
||||||
@ -164,7 +142,7 @@ protected:
|
|||||||
bool exec() override;
|
bool exec() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class QPSQLDriverPrivate : public QSqlDriverPrivate
|
class QPSQLDriverPrivate final : public QSqlDriverPrivate
|
||||||
{
|
{
|
||||||
Q_DECLARE_PUBLIC(QPSQLDriver)
|
Q_DECLARE_PUBLIC(QPSQLDriver)
|
||||||
public:
|
public:
|
||||||
@ -671,7 +649,7 @@ QVariant QPSQLResult::data(int i)
|
|||||||
return QString::fromLatin1(val).toULongLong();
|
return QString::fromLatin1(val).toULongLong();
|
||||||
case QVariant::Int:
|
case QVariant::Int:
|
||||||
return atoi(val);
|
return atoi(val);
|
||||||
case QVariant::Double:
|
case QVariant::Double: {
|
||||||
if (ptype == QNUMERICOID) {
|
if (ptype == QNUMERICOID) {
|
||||||
if (numericalPrecisionPolicy() != QSql::HighPrecision) {
|
if (numericalPrecisionPolicy() != QSql::HighPrecision) {
|
||||||
QVariant retval;
|
QVariant retval;
|
||||||
@ -689,7 +667,12 @@ QVariant QPSQLResult::data(int i)
|
|||||||
}
|
}
|
||||||
return QString::fromLatin1(val);
|
return QString::fromLatin1(val);
|
||||||
}
|
}
|
||||||
|
if (qstricmp(val, "Infinity") == 0)
|
||||||
|
return qInf();
|
||||||
|
if (qstricmp(val, "-Infinity") == 0)
|
||||||
|
return -qInf();
|
||||||
return QString::fromLatin1(val).toDouble();
|
return QString::fromLatin1(val).toDouble();
|
||||||
|
}
|
||||||
case QVariant::Date:
|
case QVariant::Date:
|
||||||
if (val[0] == '\0') {
|
if (val[0] == '\0') {
|
||||||
return QVariant(QDate());
|
return QVariant(QDate());
|
||||||
@ -1497,18 +1480,10 @@ QSqlRecord QPSQLDriver::record(const QString& tablename) const
|
|||||||
template <class FloatType>
|
template <class FloatType>
|
||||||
inline void assignSpecialPsqlFloatValue(FloatType val, QString *target)
|
inline void assignSpecialPsqlFloatValue(FloatType val, QString *target)
|
||||||
{
|
{
|
||||||
if (isnan(val)) {
|
if (qIsNaN(val))
|
||||||
*target = QLatin1String("'NaN'");
|
*target = QStringLiteral("'NaN'");
|
||||||
} else {
|
else if (qIsInf(val))
|
||||||
switch (isinf(val)) {
|
*target = (val < 0) ? QStringLiteral("'-Infinity'") : QStringLiteral("'Infinity'");
|
||||||
case 1:
|
|
||||||
*target = QLatin1String("'Infinity'");
|
|
||||||
break;
|
|
||||||
case -1:
|
|
||||||
*target = QLatin1String("'-Infinity'");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString QPSQLDriver::formatValue(const QSqlField &field, bool trimStrings) const
|
QString QPSQLDriver::formatValue(const QSqlField &field, bool trimStrings) const
|
||||||
|
@ -126,6 +126,8 @@ private slots:
|
|||||||
void formatValueTrimStrings();
|
void formatValueTrimStrings();
|
||||||
void precisionPolicy_data() { generic_data(); }
|
void precisionPolicy_data() { generic_data(); }
|
||||||
void precisionPolicy();
|
void precisionPolicy();
|
||||||
|
void infinityAndNan_data() { generic_data(); }
|
||||||
|
void infinityAndNan();
|
||||||
void multipleThreads_data() { generic_data(); }
|
void multipleThreads_data() { generic_data(); }
|
||||||
void multipleThreads();
|
void multipleThreads();
|
||||||
|
|
||||||
@ -1467,6 +1469,46 @@ void tst_QSqlDatabase::precisionPolicy()
|
|||||||
db.setNumericalPrecisionPolicy(oldPrecision);
|
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
|
// This test needs a ODBC data source containing MYSQL in it's name
|
||||||
void tst_QSqlDatabase::mysqlOdbc_unsignedIntegers()
|
void tst_QSqlDatabase::mysqlOdbc_unsignedIntegers()
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user