@@ -17,6 +17,8 @@ database_mark(void *ctx)
1717{
1818 sqlite3RubyPtr c = (sqlite3RubyPtr )ctx ;
1919 rb_gc_mark (c -> busy_handler );
20+ rb_gc_mark (c -> stmt_timeout );
21+ rb_gc_mark (c -> stmt_deadline );
2022}
2123
2224static void
@@ -260,50 +262,44 @@ busy_handler(int argc, VALUE *argv, VALUE self)
260262}
261263
262264static int
263- rb_sqlite3_progress_handler (void * ctx )
265+ rb_sqlite3_statement_timeout (void * context )
264266{
265- VALUE self = (VALUE )(ctx );
266- VALUE handle = rb_iv_get (self , "@progress_handler" );
267- VALUE result = rb_funcall (handle , rb_intern ("call" ), 0 );
267+ sqlite3RubyPtr ctx = (sqlite3RubyPtr )context ;
268+ struct timespec currentTime ;
269+ clock_gettime (CLOCK_MONOTONIC , & currentTime );
270+
271+ if (ctx -> stmt_deadline == 0 ) {
272+ ctx -> stmt_deadline = currentTime .tv_sec * 1e9 + currentTime .tv_nsec + (long long )ctx -> stmt_timeout * 1e6 ;
273+ } else {
274+ long long timeDiff = ctx -> stmt_deadline - (currentTime .tv_sec * 1e9 + currentTime .tv_nsec );
268275
269- if (Qfalse == result ) return 1 ;
276+ if (timeDiff < 0 ) { return 1 ; }
277+ }
270278
271- return 0 ;
279+ return 0 ;
272280}
273281
274- /* call-seq:
275- * progress_handler([n]) { ... }
276- * progress_handler([n,] Class.new { def call; end }.new)
282+ /* call-seq: db.statement_timeout = ms
277283 *
278- * Register a progress handler with this database instance.
279- * This handler will be invoked periodically during a long-running query or operation.
280- * If the handler returns +false+, the operation will be interrupted; otherwise, it continues.
281- * The parameter 'n' specifies the number of SQLite virtual machine instructions between invocations.
282- * If 'n' is not provided, the default value is 1.
284+ * Indicates that if a query lasts longer than the indicated number of
285+ * milliseconds, SQLite should interrupt that query and return an error.
286+ * By default, SQLite does not interrupt queries. To restore the default
287+ * behavior, send 0 as the +ms+ parameter.
283288 */
284289static VALUE
285- progress_handler ( int argc , VALUE * argv , VALUE self )
290+ set_statement_timeout ( VALUE self , VALUE milliseconds )
286291{
287- sqlite3RubyPtr ctx ;
288- VALUE block , n_value ;
289-
290- TypedData_Get_Struct (self , sqlite3Ruby , & database_type , ctx );
291- REQUIRE_OPEN_DB (ctx );
292-
293- rb_scan_args (argc , argv , "02" , & n_value , & block );
294-
295- int n = NIL_P (n_value ) ? 1 : NUM2INT (n_value );
296- if (NIL_P (block ) && rb_block_given_p ()) block = rb_block_proc ();
292+ sqlite3RubyPtr ctx ;
293+ TypedData_Get_Struct (self , sqlite3Ruby , & database_type , ctx );
297294
298- rb_iv_set (self , "@progress_handler" , block );
295+ ctx -> stmt_timeout = NUM2INT (milliseconds );
296+ int n = NUM2INT (milliseconds ) == 0 ? -1 : 1000 ;
299297
300- sqlite3_progress_handler (
301- ctx -> db , n , NIL_P (block ) ? NULL : rb_sqlite3_progress_handler , (void * )self );
298+ sqlite3_progress_handler (ctx -> db , n , rb_sqlite3_statement_timeout , (void * )ctx );
302299
303- return self ;
300+ return self ;
304301}
305302
306-
307303/* call-seq: last_insert_row_id
308304 *
309305 * Obtains the unique row ID of the last row to be inserted by this Database
@@ -919,10 +915,10 @@ init_sqlite3_database(void)
919915 rb_define_method (cSqlite3Database , "changes" , changes , 0 );
920916 rb_define_method (cSqlite3Database , "authorizer=" , set_authorizer , 1 );
921917 rb_define_method (cSqlite3Database , "busy_handler" , busy_handler , -1 );
918+ rb_define_method (cSqlite3Database , "busy_timeout=" , set_busy_timeout , 1 );
922919#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
923- rb_define_method (cSqlite3Database , "progress_handler " , progress_handler , - 1 );
920+ rb_define_method (cSqlite3Database , "statement_timeout= " , set_statement_timeout , 1 );
924921#endif
925- rb_define_method (cSqlite3Database , "busy_timeout=" , set_busy_timeout , 1 );
926922 rb_define_method (cSqlite3Database , "extended_result_codes=" , set_extended_result_codes , 1 );
927923 rb_define_method (cSqlite3Database , "transaction_active?" , transaction_active_p , 0 );
928924 rb_define_private_method (cSqlite3Database , "exec_batch" , exec_batch , 2 );
0 commit comments