Fix sqlite driver memory eating due to close failure
authorHonglei Zhang <honglei.zhang@nokia.com>
Tue, 29 Nov 2011 11:00:16 +0000 (13:00 +0200)
committerQt by Nokia <qt-info@nokia.com>
Wed, 30 Nov 2011 12:53:24 +0000 (13:53 +0100)
If an ongoing query is not finalized before close function is
called, sqlite driver still tries to close the connection to
sqlite. In this case, sqlite reports an error to sqlite driver
which is not reported to the client. The failure in close causes
connection to sqlite unclosed and memory is not freed. This
fix tries to finalize all queries before close function is called.
The close function should succeed.

Task-number: QTBUG-16967
Change-Id: I2f10a2a9017446a9d44b693b00464a89625e3602
Reviewed-by: Yunqiao Yin <charles.yin@nokia.com>
src/sql/drivers/sqlite/qsql_sqlite.cpp
tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp

index 8294a55..38e4a63 100644 (file)
@@ -104,6 +104,7 @@ class QSQLiteDriverPrivate
 public:
     inline QSQLiteDriverPrivate() : access(0) {}
     sqlite3 *access;
+    QList <QSQLiteResult *> results;
 };
 
 
@@ -286,10 +287,12 @@ QSQLiteResult::QSQLiteResult(const QSQLiteDriver* db)
 {
     d = new QSQLiteResultPrivate(this);
     d->access = db->d->access;
+    db->d->results.append(this);
 }
 
 QSQLiteResult::~QSQLiteResult()
 {
+    qobject_cast<const QSQLiteDriver *>(driver())->d->results.removeOne(this);
     d->cleanup();
     delete d;
 }
@@ -553,6 +556,10 @@ bool QSQLiteDriver::open(const QString & db, const QString &, const QString &, c
 void QSQLiteDriver::close()
 {
     if (isOpen()) {
+        foreach (QSQLiteResult *result, d->results) {
+            result->d->finalize();
+        }
+
         if (sqlite3_close(d->access) != SQLITE_OK)
             setLastError(qMakeError(d->access, tr("Error closing database"),
                                     QSqlError::ConnectionError));
index df3a4dd..cb3bb81 100644 (file)
@@ -215,6 +215,8 @@ private slots:
     void QTBUG_14132();
     void QTBUG_21884_data() { generic_data("QSQLITE"); }
     void QTBUG_21884();
+    void QTBUG_16967_data() { generic_data("QSQLITE"); }
+    void QTBUG_16967(); //clean close
 
     void sqlite_constraint_data() { generic_data("QSQLITE"); }
     void sqlite_constraint();
@@ -3129,6 +3131,53 @@ void tst_QSqlQuery::QTBUG_21884()
     }
 }
 
+/**
+  * This test case test sqlite driver close function. Sqlite driver should close cleanly
+  * even if there is still outstanding prepared statement.
+  */
+void tst_QSqlQuery::QTBUG_16967()
+{
+    QFETCH(QString, dbName);
+    {
+        QSqlDatabase db = QSqlDatabase::database(dbName);
+        CHECK_DATABASE(db);
+        db.close();
+        QCOMPARE(db.lastError().type(), QSqlError::NoError);
+    }
+    {
+        QSqlDatabase db = QSqlDatabase::database(dbName);
+        CHECK_DATABASE(db);
+        QSqlQuery q(db);
+        q.prepare("CREATE TABLE t1 (id INTEGER PRIMARY KEY, str TEXT);");
+        db.close();
+        QCOMPARE(db.lastError().type(), QSqlError::NoError);
+    }
+    {
+        QSqlDatabase db = QSqlDatabase::database(dbName);
+        CHECK_DATABASE(db);
+        QSqlQuery q(db);
+        q.prepare("CREATE TABLE t1 (id INTEGER PRIMARY KEY, str TEXT);");
+        q.exec();
+        db.close();
+        QCOMPARE(db.lastError().type(), QSqlError::NoError);
+    }
+    {
+        QSqlDatabase db = QSqlDatabase::database(dbName);
+        CHECK_DATABASE(db);
+        QSqlQuery q(db);
+        q.exec("INSERT INTO t1 (id, str) VALUES(1, \"test1\");");
+        db.close();
+        QCOMPARE(db.lastError().type(), QSqlError::NoError);
+    }
+    {
+        QSqlDatabase db = QSqlDatabase::database(dbName);
+        CHECK_DATABASE(db);
+        QSqlQuery q(db);
+        q.exec("SELECT * FROM t1;");
+        db.close();
+        QCOMPARE(db.lastError().type(), QSqlError::NoError);
+    }
+}
 
 void tst_QSqlQuery::oraOCINumber()
 {