Соединение все еще открыто после того, как QSqlDatabase выходит за рамки

Почему следующий код выводит true, а не 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();
}

Согласно примеру № 2 в первом ответе на Каков правильный способ QSqlDatabase & QSqlQuery? соединение с базой данных закрывается, когда объект QSqlDatabase выходит за пределы области действия. Таким образом, когда db1 выходит за рамки в конце openDatabase(), соединение должно быть закрыто.

(Что на самом деле происходит, немного более тонко. Внутри QSqlDatabase поддерживает счетчик ссылок QSqlDatabase объектов, которые он вернул для определенного соединения. Счетчик ссылок уменьшается на ~QSqlDatabase, и когда он достигает 0, соединение закрывается. Однако, это ничего не должно изменить.Счетчик ссылок должен быть равен 1 после первой строки в openDatabase() и 0 после завершения функции и уничтожения db1.)

Что мне не хватает?


person Ronald Bourret    schedule 27.01.2018    source источник
comment
в соответствии с вашим кодом openDatabase() будет иметь глобальную область действия.. поэтому ваш код соответствует примеру № 1 в ссылке.   -  person Mohammad Kanan    schedule 27.01.2018
comment
Я смущен. Хотя openDatabase() имеет глобальную область действия, db1 является локальным для openDatabase(), и его деструктор вызывается при возврате функции. В примере № 1 db имеет то же время жизни, что и ваше приложение (например, выделено в main()), или является глобальной переменной. Его деструктор не вызывается до завершения работы приложения.   -  person Ronald Bourret    schedule 28.01.2018


Ответы (1)


Вы правы, согласно QTBUG-17140, несмотря на то, что сценарий ошибки немного отличается, проблема общая, так как существуют проблемы с реализацией isOpen() для проверки активности соединения с базой данных, об этом сообщалось до Qt 5.3.1, отчет об ошибке не показывает, что проблема решена.

