Trolltech Home | Qt-interest Home | Recent Threads | All Threads | Author | Date
All threads index page 5

Qt-interest Archive, February 2007
QODBC driver harms QSqlQuery objects


Message 1 in thread

Dear Qt Community,

during the development of a database application I recently encountered 
the following problem with Ot's ODBC driver on Windows when connecting to 
a PostgreSQL server on Linux using the official psqlODBC driver: If the 
database connection is closed with QSqlDatabase::close() and subsequently 
reopened with QSqlDatabase::open() this close-open-cycle renders QSqlQuery 
objects relating to the freshly reestablished database connection useless. 
The application crashes due to an access violation when QSqlQuery::exec() 
is called on a query object after the second QSqlDatabase::open(). Here's 
a short example:

#include <QtGui>
#include <QtSql>

#include <iostream>

int main(int argc, char *argv[])
{
     QApplication app(argc,argv);
     QSqlDatabase db = QSqlDatabase::addDatabase("QODBC");
     db.setDatabaseName("...YOUR DATABASE HERE...");
     if (!db.open()) {
         std::cerr << "ERROR OPENING DATABASE\n";
         exit(1);
     }

     std::cerr << "FIRST QUERY... ";
     QSqlQuery query("select 0");
     std::cerr << (query.isActive() ? "DONE\n" : "FAILED\n");

     db.close();
     db.open();  // <-- This harms the QSqlQuery object above.

     std::cerr << "SECOND QUERY... ";
     query.exec(query.lastQuery());
     std::cerr << (query.isActive() ? "DONE\n" : "FAILED\n");
}

The program never returns from the call of query.exec(). This seems to be 
faulty behaviour because, IMHO, QSqlQuery objects should not be harmed by 
closing and reopening the database connection they are related to - as 
opposed to the latter's removal with QSqlDatabase::removeDatabase().

Moreover, one can observe that just closing the database connection, i.e. 
without the second call to db.open(), the call of query.exec() doesn't 
make the application crash, but complains correctly that the query is 
executed on a closed database connection.

Because closing and immediate reopening of a database connection is a 
simple but effective means of handling a connection's temporary loss this 
behaviour is quite annoying. A possible workaround is to create the query 
objects on the heap, destroy and recreate them in case of a failure and 
delete them finally, but this approach is much less elegant than having 
them exist on the stack like it is shown throughout Qt's documentation.

The abovementioned behaviour does not show up on Linux when using the 
PostgreSQL driver QPSQL. Therefore, I'd like to ask for opinions and 
experiences, especially:

- Has anybody encountered the same phenonemon or can reproduce it?
- Does anybody know if this behaviour is caused by Qt or psqlODBC?
- As the case may be, should this be filed as a bug report to TT?

The versions of the involved software are:

PostgreSQL 8.2.3 on Debian Etch 20061111
psqlODBC-08_02_0200 on Windows Server 2003 / XP
Qt 4.2.2 Open Source Edition on Linux and Windows

Any comments and advice will be greatly appreciated.

Thanks and Regards

Michael Hertling

--
 [ signature omitted ] 

Message 2 in thread

I am not expert in QODBC, but I know a little about ODBC itself. Hope it
will help.
You go against ODBC principles here. In pure ODBC you create handle for
database connection, and later you create handle for SQL statement on top of
database connection. Closing database connection effectively kills all
statements based on this connection.
So, in your example query.exec() SHOULD fail with exception on the second
run... or it should silently 'recreate' itself, reattaching inner statement
handle to a new database handle. I am not sure which way would be more
"QT-way".
And yes, recreating QSqlQuery object or objects each time you reconnect to
database and destroying them each time you lose connection to database is a
correct way to go from ODBC-driver point of view.

