2222
2323import contextlib
2424import functools
25+ import operator
2526import os
2627import sqlite3 as sqlite
2728import subprocess
3233import warnings
3334
3435from test .support import (
35- SHORT_TIMEOUT , check_disallow_instantiation , requires_subprocess
36+ SHORT_TIMEOUT , check_disallow_instantiation , requires_subprocess , subTests
3637)
3738from test .support import gc_collect
3839from test .support import threading_helper , import_helper
@@ -728,6 +729,27 @@ def test_database_keyword(self):
728729 self .assertEqual (type (cx ), sqlite .Connection )
729730
730731
732+ class CxWrapper :
733+ def __init__ (self , cx ):
734+ self .cx = cx
735+
736+
737+ class EvilParamsWithIter (CxWrapper ):
738+
739+ def __iter__ (self ):
740+ self .cx .close ()
741+ return iter ([(1 ,)])
742+
743+
744+ class EvilParamsWithNext (CxWrapper ):
745+ def __iter__ (self ):
746+ return self
747+
748+ def __next__ (self ):
749+ self .cx .close ()
750+ return (1 ,)
751+
752+
731753class CursorTests (unittest .TestCase ):
732754 def setUp (self ):
733755 self .cx = sqlite .connect (":memory:" )
@@ -742,6 +764,16 @@ def tearDown(self):
742764 self .cu .close ()
743765 self .cx .close ()
744766
767+ def do_test_connection_use_after_close (self , method_name , params_class ):
768+ # Prevent SIGSEGV with iterable of parameters closing the connection.
769+ # Regression test for https://github.com/python/cpython/issues/143198.
770+ cx = sqlite .connect (":memory:" )
771+ self .addCleanup (cx .close )
772+ cu = cx .cursor ()
773+ params = params_class (cx )
774+ method = operator .methodcaller (method_name , "SELECT ?" , params )
775+ self .assertRaises (sqlite .ProgrammingError , method , cu )
776+
745777 def test_execute_no_args (self ):
746778 self .cu .execute ("delete from test" )
747779
@@ -813,6 +845,10 @@ def test_execute_non_iterable(self):
813845 self .cu .execute ("insert into test(id) values (?)" , 42 )
814846 self .assertEqual (str (cm .exception ), 'parameters are of unsupported type' )
815847
848+ @subTests ("params_class" , (EvilParamsWithIter , EvilParamsWithNext ))
849+ def test_execute_use_after_close (self , params_class ):
850+ self .do_test_connection_use_after_close ("execute" , params_class )
851+
816852 def test_execute_wrong_no_of_args1 (self ):
817853 # too many parameters
818854 with self .assertRaises (sqlite .ProgrammingError ):
@@ -1030,6 +1066,10 @@ def test_execute_many_not_iterable(self):
10301066 with self .assertRaises (TypeError ):
10311067 self .cu .executemany ("insert into test(income) values (?)" , 42 )
10321068
1069+ @subTests ("params_class" , (EvilParamsWithIter , EvilParamsWithNext ))
1070+ def test_executemany_use_after_close (self , params_class ):
1071+ self .do_test_connection_use_after_close ("executemany" , params_class )
1072+
10331073 def test_fetch_iter (self ):
10341074 # Optional DB-API extension.
10351075 self .cu .execute ("delete from test" )
@@ -1645,6 +1685,15 @@ def tearDown(self):
16451685 self .cur .close ()
16461686 self .con .close ()
16471687
1688+ def do_test_connection_use_after_close (self , method_name , params_class ):
1689+ # Prevent SIGSEGV with iterable of parameters closing the connection.
1690+ # Regression test for https://github.com/python/cpython/issues/143198.
1691+ cx = sqlite .connect (":memory:" )
1692+ self .addCleanup (cx .close )
1693+ params = params_class (cx )
1694+ method = operator .methodcaller (method_name , "SELECT ?" , params )
1695+ self .assertRaises (sqlite .ProgrammingError , method , cx )
1696+
16481697 def test_script_string_sql (self ):
16491698 cur = self .cur
16501699 cur .executescript ("""
@@ -1711,6 +1760,10 @@ def test_connection_execute(self):
17111760 result = self .con .execute ("select 5" ).fetchone ()[0 ]
17121761 self .assertEqual (result , 5 , "Basic test of Connection.execute" )
17131762
1763+ @subTests ("params_class" , (EvilParamsWithIter , EvilParamsWithNext ))
1764+ def test_connection_execute_use_after_close (self , params_class ):
1765+ self .do_test_connection_use_after_close ("execute" , params_class )
1766+
17141767 def test_connection_executemany (self ):
17151768 con = self .con
17161769 con .execute ("create table test(foo)" )
@@ -1719,6 +1772,10 @@ def test_connection_executemany(self):
17191772 self .assertEqual (result [0 ][0 ], 3 , "Basic test of Connection.executemany" )
17201773 self .assertEqual (result [1 ][0 ], 4 , "Basic test of Connection.executemany" )
17211774
1775+ @subTests ("params_class" , (EvilParamsWithIter , EvilParamsWithNext ))
1776+ def test_connection_executemany_use_after_close (self , params_class ):
1777+ self .do_test_connection_use_after_close ("executemany" , params_class )
1778+
17221779 def test_connection_executescript (self ):
17231780 con = self .con
17241781 con .executescript ("""
0 commit comments