@@ -79,6 +79,7 @@ pub use crate::{
7979 config:: { AccessMode , Config , DefaultNullOrder , DefaultOrder } ,
8080 error:: Error ,
8181 ffi:: ErrorCode ,
82+ inner_connection:: InterruptHandle ,
8283 params:: { params_from_iter, Params , ParamsFromIter } ,
8384 row:: { AndThenRows , Map , MappedRows , Row , RowIndex , Rows } ,
8485 statement:: Statement ,
@@ -532,6 +533,30 @@ impl Connection {
532533 self . db . borrow_mut ( ) . appender ( self , table, schema)
533534 }
534535
536+ /// Get a handle to interrupt long-running queries.
537+ ///
538+ /// ## Example
539+ ///
540+ /// ```rust,no_run
541+ /// # use duckdb::{Connection, Result};
542+ /// fn run_query(conn: Connection) -> Result<()> {
543+ /// let interrupt_handle = conn.interrupt_handle();
544+ /// let join_handle = std::thread::spawn(move || { conn.execute("expensive query", []) });
545+ ///
546+ /// // Arbitrary wait for query to start
547+ /// std::thread::sleep(std::time::Duration::from_millis(100));
548+ ///
549+ /// interrupt_handle.interrupt();
550+ ///
551+ /// let query_result = join_handle.join().unwrap();
552+ /// assert!(query_result.is_err());
553+ ///
554+ /// Ok(())
555+ /// }
556+ pub fn interrupt_handle ( & self ) -> std:: sync:: Arc < InterruptHandle > {
557+ self . db . borrow ( ) . get_interrupt_handle ( )
558+ }
559+
535560 /// Close the DuckDB connection.
536561 ///
537562 /// This is functionally equivalent to the `Drop` implementation for
@@ -1337,6 +1362,36 @@ mod test {
13371362 Ok ( ( ) )
13381363 }
13391364
1365+ #[ test]
1366+ fn test_interrupt ( ) -> Result < ( ) > {
1367+ let db = checked_memory_handle ( ) ;
1368+ let db_interrupt = db. interrupt_handle ( ) ;
1369+
1370+ let ( tx, rx) = std:: sync:: mpsc:: channel ( ) ;
1371+ std:: thread:: spawn ( move || {
1372+ let mut stmt = db
1373+ . prepare ( "select count(*) from range(10000000) t1, range(1000000) t2" )
1374+ . unwrap ( ) ;
1375+ tx. send ( stmt. execute ( [ ] ) ) . unwrap ( ) ;
1376+ } ) ;
1377+
1378+ std:: thread:: sleep ( std:: time:: Duration :: from_millis ( 100 ) ) ;
1379+ db_interrupt. interrupt ( ) ;
1380+
1381+ let result = rx. recv_timeout ( std:: time:: Duration :: from_secs ( 5 ) ) . unwrap ( ) ;
1382+ assert ! ( result. is_err_and( |err| err. to_string( ) . contains( "INTERRUPT" ) ) ) ;
1383+ Ok ( ( ) )
1384+ }
1385+
1386+ #[ test]
1387+ fn test_interrupt_on_dropped_db ( ) {
1388+ let db = checked_memory_handle ( ) ;
1389+ let db_interrupt = db. interrupt_handle ( ) ;
1390+
1391+ drop ( db) ;
1392+ db_interrupt. interrupt ( ) ;
1393+ }
1394+
13401395 #[ cfg( feature = "bundled" ) ]
13411396 #[ test]
13421397 fn test_version ( ) -> Result < ( ) > {
0 commit comments