2

Why does the following code print true instead of false?

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    if (!openDatabase()) return 1;

    // false means don't open closed connections.
    QSqlDatabase db2 = QSqlDatabase::database("foo", false);

    qDebug() << db2.isOpen();
    return 0;
}

bool openDatabase()
{
    QSqlDatabase db1 = QSqlDatabase::addDatabase("QPSQL", "foo");
    db1.setHostName("localhost");
    db1.setUserName("user");
    db1.setPassword("password");
    db1.setDatabaseName("bar");

    return db1.open();
}

According to example #2 in the first answer to What is the correct way of QSqlDatabase & QSqlQuery?, the database connection is closed when the QSqlDatabase object goes out of scope. Thus, when db1 goes out of scope at the end of openDatabase(), the connection should be closed.

(What actually happens is a bit more subtle. Internally, QSqlDatabase maintains a reference count of the QSqlDatabase objects it has returned for a particular connection. The reference count is decremented by ~QSqlDatabase and, when it reaches 0, the connection is closed. However, this should not change anything. The reference count should be 1 after the first line in openDatabase() and 0 after the function ends and db1 is destroyed.)

What am I missing?

  • according to your code, `openDatabase()` will have global scope .. so in effect your code follows example #1 in the reference. – Mohammad Kanan Jan 27 '18 at 20:28
  • I'm confused. Although `openDatabase()` has global scope, `db1` is local to `openDatabase()` and its destructor is called when the function returns. In example #1, `db` has "the same lifetime as your application" (e.g. is allocated in `main()`) or is "a global variable". Its destructor is not called until the application ends. – Ronald Bourret Jan 27 '18 at 21:18

1 Answers1

1

You are right, according to QTBUG-17140, despite that the scenario in the bug is slightly different, the problem is general as there are issues with implementing isOpen() to check if database connection is active, it was reported until Qt 5.3.1 , the bug report does not show it was solved.