person Mohammad Kanan    schedule 27.01.2018
comment
При всем уважении, я не думаю, что это связано с упомянутой вами ошибкой, так как соединение никак не прерывается. Я бы объяснил это так: в openDatabase() соединение foo открывается с помощью локальной переменной db1. Когда openDatabase заканчивается, db1 действительно выходит из области видимости и переменная уничтожается, хотя соединение остается активным и открытым до тех пор, пока оно не будет закрыто явным образом с помощью QSqlDatabase::close. Я понимаю документацию QSqlDatabase так, что это на самом деле ожидаемое поведение, и использую его в своих программах именно так. - person scopchanov; 28.01.2018
comment
Просто дополнение к соединению остается активным и открытым - и всегда доступно (я имею в виду в других методах) через его имя (foo в данном случае), а не через переменную. - person scopchanov; 28.01.2018
comment
@scopchanov, вы имеете в виду случай № 1 в связанном сообщении SO, о котором мы знаем, проблема здесь другая, и она такая же, как случай № 2 в связанном сообщении. Кроме того, прочитайте второе предупреждение в документации Предупреждение: Если вы добавите соединение с тем же именем, что и существующее соединение... - person Mohammad Kanan; 28.01.2018
comment
Я согласен с вами по поводу предупреждения. Однако QSqlDatabase db2 = QSqlDatabase::database("foo", false); не добавляет новое соединение, а делает ссылку на существующее, которое уже открыто. Итак, для меня ответ на вопрос OP Почему следующий код печатает true, а не false? is - потому что соединение открыто и оно должно вернуть true. - person scopchanov; 28.01.2018
comment
@scopchanov, в документации четко упоминается, что db/query, определенные в локальной области {}, после этого уничтожаются, потому что они выходят за рамки. - person Mohammad Kanan; 28.01.2018
comment
Если вы имеете в виду эту строку из примера: // И db, и запрос уничтожаются, потому что они находятся вне области видимости, это говорит о том, что переменные уничтожены, а не о том, что соединение закрыто. - person scopchanov; 28.01.2018
comment
@scopchanov, а как вы можете открыть соединение, когда экземпляры уничтожены? - person Mohammad Kanan; 28.01.2018
comment
@scopchanov, я согласен, что ошибка не имеет отношения. (Хотя я не включил код, я могу успешно подключиться к базе данных.) До сих пор не ясно, почему соединение остается открытым. Согласно документации для ~QSqlDatabase, деструктор вызывает close() при уничтожении последнего соединения. Если вы посмотрите на код на GitHub, деструктор уменьшает счетчик ссылок, а затем проверяет, равен ли он 0. Поскольку close() не вызывается, это означает, что счетчик ссылок равен ›0. Откуда взялись дополнительные ссылки? - person Ronald Bourret; 28.01.2018
comment
@RonaldBourret, когда последнее соединение разрушено. Уничтожение переменной не уничтожает/закрывает соединение. QSqlDatabase::close закрывает соединение, а QSqlDatabase::removeDatabase уничтожает его. - person scopchanov; 28.01.2018
comment
@MohammadKanan, если честно, я мог только догадываться, почему это сделано именно так, однако это факт, что соединение остается в памяти после уничтожения указывающей на него переменной, и ничто в документации не показывает, что это не то, что ожидалось поведение. - person scopchanov; 28.01.2018
comment
@scopchanov, само подключение не обсуждается, у него есть время жизни приложения, это не путать. Однако само соединение не открывает базу данных до тех пор, пока вы не open() с экземпляром... и оно закрывается, когда этот экземпляр выходит за рамки - person Mohammad Kanan; 28.01.2018
comment
@MohammadKanan, я думаю, вы ошибаетесь: он закрывается, когда этот экземпляр выходит за рамки. Он не закрывается, когда переменная, указывающая на него, уничтожается, выходя за пределы области видимости. - person scopchanov; 28.01.2018
comment
@scopchanov, обратитесь к db1 в примере Рональда Бурре, это db1, который содержит имя пользователя/пароль и имя базы данных для этого экземпляра.. теперь представьте, что происходит, когда db1 уничтожается` ?? как приложение может ЗНАТЬ, как получить доступ к базе данных? обратите внимание, что они хранятся не в самом соединении, а в экземпляре. - person Mohammad Kanan; 28.01.2018
comment
@MohammadKanan, приложение знает имя соединения и, следовательно, может создать новую ссылку, указав это конкретное имя. Если имя не указано, используется соединение по умолчанию. - person scopchanov; 28.01.2018
comment
@scopchanov, какое имя?! это было бы ошибкой, мой друг! - person Mohammad Kanan; 28.01.2018
comment
@RonaldBourret, могу я попросить вас сделать тест. После qDebug() << db2.isOpen(); покажите нам, например, результат qDebug() << db2.userName(). - person scopchanov; 28.01.2018
comment
@MohammadKanan, foo - это имя соединения, и вы можете получить доступ к соединению в любом месте своего кода, если знаете это имя. Это не ошибка. Это намеренно так сделано. - person scopchanov; 28.01.2018
comment
@MohammadKanan, если вы дадите мне пару минут, я могу подготовить пример с SQLite, чтобы его было легко проверить. - person scopchanov; 28.01.2018
comment
@scopchanov, я сделал это как часть моего первоначального тестирования. Он печатает правильное имя. Если вы посмотрите на реализацию QSqlDatabase, вы увидите, что она хранит статические QHash объектов соединения (QSqlDatabase), которые включают всю информацию о соединении. Именно это позволяет работать таким методам, как QSqlDatabase::database()::addDatabase добавляет объект в эту хэш-таблицу, ::database() извлекает объекты, а ::removeDatabase() удаляет объекты, — а также позволяет приложениям повторно открывать соединения без повторного ввода этой информации. - person Ronald Bourret; 28.01.2018
comment
Меня смущает подсчет ссылок на QSqlDatabase объектов. Пример № 2 из ссылки гласит, что соединение закрывается, когда соединение выходит за пределы области действия — очевидная ссылка на dtor, вызывающий close(). В моем коде это не так, то есть счетчик ссылок не равен 0, и я не могу понять, почему. - person Ronald Bourret; 28.01.2018
comment
@RonaldBourret, вы имеете в виду п.2 из принятого ответа, верно? Эта строка: // если нет другого открытого соединения с этим именем соединения, // соединение закрывается, когда db выходит за пределы области видимости? - person scopchanov; 28.01.2018
comment
@scopchanov, на самом деле я имел в виду п. 1 (db закрывается, когда выходит за рамки), но и п. 1, и п. 2 означают одно и то же. - person Ronald Bourret; 28.01.2018
comment
@RonaldBourret, позвольте мне провести тест, и я вернусь к вам с результатом. - person scopchanov; 28.01.2018
comment
Спасибо. Я не смогу ответить до понедельника. - person Ronald Bourret; 28.01.2018
comment
@RonaldBourret, :) это понятно. Хороших выходных вам обоим! - person scopchanov; 28.01.2018