"Michael Hertling" <hertling@xxxxxxxxxxxxxxxxxxx> wrote in message
news:Pine.LNX.4.64.0702191646520.17551@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
> Dear Qt Community,
>
> during the development of a database application I recently encountered
> the following problem with Ot's ODBC driver on Windows when connecting to
> a PostgreSQL server on Linux using the official psqlODBC driver: If the
> database connection is closed with QSqlDatabase::close() and subsequently
> reopened with QSqlDatabase::open() this close-open-cycle renders QSqlQuery
> objects relating to the freshly reestablished database connection useless.
> The application crashes due to an access violation when QSqlQuery::exec()
> is called on a query object after the second QSqlDatabase::open(). Here's
> a short example:
>
> #include <QtGui>
> #include <QtSql>
>
> #include <iostream>
>
> int main(int argc, char *argv[])
> {
>      QApplication app(argc,argv);
>      QSqlDatabase db = QSqlDatabase::addDatabase("QODBC");
>      db.setDatabaseName("...YOUR DATABASE HERE...");
>      if (!db.open()) {
>          std::cerr << "ERROR OPENING DATABASE\n";
>          exit(1);
>      }
>
>      std::cerr << "FIRST QUERY... ";
>      QSqlQuery query("select 0");
>      std::cerr << (query.isActive() ? "DONE\n" : "FAILED\n");
>
>      db.close();
>      db.open();  // <-- This harms the QSqlQuery object above.
>
>      std::cerr << "SECOND QUERY... ";
>      query.exec(query.lastQuery());
>      std::cerr << (query.isActive() ? "DONE\n" : "FAILED\n");
> }
>
> The program never returns from the call of query.exec(). This seems to be
> faulty behaviour because, IMHO, QSqlQuery objects should not be harmed by
> closing and reopening the database connection they are related to - as
> opposed to the latter's removal with QSqlDatabase::removeDatabase().
>
> Moreover, one can observe that just closing the database connection, i.e.
> without the second call to db.open(), the call of query.exec() doesn't
> make the application crash, but complains correctly that the query is
> executed on a closed database connection.
>
> Because closing and immediate reopening of a database connection is a
> simple but effective means of handling a connection's temporary loss this
> behaviour is quite annoying. A possible workaround is to create the query
> objects on the heap, destroy and recreate them in case of a failure and
> delete them finally, but this approach is much less elegant than having
> them exist on the stack like it is shown throughout Qt's documentation.
>
> The abovementioned behaviour does not show up on Linux when using the
> PostgreSQL driver QPSQL. Therefore, I'd like to ask for opinions and
> experiences, especially:
>
> - Has anybody encountered the same phenonemon or can reproduce it?
> - Does anybody know if this behaviour is caused by Qt or psqlODBC?
> - As the case may be, should this be filed as a bug report to TT?
>
> The versions of the involved software are:
>
> PostgreSQL 8.2.3 on Debian Etch 20061111
> psqlODBC-08_02_0200 on Windows Server 2003 / XP
> Qt 4.2.2 Open Source Edition on Linux and Windows
>
> Any comments and advice will be greatly appreciated.
>
> Thanks and Regards
>
> Michael Hertling
>
> --
> To unsubscribe - send a mail to qt-interest-request@xxxxxxxxxxxxx with
"unsubscribe" in the subject or the body.
> List archive and information: http://lists.trolltech.com/qt-interest/


--
 [ signature omitted ] 

Message 3 in thread

Hi,

> So, in your example query.exec() SHOULD fail with exception on the second
> run... or it should silently 'recreate' itself, reattaching inner statement
> handle to a new database handle. I am not sure which way would be more
> "QT-way".

Silently 'recreating' itself would be "Qt-ish" indeed, but what if there 
are multiple active database connections? Which one should be used?

--
 [ signature omitted ] 

Message 4 in thread

Hi Dimitri!

On Thu, 22 Feb 2007, Dimitri wrote:

>> So, in your example query.exec() SHOULD fail with exception on the second
>> run... or it should silently 'recreate' itself, reattaching inner statement
>> handle to a new database handle. I am not sure which way would be more
>> "QT-way".
>
> Silently 'recreating' itself would be "Qt-ish" indeed, but what if there are 
> multiple active database connections? Which one should be used?

Obviously, this should be the connection the QSqlQuery was created for 
initially, i.e. the one mentioned in the constructor and, casually, still 
persisting even after it has been closed. For this purpose, the QSqlQuery 
has to be aware of "its" QSqlDatabase, but a glance into the sources shows 
that, apparently, QSqlQuery only contains data necessary for the execution 
of its SQL statement while not having a link to the affected QSqlDatabase. 
This issue has, IMHO, already given rise to several postings and also to 
task #131011. Perhaps, the latter's resolution will clarify the somewhat 
subtle relationship between QSqlDatabase and QSqlQuery.

Regards

Michael Hertling

