mirror of
https://github.com/python/cpython.git
synced 2025-11-01 14:11:41 +00:00
gh-105539: Explict resource management for connection objects in sqlite3 tests (#108017)
- Use memory_database() helper - Move test utility functions to util.py - Add convenience memory database mixin - Add check() helper for closed connection tests
This commit is contained in:
parent
c9d83f93d8
commit
1344cfac43
9 changed files with 371 additions and 385 deletions
|
|
@ -28,15 +28,12 @@
|
|||
|
||||
from test import support
|
||||
from unittest.mock import patch
|
||||
from test.test_sqlite3.test_dbapi import memory_database, cx_limit
|
||||
|
||||
from .util import memory_database, cx_limit
|
||||
from .util import MemoryDatabaseMixin
|
||||
|
||||
|
||||
class RegressionTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.con = sqlite.connect(":memory:")
|
||||
|
||||
def tearDown(self):
|
||||
self.con.close()
|
||||
class RegressionTests(MemoryDatabaseMixin, unittest.TestCase):
|
||||
|
||||
def test_pragma_user_version(self):
|
||||
# This used to crash pysqlite because this pragma command returns NULL for the column name
|
||||
|
|
@ -45,28 +42,24 @@ def test_pragma_user_version(self):
|
|||
|
||||
def test_pragma_schema_version(self):
|
||||
# This still crashed pysqlite <= 2.2.1
|
||||
con = sqlite.connect(":memory:", detect_types=sqlite.PARSE_COLNAMES)
|
||||
try:
|
||||
with memory_database(detect_types=sqlite.PARSE_COLNAMES) as con:
|
||||
cur = self.con.cursor()
|
||||
cur.execute("pragma schema_version")
|
||||
finally:
|
||||
cur.close()
|
||||
con.close()
|
||||
|
||||
def test_statement_reset(self):
|
||||
# pysqlite 2.1.0 to 2.2.0 have the problem that not all statements are
|
||||
# reset before a rollback, but only those that are still in the
|
||||
# statement cache. The others are not accessible from the connection object.
|
||||
con = sqlite.connect(":memory:", cached_statements=5)
|
||||
cursors = [con.cursor() for x in range(5)]
|
||||
cursors[0].execute("create table test(x)")
|
||||
for i in range(10):
|
||||
cursors[0].executemany("insert into test(x) values (?)", [(x,) for x in range(10)])
|
||||
with memory_database(cached_statements=5) as con:
|
||||
cursors = [con.cursor() for x in range(5)]
|
||||
cursors[0].execute("create table test(x)")
|
||||
for i in range(10):
|
||||
cursors[0].executemany("insert into test(x) values (?)", [(x,) for x in range(10)])
|
||||
|
||||
for i in range(5):
|
||||
cursors[i].execute(" " * i + "select x from test")
|
||||
for i in range(5):
|
||||
cursors[i].execute(" " * i + "select x from test")
|
||||
|
||||
con.rollback()
|
||||
con.rollback()
|
||||
|
||||
def test_column_name_with_spaces(self):
|
||||
cur = self.con.cursor()
|
||||
|
|
@ -81,17 +74,15 @@ def test_statement_finalization_on_close_db(self):
|
|||
# cache when closing the database. statements that were still
|
||||
# referenced in cursors weren't closed and could provoke "
|
||||
# "OperationalError: Unable to close due to unfinalised statements".
|
||||
con = sqlite.connect(":memory:")
|
||||
cursors = []
|
||||
# default statement cache size is 100
|
||||
for i in range(105):
|
||||
cur = con.cursor()
|
||||
cur = self.con.cursor()
|
||||
cursors.append(cur)
|
||||
cur.execute("select 1 x union select " + str(i))
|
||||
con.close()
|
||||
|
||||
def test_on_conflict_rollback(self):
|
||||
con = sqlite.connect(":memory:")
|
||||
con = self.con
|
||||
con.execute("create table foo(x, unique(x) on conflict rollback)")
|
||||
con.execute("insert into foo(x) values (1)")
|
||||
try:
|
||||
|
|
@ -126,16 +117,16 @@ def test_type_map_usage(self):
|
|||
a statement. This test exhibits the problem.
|
||||
"""
|
||||
SELECT = "select * from foo"
|
||||
con = sqlite.connect(":memory:",detect_types=sqlite.PARSE_DECLTYPES)
|
||||
cur = con.cursor()
|
||||
cur.execute("create table foo(bar timestamp)")
|
||||
with self.assertWarnsRegex(DeprecationWarning, "adapter"):
|
||||
cur.execute("insert into foo(bar) values (?)", (datetime.datetime.now(),))
|
||||
cur.execute(SELECT)
|
||||
cur.execute("drop table foo")
|
||||
cur.execute("create table foo(bar integer)")
|
||||
cur.execute("insert into foo(bar) values (5)")
|
||||
cur.execute(SELECT)
|
||||
with memory_database(detect_types=sqlite.PARSE_DECLTYPES) as con:
|
||||
cur = con.cursor()
|
||||
cur.execute("create table foo(bar timestamp)")
|
||||
with self.assertWarnsRegex(DeprecationWarning, "adapter"):
|
||||
cur.execute("insert into foo(bar) values (?)", (datetime.datetime.now(),))
|
||||
cur.execute(SELECT)
|
||||
cur.execute("drop table foo")
|
||||
cur.execute("create table foo(bar integer)")
|
||||
cur.execute("insert into foo(bar) values (5)")
|
||||
cur.execute(SELECT)
|
||||
|
||||
def test_bind_mutating_list(self):
|
||||
# Issue41662: Crash when mutate a list of parameters during iteration.
|
||||
|
|
@ -144,11 +135,11 @@ def __conform__(self, protocol):
|
|||
parameters.clear()
|
||||
return "..."
|
||||
parameters = [X(), 0]
|
||||
con = sqlite.connect(":memory:",detect_types=sqlite.PARSE_DECLTYPES)
|
||||
con.execute("create table foo(bar X, baz integer)")
|
||||
# Should not crash
|
||||
with self.assertRaises(IndexError):
|
||||
con.execute("insert into foo(bar, baz) values (?, ?)", parameters)
|
||||
with memory_database(detect_types=sqlite.PARSE_DECLTYPES) as con:
|
||||
con.execute("create table foo(bar X, baz integer)")
|
||||
# Should not crash
|
||||
with self.assertRaises(IndexError):
|
||||
con.execute("insert into foo(bar, baz) values (?, ?)", parameters)
|
||||
|
||||
def test_error_msg_decode_error(self):
|
||||
# When porting the module to Python 3.0, the error message about
|
||||
|
|
@ -173,7 +164,7 @@ def upper(self):
|
|||
def __del__(self):
|
||||
con.isolation_level = ""
|
||||
|
||||
con = sqlite.connect(":memory:")
|
||||
con = self.con
|
||||
con.isolation_level = None
|
||||
for level in "", "DEFERRED", "IMMEDIATE", "EXCLUSIVE":
|
||||
with self.subTest(level=level):
|
||||
|
|
@ -204,8 +195,7 @@ class Cursor(sqlite.Cursor):
|
|||
def __init__(self, con):
|
||||
pass
|
||||
|
||||
con = sqlite.connect(":memory:")
|
||||
cur = Cursor(con)
|
||||
cur = Cursor(self.con)
|
||||
with self.assertRaises(sqlite.ProgrammingError):
|
||||
cur.execute("select 4+5").fetchall()
|
||||
with self.assertRaisesRegex(sqlite.ProgrammingError,
|
||||
|
|
@ -238,7 +228,9 @@ def test_auto_commit(self):
|
|||
2.5.3 introduced a regression so that these could no longer
|
||||
be created.
|
||||
"""
|
||||
con = sqlite.connect(":memory:", isolation_level=None)
|
||||
with memory_database(isolation_level=None) as con:
|
||||
self.assertIsNone(con.isolation_level)
|
||||
self.assertFalse(con.in_transaction)
|
||||
|
||||
def test_pragma_autocommit(self):
|
||||
"""
|
||||
|
|
@ -273,9 +265,7 @@ def test_recursive_cursor_use(self):
|
|||
Recursively using a cursor, such as when reusing it from a generator led to segfaults.
|
||||
Now we catch recursive cursor usage and raise a ProgrammingError.
|
||||
"""
|
||||
con = sqlite.connect(":memory:")
|
||||
|
||||
cur = con.cursor()
|
||||
cur = self.con.cursor()
|
||||
cur.execute("create table a (bar)")
|
||||
cur.execute("create table b (baz)")
|
||||
|
||||
|
|
@ -295,29 +285,30 @@ def test_convert_timestamp_microsecond_padding(self):
|
|||
since the microsecond string "456" actually represents "456000".
|
||||
"""
|
||||
|
||||
con = sqlite.connect(":memory:", detect_types=sqlite.PARSE_DECLTYPES)
|
||||
cur = con.cursor()
|
||||
cur.execute("CREATE TABLE t (x TIMESTAMP)")
|
||||
with memory_database(detect_types=sqlite.PARSE_DECLTYPES) as con:
|
||||
cur = con.cursor()
|
||||
cur.execute("CREATE TABLE t (x TIMESTAMP)")
|
||||
|
||||
# Microseconds should be 456000
|
||||
cur.execute("INSERT INTO t (x) VALUES ('2012-04-04 15:06:00.456')")
|
||||
# Microseconds should be 456000
|
||||
cur.execute("INSERT INTO t (x) VALUES ('2012-04-04 15:06:00.456')")
|
||||
|
||||
# Microseconds should be truncated to 123456
|
||||
cur.execute("INSERT INTO t (x) VALUES ('2012-04-04 15:06:00.123456789')")
|
||||
# Microseconds should be truncated to 123456
|
||||
cur.execute("INSERT INTO t (x) VALUES ('2012-04-04 15:06:00.123456789')")
|
||||
|
||||
cur.execute("SELECT * FROM t")
|
||||
with self.assertWarnsRegex(DeprecationWarning, "converter"):
|
||||
values = [x[0] for x in cur.fetchall()]
|
||||
cur.execute("SELECT * FROM t")
|
||||
with self.assertWarnsRegex(DeprecationWarning, "converter"):
|
||||
values = [x[0] for x in cur.fetchall()]
|
||||
|
||||
self.assertEqual(values, [
|
||||
datetime.datetime(2012, 4, 4, 15, 6, 0, 456000),
|
||||
datetime.datetime(2012, 4, 4, 15, 6, 0, 123456),
|
||||
])
|
||||
self.assertEqual(values, [
|
||||
datetime.datetime(2012, 4, 4, 15, 6, 0, 456000),
|
||||
datetime.datetime(2012, 4, 4, 15, 6, 0, 123456),
|
||||
])
|
||||
|
||||
def test_invalid_isolation_level_type(self):
|
||||
# isolation level is a string, not an integer
|
||||
self.assertRaises(TypeError,
|
||||
sqlite.connect, ":memory:", isolation_level=123)
|
||||
regex = "isolation_level must be str or None"
|
||||
with self.assertRaisesRegex(TypeError, regex):
|
||||
memory_database(isolation_level=123).__enter__()
|
||||
|
||||
|
||||
def test_null_character(self):
|
||||
|
|
@ -333,7 +324,7 @@ def test_null_character(self):
|
|||
cur.execute, query)
|
||||
|
||||
def test_surrogates(self):
|
||||
con = sqlite.connect(":memory:")
|
||||
con = self.con
|
||||
self.assertRaises(UnicodeEncodeError, con, "select '\ud8ff'")
|
||||
self.assertRaises(UnicodeEncodeError, con, "select '\udcff'")
|
||||
cur = con.cursor()
|
||||
|
|
@ -359,7 +350,7 @@ def test_commit_cursor_reset(self):
|
|||
to return rows multiple times when fetched from cursors
|
||||
after commit. See issues 10513 and 23129 for details.
|
||||
"""
|
||||
con = sqlite.connect(":memory:")
|
||||
con = self.con
|
||||
con.executescript("""
|
||||
create table t(c);
|
||||
create table t2(c);
|
||||
|
|
@ -391,10 +382,9 @@ def test_bpo31770(self):
|
|||
"""
|
||||
def callback(*args):
|
||||
pass
|
||||
con = sqlite.connect(":memory:")
|
||||
cur = sqlite.Cursor(con)
|
||||
cur = sqlite.Cursor(self.con)
|
||||
ref = weakref.ref(cur, callback)
|
||||
cur.__init__(con)
|
||||
cur.__init__(self.con)
|
||||
del cur
|
||||
# The interpreter shouldn't crash when ref is collected.
|
||||
del ref
|
||||
|
|
@ -425,6 +415,7 @@ def test_return_empty_bytestring(self):
|
|||
|
||||
def test_table_lock_cursor_replace_stmt(self):
|
||||
with memory_database() as con:
|
||||
con = self.con
|
||||
cur = con.cursor()
|
||||
cur.execute("create table t(t)")
|
||||
cur.executemany("insert into t values(?)",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue