Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-10451

wrong default NumericalPrecisionPolicy



    • Type: Bug
    • Status: Open
    • Priority: P3: Somewhat important
    • Resolution: Unresolved
    • Affects Version/s: 4.6.2, 5.5.0, 5.11.0
    • Fix Version/s: None
    • Component/s: SQL Support
    • Labels:
    • Environment:


      The problem was noticed during switching from qt 4.5.3 to 4.6.2: values returned from postgres as "numeric" have wrong type QVariant::double instead of correct QVariant::QString. This seems to be due to wrong default NumericalPrecisionPolicy.

      Docs for QSql::NumericalPrecisionPolicy say that

      	QSql::HighPrecision	0	The default behavior

      But we get QSql::LowPrecisionDouble as default instead.

      The result is that unless we call setNumericalPrecisionPolicy(QSql::HighPrecision) explicitly, the values are recognized as QVariant::double. Example of a problem with this is that if such values are shown in ui using query.value(0).toString(), the resulting string shows the result without final zeros after decimal dot, or are shown rounded (for values with lots of decimals). In any case (indepnenent of toString()) precision can be lost and there is no way to be sure that the retrived value is exactly what the db returned (again, unless one calls setNumericalPrecisionPolicy(QSql::HighPrecision) explicitly).

      I attach example which reproduces the problem. I select from database two numeric values: 1.230 and 1.234567890123456789.

      I just add database, set host, port etc. and execute one query:

      select 1.230::numeric, 1.234567890123456789::numeric

      The result is:

      numericalPrecisionPolicy_test-v1> ./numericalPrecisionPolicy_test
      db numericalPrecisionPolicy:  4
      driver numericalPrecisionPolicy:  4
      query numericalPrecisionPolicy:  4
      query.v(0) type:  QVariant::double
      query.v(0) toString:  "1.23"
      query.v(1) type:  QVariant::double
      query.v(1) toString:  "1.23456789012346"

      As you can see, the default numericalPrecisionPolicy is 4 (QSql::LowPrecisionDouble). (This eventually leads to resulting type QVariant::double and rouded values shown by toString()).

      Afer calling setNumericalPrecisionPolicy(QSql::HighPrecision) explicitly (see attached v2 version of the test code) we get a precise result (which according to the docs should have been the default behaviour!):

      numericalPrecisionPolicy_test-v2> ./numericalPrecisionPolicy_test
      db numericalPrecisionPolicy: 0
      driver numericalPrecisionPolicy: 0
      query numericalPrecisionPolicy: 0
      query.v(0) type: QVariant::QString
      query.v(0) toString: "1.230"
      query.v(1) type: QVariant::QString
      query.v(1) toString: "1.234567890123456789"

      Note: we noticed this problem while switching from qt 4.5.3 to 4.6.2, but it seems that there was no change in relevant places in qt-git, so the same bug should be reproducible with 4.7. Here are the relevant diffs between 4.5.3 to 4.6.2.

      It seems that the code setting teh default precisionPolicy to QSql::HighPrecision was removed from constructor of one of private classes:

      diff -ru sql-4.5.3/drivers/psql//qsql_psql.cpp sql-4.6.2/drivers/psql//qsql_psql.cpp
      --- sql-4.5.3/drivers/psql//qsql_psql.cpp       2009-09-29 13:01:39.000000000 +0200
      +++ sql-4.6.2/drivers/psql//qsql_psql.cpp       2010-05-06 11:20:53.576505175 +0200
      @@ -158,13 +158,12 @@
       class QPSQLResultPrivate
      -    QPSQLResultPrivate(QPSQLResult *qq): q(qq), driver(0), result(0), currentSize(-1), precisionPolicy(QSql::HighPrecision) {}
      +    QPSQLResultPrivate(QPSQLResult *qq): q(qq), driver(0), result(0), currentSize(-1), preparedQueriesEnabled(false) {}
           QPSQLResult *q;
           const QPSQLDriverPrivate *driver;
           PGresult *result;
           int currentSize;
      -    QSql::NumericalPrecisionPolicy precisionPolicy;
           bool preparedQueriesEnabled;
           QString preparedStmtId;

      and the intention seems to have been that the default value is set by the driver:

      diff -ru sql-4.5.3/kernel/qsqldatabase.cpp sql-4.6.2/kernel/qsqldatabase.cpp
      --- sql-4.5.3/kernel/qsqldatabase.cpp   2009-09-29 13:01:39.000000000 +0200
      +++ sql-4.6.2/kernel/qsqldatabase.cpp   2010-05-06 11:20:57.366194509 +0200
      @@ -129,11 +129,16 @@
       class QSqlDatabasePrivate
      -    QSqlDatabasePrivate(QSqlDriver *dr = 0):
      +    QSqlDatabasePrivate(QSqlDatabase *d, QSqlDriver *dr = 0):
      +        q(d),
               ref = 1;
      +        if(driver)
      +            precisionPolicy = driver->numericalPrecisionPolicy();
      +        else
      +            precisionPolicy= QSql::LowPrecisionDouble;
           QSqlDatabasePrivate(const QSqlDatabasePrivate &other);

      There are a few problems with this:
      1. first, if driver == 0 the default policy is set to QSql::LowPrecisionDouble (see the last different line of above diff) -> if one follows the docs, it should be considered a bug, it should have been QSql::HighPrecision;
      2. second, this part has any chance to be executed with driver != 0 only if one uses "addDatabase( QSqlDriver * driver...)" (i.e. one constructs db connection herself/himself first); this code will never be called if one uses (a much, much simpler) "addDatabase( const QString & type ...)", because of the version of QSqlDatabase constructor called from "addDatabase( const QString & type ...)".
      3. third, even if one constructs psql connection herself/himself, the default stays QSql::LowPrecisionDouble; it seems that the intention was to set this default in QSqlDatabasePrivate based on the driver, but the corresponding changes in QSqlDriver were not done (at least not in the case of postgres: see version 3 of the test code, which still results in

      numericalPrecisionPolicy_test-v3> ./numericalPrecisionPolicy_test
      db numericalPrecisionPolicy:  4
      driver numericalPrecisionPolicy:  4
      query numericalPrecisionPolicy:  4
      query.v(0) type:  QVariant::double
      query.v(0) toString:  "1.23"
      query.v(1) type:  QVariant::double
      query.v(1) toString:  "1.23456789012346"



          Issue Links

          No reviews matched the request. Check your Options in the drop-down menu of this sections header.



              andysh Andy Shaw
              wiecko Marek Wieckowski
              2 Vote for this issue
              5 Start watching this issue



                  Gerrit Reviews

                  There are no open Gerrit changes