--
 [ signature omitted ] 

Message 5 in thread

Hi,

> Obviously, this should be the connection the QSqlQuery was created for 
> initially, i.e. the one mentioned in the constructor and, casually, 
> still persisting even after it has been closed.

Indeed, but how do you identify a connection? For example there could be 
two connections to the same database in a program (at least that's my 
understanding, I don't know much about this part of Qt). If one of the 
connections is killed, should you re-use the other connection or create 
another one?

What if a password is needed for the connection?

What if the database connection has been killed on purpose elsewhere in 
the program? Then it should not be silently recreated.

What if... OK, just playing devil's advocate :-)

--
 [ signature omitted ] 

Message 6 in thread

I wrote:
> Indeed, but how do you identify a connection? For example there could be 
> two connections to the same database in a program (at least that's my 
> understanding, I don't know much about this part of Qt). If one of the 
> connections is killed, should you re-use the other connection or create 
> another one?

Hmmm... QSqlQuery remembers the QSqlDatabse passed as a parameter to its 
constructor. I should have thought before posting this one...

--
 [ signature omitted ] 

Message 7 in thread

Hi Dimitri!

On Sun, 25 Feb 2007, Dimitri wrote:

> I wrote:
>> Indeed, but how do you identify a connection? For example there could be 
>> two connections to the same database in a program (at least that's my 
>> understanding, I don't know much about this part of Qt). If one of the 
>> connections is killed, should you re-use the other connection or create 
>> another one?
>
> Hmmm... QSqlQuery remembers the QSqlDatabse passed as a parameter to its 
> constructor. I should have thought before posting this one...

This is a point about which I'm increasingly in doubt. Having thrown a 
glance at the sources, I can't see if QSqlQuery stores the QSqlDatabase 
mentioned in its constructor. Possibly, I have overlooked the concerning 
lines of code, but, up to now, it seems to me as if QSqlQuery only stores 
the data needed to handle its SQL statement, e.g. the PGconn and PGresult 
pointers when using PostgreSQL or at least the statement handle when using 
ODBC. Again, I'd like to refer to task #131011 which, IMHO, relates right 
to this issue. Perhaps, someone out in QUniverse can throw light on this 
question.

Regards

Michael Hertling

--
 [ signature omitted ] 

Message 8 in thread

Hi Dimitri!

On Sun, 25 Feb 2007, Dimitri wrote:

>> Obviously, this should be the connection the QSqlQuery was created for 
>> initially, i.e. the one mentioned in the constructor and, casually, still 
>> persisting even after it has been closed.
>
> Indeed, but how do you identify a connection? For example there could be two 
> connections to the same database in a program [...]

Do you mean

- two different connections, each created by QSqlDatabase::addDatabase()
   with different connection names, but addressing the same database, or
- two different instances of QSqlDatabase, e.g. each retrieved using
   QSqlDatabase::database() with the same connection name each time?

In both cases, one will have two different QSqlDatabase objects, but in 
the first case, each of them refers to a different database connection 
whereas, in the latter case, both represent the same single connection.
To my knowledge, this will make a fundamental difference in some ways.

> [..] If one of the 
> connections is killed, should you re-use the other connection or create 
> another one?

What do you mean exactly with "killing" a connection?

- Closing with QSqlDatabase::close(), or
- Removing with QSqlDatabase::removeDatabase() ?

> What if a password is needed for the connection?

If the connection was just closed it still exists and can be retrieved 
with QSqlDatabase::database(). The password is either stored within the 
returned QSqlDatabase object - accessed by QSqlDatabase::password() - or 
it must be provided in the call of QSqlDatabase::open(). However, if the 
connection was removed one wouldn't expect QSqlQuery having a chance for 
automatic reconnecting, but this is reasonable and documented behaviour.

> What if the database connection has been killed on purpose elsewhere in the 
> program? Then it should not be silently recreated.

If killing means removing here, I absolutely agree with you, but what if 
killing means closing? So, we return to the thread's original question.

> What if... OK, just playing devil's advocate :-)

The devil is using Qt, too? Hellish... ;)

Regards

Michael Hertling

--
 [ signature omitted ] 

Message 9 in thread

Hi George!

On Tue, 20 Feb 2007, George Brink wrote:

> You go against ODBC principles here. In pure ODBC you create handle for
> database connection, and later you create handle for SQL statement on top of
> database connection. Closing database connection effectively kills all
> statements based on this connection.

You're absolutely right. After reception of your reply I read the ODBC 
reference more closely and learned that SQLDisconnect() frees remaining 
statements allocated on the affected connection. An inspection of Qt's 
relating source code turns out that, ultimately, QSqlQuery allocates an 
ODBC statement handle which isn't reallocated by QSqlQuery:exec() while 
QSqlDatabase::close() invokes SQLDisconnect() without being aware of the 
connection's still pending QSqlQuery objects. Thus, QSqlQuery::exec() on 
any closed and reopened ODBC connection most certainly leads to an access 
violation.

> So, in your example query.exec() SHOULD fail with exception on the second
> run... or it should silently 'recreate' itself, reattaching inner statement
> handle to a new database handle. I am not sure which way would be more
> "QT-way".

Of course, such a kind of self-healing would be most elegant, but as far 
as I can see, this requires a tighter interconnection of QSqlDatabase and 
QSqlQuery. Perhaps it can be addressed in the course of task #131011 which 
relates to this subject, too, and, regarding its history, reveals that the 
relation between QSqlDatabase and QSqlQuery is a rather subtle one... ;-)

> And yes, recreating QSqlQuery object or objects each time you reconnect to
> database and destroying them each time you lose connection to database is a
> correct way to go from ODBC-driver point of view.

Indeed, for the time being, this appears to be the preferable way to have 
applications recover lost database connections independent of the actual 
backend and without subsequent crashing.

> [...] Hope it will help.

It did. Thank you very much for sharing your knowledge.

Regards

Michael Hertling

--
 [ signature omitted ] 

Message 10 in thread

Hi,

Michael Hertling wrote:
>     QApplication app(argc,argv);
>     QSqlDatabase db = QSqlDatabase::addDatabase("QODBC");
>     db.setDatabaseName("...YOUR DATABASE HERE...");
>     if (!db.open()) {
>         std::cerr << "ERROR OPENING DATABASE\n";
>         exit(1);
>     }
> 
>     std::cerr << "FIRST QUERY... ";
>     QSqlQuery query("select 0");

Here you are using this constructor:
	http://doc.trolltech.com/4.2/qsqlquery.html#QSqlQuery-2
	If db is not specified, the application's default
	database is used.

What if you specify the database? It shouldn't make a difference but you 
never know:
      QSqlQuery query("select 0", db);

Otherwise I tend to agree with George Brink, it doesn't sound like a 
good idea to kill a database connection and expect queries to survive 
that. Still, the documentation could maybe warn against that.

--
 [ signature omitted ] 

Message 11 in thread

Hi Dimitri!

On Thu, 22 Feb 2007, Dimitri wrote:

>>     QApplication app(argc,argv);
>>     QSqlDatabase db = QSqlDatabase::addDatabase("QODBC");
>>     db.setDatabaseName("...YOUR DATABASE HERE...");
>>     if (!db.open()) {
>>         std::cerr << "ERROR OPENING DATABASE\n";
>>         exit(1);
>>     }
>>
>>     std::cerr << "FIRST QUERY... ";
>>     QSqlQuery query("select 0");
>
> Here you are using this constructor:
> 	http://doc.trolltech.com/4.2/qsqlquery.html#QSqlQuery-2
> 	If db is not specified, the application's default
> 	database is used.
>
> What if you specify the database? It shouldn't make a difference but you 
> never know:
>     QSqlQuery query("select 0", db);

Lately, I gave it a try, but it didn't bring a change: Crash happens...

> Otherwise I tend to agree with George Brink, it doesn't sound like a good 
> idea to kill a database connection and expect queries to survive that. Still, 
> the documentation could maybe warn against that.

Yes, I agree, especially because the workaround isn't difficult: Simply 
create the queries on the heap, but don't forget to delete them finally. 
Nevertheless, having them exist on the stack would mean to be with Qt's 
elegance, and, as I mentioned in the thread's initial posting, the QPSQL 
driver is capable for this: A QSqlQuery object remains usable after its 
QSqlDatabase was closed and reopened. For the time being, this shouldn't 
be considered as assured behaviour, but perhaps the future will bring an 
improvement (task #131011).

Thank you very much for your reply.

Regards

Michael Hertling

--
 [ signature omitted ]