Author: Armin Rigo <[email protected]>
Branch: 
Changeset: r76677:ba063d80a04f
Date: 2015-04-01 15:25 +0200
http://bitbucket.org/pypy/pypy/changeset/ba063d80a04f/

Log:    Test and fix for issue #2006

diff --git a/lib_pypy/_sqlite3.py b/lib_pypy/_sqlite3.py
--- a/lib_pypy/_sqlite3.py
+++ b/lib_pypy/_sqlite3.py
@@ -454,6 +454,7 @@
         self.__cursors_counter = 0
         self.__statements = []
         self.__statements_counter = 0
+        self.__rawstatements = set()
         self._statement_cache = _StatementCache(self, cached_statements)
 
         self.__func_cache = {}
@@ -483,6 +484,14 @@
 
         self.__do_all_statements(Statement._finalize, True)
 
+        # depending on when this close() is called, the statements' weakrefs
+        # may be already dead, even though Statement.__del__() was not called
+        # yet.  In this case, self.__rawstatements is not empty.
+        if self.__rawstatements is not None:
+            for stmt in list(self.__rawstatements):
+                self._finalize_raw_statement(stmt)
+            self.__rawstatements = None
+
         if self._db:
             ret = _lib.sqlite3_close(self._db)
             if ret != _lib.SQLITE_OK:
@@ -562,6 +571,7 @@
         self.__cursors = [r for r in self.__cursors if r() is not None]
 
     def _remember_statement(self, statement):
+        self.__rawstatements.add(statement._statement)
         self.__statements.append(weakref.ref(statement))
         self.__statements_counter += 1
         if self.__statements_counter < 200:
@@ -569,6 +579,11 @@
         self.__statements_counter = 0
         self.__statements = [r for r in self.__statements if r() is not None]
 
+    def _finalize_raw_statement(self, _statement):
+        if self.__rawstatements is not None:
+            self.__rawstatements.remove(_statement)
+            _lib.sqlite3_finalize(_statement)
+
     def __do_all_statements(self, action, reset_cursors):
         for weakref in self.__statements:
             statement = weakref()
@@ -1199,7 +1214,6 @@
 
     def __init__(self, connection, sql):
         self.__con = connection
-        self.__con._remember_statement(self)
 
         self._in_use = False
 
@@ -1232,6 +1246,7 @@
         ret = _lib.sqlite3_prepare_v2(self.__con._db, c_sql, -1,
                                       statement_star, next_char)
         self._statement = statement_star[0]
+        self.__con._remember_statement(self)
 
         if ret == _lib.SQLITE_OK and not self._statement:
             # an empty statement, work around that, as it's the least trouble
@@ -1250,11 +1265,11 @@
 
     def __del__(self):
         if self._statement:
-            _lib.sqlite3_finalize(self._statement)
+            self.__con._finalize_raw_statement(self._statement)
 
     def _finalize(self):
         if self._statement:
-            _lib.sqlite3_finalize(self._statement)
+            self.__con._finalize_raw_statement(self._statement)
             self._statement = None
         self._in_use = False
 
diff --git a/pypy/module/test_lib_pypy/test_sqlite3.py 
b/pypy/module/test_lib_pypy/test_sqlite3.py
--- a/pypy/module/test_lib_pypy/test_sqlite3.py
+++ b/pypy/module/test_lib_pypy/test_sqlite3.py
@@ -276,6 +276,32 @@
         exc = raises(ValueError, cur.execute, "select 2\0")
         assert str(exc.value) == "the query contains a null character"
 
+        import sqlite3
+
+    def test_close_in_del_ordering(self):
+        import gc
+        class SQLiteBackend(object):
+            success = False
+            def __init__(self):
+                self.connection = _sqlite3.connect(":memory:")
+            def close(self):
+                self.connection.close()
+            def __del__(self):
+                self.close()
+                SQLiteBackend.success = True
+            def create_db_if_needed(self):
+                conn = self.connection
+                cursor = conn.cursor()
+                cursor.execute("""
+                    create table if not exists nameoftable(value text)
+                """)
+                cursor.close()
+                conn.commit()
+        SQLiteBackend().create_db_if_needed()
+        gc.collect()
+        gc.collect()
+        assert SQLiteBackend.success
+
 
 class TestSQLiteHost(BaseTestSQLite):
     def setup_class(cls):
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to