Mohammad Kanan
  • 4,452
  • 10
  • 23
  • 47
  • With no disrespect, I don't think that this is related to the bug you've mentioned, since the connection is not interrupted in any way. I would explain it as follows: In `openDatabase()` a connection _foo_ is opened using the local variable `db1`. When `openDatabase` ends, `db1` indeed goes out of scope and the variable is destroyed, though the connection remains active and open until it is closed explicitly with QSqlDatabase::close. I understand the documentation of _QSqlDatabase_ so, that this is actually the expected behaviour and use it in my programs exactly this way. – scopchanov Jan 27 '18 at 22:20
  • Just an addition to "the connection remains active and open" - and is always accessible (I mean in other methods) through its name (_foo_ in this case), not through the variable. – scopchanov Jan 27 '18 at 22:26
  • @scopchanov, you are referring to case #1 on the linked SO post, which we know, the problem here is different and it is same as case#2 in linked post. Moreover, read second Warning in the documentation **Warning**: If you add a connection with the same name as an existing connection ... – Mohammad Kanan Jan 27 '18 at 22:34
  • I agree with you about the warning. However `QSqlDatabase db2 = QSqlDatabase::database("foo", false);` doesn't add a new connection, but makes a reference to the existing one, which is already open. So, for me the answer to the question of the OP "Why does the following code print true instead of false?" is - because the connection is open and it should return true. – scopchanov Jan 27 '18 at 22:39
  • @scopchanov, the documentation clearly mentions that a "db"/"query" defined in a _local scope_ {} are destroyed after that because they are out of scope. – Mohammad Kanan Jan 27 '18 at 22:46
  • If you mean this line from the example: "// Both "db" and "query" are destroyed because they are out of scope", it says that the variables are destroyed, not that the connection is closed. – scopchanov Jan 27 '18 at 22:50
  • @scopchanov, and how you can have a connection open when _instances_ are destroyed? – Mohammad Kanan Jan 27 '18 at 22:53
  • @scopchanov, I agree that the bug is unrelated. (Although I didn't include the code, I can successfully connect to the database.) It is still not clear why the connection stays open. According to the documentation for `~QSqlDatabase`, the destructor calls `close()` when the last connection is destroyed. If you look at the code on GitHub, the destructor decrements the reference count, then checks if it is 0. Since `close()` does not appear to be being called, this means the reference count is >0. Where did the additional reference(s) come from? – Ronald Bourret Jan 27 '18 at 22:53
  • @RonaldBourret, "when the last connection is destroyed". Destroying the variable doesn't destroy/close the connection. QSqlDatabase::close closes the connection and QSqlDatabase::removeDatabase destroys it. – scopchanov Jan 27 '18 at 22:57
  • @MohammadKanan, to be honest I could just guess why it is made like this, however, it is a fact that the connection remains in the memory after the variable pointing to it is destroyed and nothing in the documentation shows that this is not the expected behavior. – scopchanov Jan 27 '18 at 22:59
  • @scopchanov, the connection itself is not under discussion, it has application lifetime, this is not to be confused. However the connection itself does not open the database until you `open()` it with an instance .. and it closes when that instance is out of scope – Mohammad Kanan Jan 27 '18 at 23:04
  • @MohammadKanan, the part I think you mistaken is: "it closes when that instance is out of scope". It does not close when the variable, pointing to it is destroyed by going out of scope. – scopchanov Jan 27 '18 at 23:06
  • @scopchanov, refer to db1 in Ronald Bourret example its db1 that holds the username/password and database name for that instance .. now imagine what happens when db1 is destroyed` ?? how can the application KNOW how to access the database? note that these are not stored in the connection itself, but in the instance. – Mohammad Kanan Jan 27 '18 at 23:10
  • @MohammadKanan, the application knows the name of the connection and thus could create a new reference, providing this particular name. When no name is provided, the default connection is referred. – scopchanov Jan 27 '18 at 23:12
  • @scopchanov, which name?! this would have been a bug my friend! – Mohammad Kanan Jan 27 '18 at 23:14
  • @RonaldBourret, may I ask you to make a test. After `qDebug() << db2.isOpen();` show us the result of `qDebug() << db2.userName()` for example. – scopchanov Jan 27 '18 at 23:15
  • @MohammadKanan, "foo" is the connection name and you may access the connection anywhere in your code when you know the name. It is not a bug. This is intentionaly done like that. – scopchanov Jan 27 '18 at 23:18
  • @MohammadKanan, when you give me a couple of minutes, I could prepare an example with SQLite to be easily checkable. – scopchanov Jan 27 '18 at 23:20
  • 1
    @scopchanov, I did that as part of my original testing. It prints the correct name. If you look at the implementation of `QSqlDatabase`, you will see that it keeps a static `QHash` of connection (`QSqlDatabase`) objects, which include all the connection information. This is what allows methods like `QSqlDatabase::database()` to work -- `::addDatabase` adds object to this hash table, `::database()` retrieves objects, and `::removeDatabase()` removes objects -- and also allows applications to reopen connections without reentering this information. – Ronald Bourret Jan 27 '18 at 23:27
  • 1
    Where I am confused is with the reference counting on `QSqlDatabase` objects. Example #2 from the link states that the connection is closed when the connection goes out of scope -- an apparent reference to the dtor calling `close()`. In my code, this is not the case, meaning the reference count is not 0, and I can't figure out why. – Ronald Bourret Jan 27 '18 at 23:33
  • @RonaldBourret, you mean p.2 from the accepted answer, right? This line: "// if there is no other connection open with that connection name, // the connection is closed when db goes out of scope" ? – scopchanov Jan 27 '18 at 23:41
  • @scopchanov, I was actually referring to p.1 ("db is closed when it goes out of scope"), but both p.1 and p.2 mean the same thing. – Ronald Bourret Jan 27 '18 at 23:45
  • @RonaldBourret, let me do a test and I will get back to you with the result. – scopchanov Jan 27 '18 at 23:47
  • 1
    Thanks. I may not be able to reply until Monday. – Ronald Bourret Jan 27 '18 at 23:50
  • @RonaldBourret, :) that is understandable. Have a nice weekend, both of you! – scopchanov Jan 27 '18 at 23:52