@@ -587,6 +587,33 @@ def value(self): return 1 << 65
587587 self .assertRaisesRegex (sqlite .DataError , "string or blob too big" ,
588588 self .cur .execute , self .query % "err_val_ret" )
589589
590+ def test_close_conn_in_window_func_value (self ):
591+ """gh-145040: closing connection in window function value() callback."""
592+ con = sqlite .connect (":memory:" , autocommit = True )
593+ con .execute ("CREATE TABLE t(x INTEGER)" )
594+ con .executemany ("INSERT INTO t VALUES(?)" ,
595+ [(i ,) for i in range (20 )])
596+
597+ class CloseConnWindow :
598+ def step (self , value ):
599+ pass
600+ def finalize (self ):
601+ return 0
602+ def value (self ):
603+ con .close ()
604+ return 0
605+ def inverse (self , value ):
606+ pass
607+
608+ con .create_window_function ("evil_win" , 1 , CloseConnWindow )
609+ msg = "from within a callback"
610+ with self .assertRaisesRegex (sqlite .ProgrammingError , msg ):
611+ cursor = con .execute (
612+ "SELECT evil_win(x) OVER "
613+ "(ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM t"
614+ )
615+ list (cursor )
616+
590617
591618class AggregateTests (unittest .TestCase ):
592619 def setUp (self ):
@@ -743,9 +770,78 @@ def finalize(self):
743770 return self .total
744771
745772 con .create_aggregate ("agg_close" , 1 , CloseConnAgg )
746- with self .assertRaises (sqlite .ProgrammingError ):
773+ msg = "from within a callback"
774+ with self .assertRaisesRegex (sqlite .ProgrammingError , msg ):
747775 con .execute ("SELECT agg_close(x) FROM t" )
748776
777+ def test_close_conn_in_udf_during_executemany (self ):
778+ """gh-145040: closing connection in UDF during executemany."""
779+ con = sqlite .connect (":memory:" , autocommit = True )
780+ con .execute ("CREATE TABLE t(x)" )
781+
782+ def close_conn (x ):
783+ con .close ()
784+ return x
785+
786+ con .create_function ("close_conn" , 1 , close_conn )
787+ msg = "from within a callback"
788+ with self .assertRaisesRegex (sqlite .ProgrammingError , msg ):
789+ con .executemany ("INSERT INTO t VALUES(close_conn(?))" ,
790+ [(i ,) for i in range (10 )])
791+
792+ def test_close_conn_in_progress_handler_during_iternext (self ):
793+ """gh-145040: closing connection in progress handler during iteration."""
794+ con = sqlite .connect (":memory:" , autocommit = True )
795+ con .execute ("CREATE TABLE t(x)" )
796+ con .executemany ("INSERT INTO t VALUES(?)" ,
797+ [(i ,) for i in range (100 )])
798+
799+ count = 0
800+ def close_progress ():
801+ nonlocal count
802+ count += 1
803+ if count >= 5 :
804+ con .close ()
805+ return 1
806+ return 0
807+
808+ cursor = con .execute ("SELECT * FROM t" )
809+ con .set_progress_handler (close_progress , 1 )
810+ msg = "from within a callback"
811+ import test .support
812+ with test .support .catch_unraisable_exception ():
813+ with self .assertRaisesRegex (sqlite .ProgrammingError , msg ):
814+ for row in cursor :
815+ pass
816+ del cursor
817+ gc_collect ()
818+
819+ def test_close_conn_in_collation_callback (self ):
820+ """gh-145040: closing connection in collation callback."""
821+ con = sqlite .connect (":memory:" , autocommit = True )
822+ con .execute ("CREATE TABLE t(x TEXT)" )
823+ con .executemany ("INSERT INTO t VALUES(?)" ,
824+ [(f"item_{ i } " ,) for i in range (50 )])
825+
826+ count = 0
827+ def evil_collation (a , b ):
828+ nonlocal count
829+ count += 1
830+ if count == 10 :
831+ con .close ()
832+ if a < b :
833+ return - 1
834+ elif a > b :
835+ return 1
836+ return 0
837+
838+ con .create_collation ("evil_coll" , evil_collation )
839+ msg = "from within a callback"
840+ with self .assertRaisesRegex (sqlite .ProgrammingError , msg ):
841+ con .execute (
842+ "SELECT * FROM t ORDER BY x COLLATE evil_coll"
843+ )
844+
749845
750846class AuthorizerTests (unittest .TestCase ):
751847 @staticmethod
0 commit comments