extralite 2.4 → 2.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -61,7 +61,7 @@ class DatabaseTest < MiniTest::Test
61
61
 
62
62
  r = @db.query_single_column('select y from t where x = 2')
63
63
  assert_equal [], r
64
- end
64
+ end
65
65
 
66
66
  def test_query_single_value
67
67
  r = @db.query_single_value('select z from t order by Z desc limit 1')
@@ -288,16 +288,38 @@ end
288
288
  assert_equal [], @db.tables
289
289
  end
290
290
 
291
+ def test_tables_with_db_name
292
+ assert_equal ['t'], @db.tables('main')
293
+
294
+ @db.query('create table foo (bar text)')
295
+ assert_equal ['t', 'foo'], @db.tables('main')
296
+
297
+ @db.query('drop table t')
298
+ assert_equal ['foo'], @db.tables('main')
299
+
300
+ @db.query('drop table foo')
301
+ assert_equal [], @db.tables('main')
302
+
303
+ assert_raises { @db.tables('foo') }
304
+
305
+ fn = Tempfile.new('extralite_test_tables_with_db_name').path
306
+ @db.execute "attach database '#{fn}' as foo"
307
+
308
+ assert_equal [], @db.tables('foo')
309
+ @db.execute 'create table foo.bar (x, y)'
310
+ assert_equal ['bar'], @db.tables('foo')
311
+ end
312
+
291
313
  def test_pragma
292
- assert_equal [{journal_mode: 'memory'}], @db.pragma('journal_mode')
293
- assert_equal [{synchronous: 2}], @db.pragma('synchronous')
314
+ assert_equal 'memory', @db.pragma('journal_mode')
315
+ assert_equal 2, @db.pragma('synchronous')
294
316
 
295
- assert_equal [{schema_version: 1}], @db.pragma(:schema_version)
296
- assert_equal [{recursive_triggers: 0}], @db.pragma(:recursive_triggers)
317
+ assert_equal 1, @db.pragma(:schema_version)
318
+ assert_equal 0, @db.pragma(:recursive_triggers)
297
319
 
298
320
  assert_equal [], @db.pragma(schema_version: 33, recursive_triggers: 1)
299
- assert_equal [{schema_version: 33}], @db.pragma(:schema_version)
300
- assert_equal [{recursive_triggers: 1}], @db.pragma(:recursive_triggers)
321
+ assert_equal 33, @db.pragma(:schema_version)
322
+ assert_equal 1, @db.pragma(:recursive_triggers)
301
323
  end
302
324
 
303
325
  def test_execute
@@ -311,7 +333,13 @@ end
311
333
  assert_equal [[1, 2, 3], [42, 5, 6]], @db.query_ary('select * from t order by x')
312
334
  end
313
335
 
314
- def test_execute_multi
336
+ def test_execute_with_params_array
337
+ changes = @db.execute('update t set x = ? where z = ?', [42, 6])
338
+ assert_equal 1, changes
339
+ assert_equal [[1, 2, 3], [42, 5, 6]], @db.query_ary('select * from t order by x')
340
+ end
341
+
342
+ def test_batch_execute
315
343
  @db.query('create table foo (a, b, c)')
316
344
  assert_equal [], @db.query('select * from foo')
317
345
 
@@ -320,7 +348,7 @@ end
320
348
  ['4', 5, 6]
321
349
  ]
322
350
 
323
- changes = @db.execute_multi('insert into foo values (?, ?, ?)', records)
351
+ changes = @db.batch_execute('insert into foo values (?, ?, ?)', records)
324
352
 
325
353
  assert_equal 2, changes
326
354
  assert_equal [
@@ -329,7 +357,7 @@ end
329
357
  ], @db.query('select * from foo')
330
358
  end
331
359
 
332
- def test_execute_multi_single_values
360
+ def test_batch_execute_single_values
333
361
  @db.query('create table foo (bar)')
334
362
  assert_equal [], @db.query('select * from foo')
335
363
 
@@ -338,7 +366,7 @@ end
338
366
  'bye'
339
367
  ]
340
368
 
341
- changes = @db.execute_multi('insert into foo values (?)', records)
369
+ changes = @db.batch_execute('insert into foo values (?)', records)
342
370
 
343
371
  assert_equal 2, changes
344
372
  assert_equal [
@@ -347,6 +375,314 @@ end
347
375
  ], @db.query('select * from foo')
348
376
  end
349
377
 
378
+ def test_batch_execute_with_each_interface
379
+ @db.query('create table foo (bar)')
380
+ assert_equal [], @db.query('select * from foo')
381
+
382
+ changes = @db.batch_execute('insert into foo values (?)', 1..3)
383
+
384
+ assert_equal 3, changes
385
+ assert_equal [
386
+ { bar: 1 },
387
+ { bar: 2 },
388
+ { bar: 3 }
389
+ ], @db.query('select * from foo')
390
+ end
391
+
392
+ def test_batch_execute_with_proc
393
+ source = [42, 43, 44]
394
+
395
+ @db.query('create table foo (a)')
396
+ assert_equal [], @db.query('select * from foo')
397
+
398
+ pr = proc { source.shift }
399
+ changes = @db.batch_execute('insert into foo values (?)', pr)
400
+
401
+ assert_equal 3, changes
402
+ assert_equal [
403
+ { a: 42 },
404
+ { a: 43 },
405
+ { a: 44 }
406
+ ], @db.query('select * from foo')
407
+ end
408
+
409
+ def test_batch_query_with_array
410
+ @db.query('create table foo (a, b, c)')
411
+ assert_equal [], @db.query('select * from foo')
412
+
413
+ data = [
414
+ [1, '2', 3],
415
+ ['4', 5, 6]
416
+ ]
417
+ results = @db.batch_query('insert into foo values (?, ?, ?) returning *', data)
418
+ assert_equal [
419
+ [{ a: 1, b: '2', c: 3 }],
420
+ [{ a: '4', b: 5, c: 6 }]
421
+ ], results
422
+
423
+ results = @db.batch_query('update foo set c = ? returning *', [42, 43])
424
+ assert_equal [
425
+ [{ a: 1, b: '2', c: 42 }, { a: '4', b: 5, c: 42 }],
426
+ [{ a: 1, b: '2', c: 43 }, { a: '4', b: 5, c: 43 }]
427
+ ], results
428
+
429
+ array = []
430
+ changes = @db.batch_query('update foo set c = ? returning *', [44, 45]) do |rows|
431
+ array << rows
432
+ end
433
+ assert_equal 4, changes
434
+ assert_equal [
435
+ [{ a: 1, b: '2', c: 44 }, { a: '4', b: 5, c: 44 }],
436
+ [{ a: 1, b: '2', c: 45 }, { a: '4', b: 5, c: 45 }]
437
+ ], array
438
+ end
439
+
440
+ def test_batch_query_with_enumerable
441
+ @db.query('create table foo (a integer primary key, b)')
442
+ assert_equal [], @db.query('select * from foo')
443
+
444
+ results = @db.batch_query('insert into foo (b) values (?) returning *', 11..13)
445
+ assert_equal [
446
+ [{ a: 1, b: 11 }],
447
+ [{ a: 2, b: 12 }],
448
+ [{ a: 3, b: 13 }]
449
+ ], results
450
+
451
+ results = @db.batch_query('update foo set b = ? returning *', [42, 43])
452
+ assert_equal [
453
+ [{ a: 1, b: 42 }, { a: 2, b: 42 }, { a: 3, b: 42 }],
454
+ [{ a: 1, b: 43 }, { a: 2, b: 43 }, { a: 3, b: 43 }]
455
+ ], results
456
+
457
+ array = []
458
+ changes = @db.batch_query('update foo set b = ? returning *', [44, 45]) do |rows|
459
+ array << rows
460
+ end
461
+ assert_equal 6, changes
462
+ assert_equal [
463
+ [{ a: 1, b: 44 }, { a: 2, b: 44 }, { a: 3, b: 44 }],
464
+ [{ a: 1, b: 45 }, { a: 2, b: 45 }, { a: 3, b: 45 }]
465
+ ], array
466
+ end
467
+
468
+ def parameter_source_proc(values)
469
+ proc { values.shift }
470
+ end
471
+
472
+ def test_batch_query_with_proc
473
+ @db.query('create table foo (a integer primary key, b)')
474
+ assert_equal [], @db.query('select * from foo')
475
+
476
+ pr = parameter_source_proc([5, 4, 3])
477
+ assert_kind_of Proc, pr
478
+ results = @db.batch_query('insert into foo (b) values (?) returning *', pr)
479
+ assert_equal [
480
+ [{ a: 1, b: 5 }],
481
+ [{ a: 2, b: 4 }],
482
+ [{ a: 3, b: 3 }]
483
+ ], results
484
+
485
+ pr = parameter_source_proc([42, 43])
486
+ results = @db.batch_query('update foo set b = ? returning *', pr)
487
+ assert_equal [
488
+ [{ a: 1, b: 42 }, { a: 2, b: 42 }, { a: 3, b: 42 }],
489
+ [{ a: 1, b: 43 }, { a: 2, b: 43 }, { a: 3, b: 43 }]
490
+ ], results
491
+
492
+ array = []
493
+ pr = parameter_source_proc([44, 45])
494
+ changes = @db.batch_query('update foo set b = ? returning *', pr) do |rows|
495
+ array << rows
496
+ end
497
+ assert_equal 6, changes
498
+ assert_equal [
499
+ [{ a: 1, b: 44 }, { a: 2, b: 44 }, { a: 3, b: 44 }],
500
+ [{ a: 1, b: 45 }, { a: 2, b: 45 }, { a: 3, b: 45 }]
501
+ ], array
502
+ end
503
+
504
+ def test_batch_query_ary_with_array
505
+ @db.query('create table foo (a, b, c)')
506
+ assert_equal [], @db.query('select * from foo')
507
+
508
+ data = [
509
+ [1, '2', 3],
510
+ ['4', 5, 6]
511
+ ]
512
+ results = @db.batch_query_ary('insert into foo values (?, ?, ?) returning *', data)
513
+ assert_equal [
514
+ [[1, '2', 3]],
515
+ [['4', 5, 6]]
516
+ ], results
517
+
518
+ results = @db.batch_query_ary('update foo set c = ? returning *', [42, 43])
519
+ assert_equal [
520
+ [[1, '2', 42], ['4', 5, 42]],
521
+ [[1, '2', 43], ['4', 5, 43]]
522
+ ], results
523
+
524
+ array = []
525
+ changes = @db.batch_query_ary('update foo set c = ? returning *', [44, 45]) do |rows|
526
+ array << rows
527
+ end
528
+ assert_equal 4, changes
529
+ assert_equal [
530
+ [[1, '2', 44], ['4', 5, 44]],
531
+ [[1, '2', 45], ['4', 5, 45]]
532
+ ], array
533
+ end
534
+
535
+ def test_batch_query_ary_with_enumerable
536
+ @db.query('create table foo (a integer primary key, b)')
537
+ assert_equal [], @db.query('select * from foo')
538
+
539
+ results = @db.batch_query_ary('insert into foo (b) values (?) returning *', 11..13)
540
+ assert_equal [
541
+ [[1, 11]],
542
+ [[2, 12]],
543
+ [[3, 13]]
544
+ ], results
545
+
546
+ results = @db.batch_query_ary('update foo set b = ? returning *', [42, 43])
547
+ assert_equal [
548
+ [[1, 42], [2, 42], [3, 42]],
549
+ [[1, 43], [2, 43], [3, 43]]
550
+ ], results
551
+
552
+ array = []
553
+ changes = @db.batch_query_ary('update foo set b = ? returning *', [44, 45]) do |rows|
554
+ array << rows
555
+ end
556
+ assert_equal 6, changes
557
+ assert_equal [
558
+ [[1, 44], [2, 44], [3, 44]],
559
+ [[1, 45], [2, 45], [3, 45]]
560
+ ], array
561
+ end
562
+
563
+ def test_batch_query_ary_with_proc
564
+ @db.query('create table foo (a integer primary key, b)')
565
+ assert_equal [], @db.query('select * from foo')
566
+
567
+ pr = parameter_source_proc([5, 4, 3])
568
+ assert_kind_of Proc, pr
569
+ results = @db.batch_query_ary('insert into foo (b) values (?) returning *', pr)
570
+ assert_equal [
571
+ [[1, 5]],
572
+ [[2, 4]],
573
+ [[3, 3]]
574
+ ], results
575
+
576
+ pr = parameter_source_proc([42, 43])
577
+ results = @db.batch_query_ary('update foo set b = ? returning *', pr)
578
+ assert_equal [
579
+ [[1, 42], [2, 42], [3, 42]],
580
+ [[1, 43], [2, 43], [3, 43]]
581
+ ], results
582
+
583
+ array = []
584
+ pr = parameter_source_proc([44, 45])
585
+ changes = @db.batch_query_ary('update foo set b = ? returning *', pr) do |rows|
586
+ array << rows
587
+ end
588
+ assert_equal 6, changes
589
+ assert_equal [
590
+ [[1, 44], [2, 44], [3, 44]],
591
+ [[1, 45], [2, 45], [3, 45]]
592
+ ], array
593
+ end
594
+
595
+ def test_batch_query_single_column_with_array
596
+ @db.query('create table foo (a, b, c)')
597
+ assert_equal [], @db.query('select * from foo')
598
+
599
+ data = [
600
+ [1, '2', 3],
601
+ ['4', 5, 6]
602
+ ]
603
+ results = @db.batch_query_single_column('insert into foo values (?, ?, ?) returning c', data)
604
+ assert_equal [
605
+ [3],
606
+ [6]
607
+ ], results
608
+
609
+ results = @db.batch_query_single_column('update foo set c = ? returning c * 10 + cast(b as integer)', [42, 43])
610
+ assert_equal [
611
+ [422, 425],
612
+ [432, 435]
613
+ ], results
614
+
615
+ array = []
616
+ changes = @db.batch_query_single_column('update foo set c = ? returning c * 10 + cast(b as integer)', [44, 45]) do |rows|
617
+ array << rows
618
+ end
619
+ assert_equal 4, changes
620
+ assert_equal [
621
+ [442, 445],
622
+ [452, 455]
623
+ ], array
624
+ end
625
+
626
+ def test_batch_query_single_column_with_enumerable
627
+ @db.query('create table foo (a integer primary key, b)')
628
+ assert_equal [], @db.query('select * from foo')
629
+
630
+ results = @db.batch_query_single_column('insert into foo (b) values (?) returning b * 10 + a', 11..13)
631
+ assert_equal [
632
+ [111],
633
+ [122],
634
+ [133]
635
+ ], results
636
+
637
+ results = @db.batch_query_single_column('update foo set b = ? returning b * 10 + a', 42..43)
638
+ assert_equal [
639
+ [421, 422, 423],
640
+ [431, 432, 433]
641
+ ], results
642
+
643
+ array = []
644
+ changes = @db.batch_query_single_column('update foo set b = ? returning b * 10 + a', 44..45) do |rows|
645
+ array << rows
646
+ end
647
+ assert_equal 6, changes
648
+ assert_equal [
649
+ [441, 442, 443],
650
+ [451, 452, 453]
651
+ ], array
652
+ end
653
+
654
+ def test_batch_query_single_column_with_proc
655
+ @db.query('create table foo (a integer primary key, b)')
656
+ assert_equal [], @db.query('select * from foo')
657
+
658
+ pr = parameter_source_proc([5, 4, 3])
659
+ assert_kind_of Proc, pr
660
+ results = @db.batch_query_single_column('insert into foo (b) values (?) returning b', pr)
661
+ assert_equal [
662
+ [5],
663
+ [4],
664
+ [3]
665
+ ], results
666
+
667
+ pr = parameter_source_proc([42, 43])
668
+ results = @db.batch_query_single_column('update foo set b = ? returning b * 10 + a', pr)
669
+ assert_equal [
670
+ [421, 422, 423],
671
+ [431, 432, 433]
672
+ ], results
673
+
674
+ array = []
675
+ pr = parameter_source_proc([44, 45])
676
+ changes = @db.batch_query_single_column('update foo set b = ? returning b * 10 + a', pr) do |rows|
677
+ array << rows
678
+ end
679
+ assert_equal 6, changes
680
+ assert_equal [
681
+ [441, 442, 443],
682
+ [451, 452, 453]
683
+ ], array
684
+ end
685
+
350
686
  def test_interrupt
351
687
  t = Thread.new do
352
688
  sleep 0.5
@@ -477,6 +813,19 @@ end
477
813
  assert_equal true, db.read_only?
478
814
  end
479
815
 
816
+ def test_database_initialize_options
817
+ db = Extralite::Database.new(':memory:', gvl_release_threshold: 23)
818
+ assert_equal 23, db.gvl_release_threshold
819
+
820
+ fn = Tempfile.new('extralite_test_database_initialize_options_1').path
821
+ db = Extralite::Database.new(fn, wal_journal_mode: true)
822
+ assert_equal 'wal', db.pragma(:journal_mode)
823
+
824
+ fn = Tempfile.new('extralite_test_database_initialize_options_2').path
825
+ db = Extralite::Database.new(fn, synchronous: true)
826
+ assert_equal 1, db.pragma(:synchronous)
827
+ end
828
+
480
829
  def test_database_inspect
481
830
  db = Extralite::Database.new(':memory:')
482
831
  assert_match /^\#\<Extralite::Database:0x[0-9a-f]+ :memory:\>$/, db.inspect
@@ -547,6 +896,53 @@ end
547
896
  assert_kind_of RuntimeError, exception
548
897
  assert_equal 'bar', exception.message
549
898
  end
899
+
900
+ def test_database_transaction_rollback!
901
+ db = Extralite::Database.new(':memory:')
902
+ db.execute('create table foo(x)')
903
+
904
+ exception = nil
905
+ begin
906
+ db.transaction do
907
+ db.execute('insert into foo values (42)')
908
+ db.rollback!
909
+ end
910
+ rescue => e
911
+ exception = e
912
+ end
913
+
914
+ assert_equal [], db.query('select * from foo')
915
+ assert_nil exception
916
+ end
917
+
918
+ def test_database_savepoint
919
+ db = Extralite::Database.new(':memory:')
920
+ db.execute('create table foo(x)')
921
+
922
+ db.transaction do
923
+ assert_equal [], db.query('select * from foo')
924
+
925
+ db.execute('insert into foo values (42)')
926
+ assert_equal [42], db.query_single_column('select x from foo')
927
+
928
+ db.savepoint(:a)
929
+
930
+ db.execute('insert into foo values (43)')
931
+ assert_equal [42, 43], db.query_single_column('select x from foo')
932
+
933
+ db.savepoint(:b)
934
+
935
+ db.execute('insert into foo values (44)')
936
+ assert_equal [42, 43, 44], db.query_single_column('select x from foo')
937
+
938
+ db.rollback_to(:b)
939
+ assert_equal [42, 43], db.query_single_column('select x from foo')
940
+
941
+ db.release(:a)
942
+
943
+ assert_equal [42, 43], db.query_single_column('select x from foo')
944
+ end
945
+ end
550
946
  end
551
947
 
552
948
  class ScenarioTest < MiniTest::Test
@@ -616,6 +1012,32 @@ class ScenarioTest < MiniTest::Test
616
1012
  assert_equal [1, 4, 7], result
617
1013
  end
618
1014
 
1015
+ def test_concurrent_queries
1016
+ @db.query('delete from t')
1017
+ @db.gvl_release_threshold = 1
1018
+ q1 = @db.prepare('insert into t values (?, ?, ?)')
1019
+ q2 = @db.prepare('insert into t values (?, ?, ?)')
1020
+
1021
+ t1 = Thread.new do
1022
+ data = (1..50).each_slice(10).map { |a| a.map { |i| [i, i + 1, i + 2] } }
1023
+ data.each do |params|
1024
+ q1.batch_execute(params)
1025
+ end
1026
+ end
1027
+
1028
+ t2 = Thread.new do
1029
+ data = (51..100).each_slice(10).map { |a| a.map { |i| [i, i + 1, i + 2] } }
1030
+ data.each do |params|
1031
+ q2.batch_execute(params)
1032
+ end
1033
+ end
1034
+
1035
+ t1.join
1036
+ t2.join
1037
+
1038
+ assert_equal (1..100).to_a, @db.query_single_column('select x from t order by x')
1039
+ end
1040
+
619
1041
  def test_database_trace
620
1042
  sqls = []
621
1043
  @db.trace { |sql| sqls << sql }
@@ -678,7 +1100,7 @@ class BackupTest < MiniTest::Test
678
1100
  end
679
1101
  end
680
1102
 
681
- class GVLReleaseThresholdTest < Minitest::Test
1103
+ class ConcurrencyTest < Minitest::Test
682
1104
  def setup
683
1105
  @sql = <<~SQL
684
1106
  WITH RECURSIVE r(i) AS (
@@ -773,4 +1195,241 @@ class GVLReleaseThresholdTest < Minitest::Test
773
1195
  db.gvl_release_threshold = nil
774
1196
  assert_equal 1000, db.gvl_release_threshold
775
1197
  end
776
- end
1198
+
1199
+ def test_progress_handler_simple
1200
+ db = Extralite::Database.new(':memory:')
1201
+
1202
+ buf = []
1203
+ db.on_progress(1) { buf << :progress }
1204
+
1205
+ result = db.query_single_row('select 1 as a, 2 as b, 3 as c')
1206
+ assert_equal({ a: 1, b: 2, c: 3 }, result)
1207
+ assert_in_range 5..7, buf.size
1208
+
1209
+ buf = []
1210
+ db.on_progress(2) { buf << :progress }
1211
+
1212
+ result = db.query_single_row('select 1 as a, 2 as b, 3 as c')
1213
+ assert_equal({ a: 1, b: 2, c: 3 }, result)
1214
+ assert_in_range 2..4, buf.size
1215
+ end
1216
+
1217
+ LONG_QUERY = <<~SQL
1218
+ WITH RECURSIVE
1219
+ fibo (curr, next)
1220
+ AS
1221
+ ( SELECT 1,1
1222
+ UNION ALL
1223
+ SELECT next, curr + next FROM fibo
1224
+ LIMIT 10000000 )
1225
+ SELECT curr, next FROM fibo LIMIT 1 OFFSET 10000000-1;
1226
+ SQL
1227
+
1228
+ def test_progress_handler_timeout_interrupt
1229
+ db = Extralite::Database.new(':memory:')
1230
+ t0 = Time.now
1231
+ db.on_progress(1000) do
1232
+ Thread.pass
1233
+ db.interrupt if Time.now - t0 >= 0.2
1234
+ end
1235
+
1236
+ q = db.prepare(LONG_QUERY)
1237
+ result = nil
1238
+ err = nil
1239
+ begin
1240
+ result = q.next
1241
+ rescue => e
1242
+ err = e
1243
+ end
1244
+ t1 = Time.now
1245
+
1246
+ assert_nil result
1247
+ assert_equal 1, ((t1 - t0) * 5).round.to_i
1248
+ assert_kind_of Extralite::InterruptError, err
1249
+
1250
+ # try a second time, just to make sure no undefined state is left behind
1251
+ t0 = Time.now
1252
+ q = db.prepare(LONG_QUERY)
1253
+ result = nil
1254
+ err = nil
1255
+ begin
1256
+ result = q.next
1257
+ rescue => e
1258
+ err = e
1259
+ end
1260
+ t1 = Time.now
1261
+
1262
+ assert_nil result
1263
+ assert_equal 1, ((t1 - t0) * 5).round.to_i
1264
+ assert_kind_of Extralite::InterruptError, err
1265
+ end
1266
+
1267
+ class CustomTimeoutError < RuntimeError
1268
+ end
1269
+
1270
+ def test_progress_handler_timeout_raise
1271
+ db = Extralite::Database.new(':memory:')
1272
+ t0 = Time.now
1273
+ db.on_progress(1000) do
1274
+ Thread.pass
1275
+ raise CustomTimeoutError if Time.now - t0 >= 0.2
1276
+ end
1277
+
1278
+ q = db.prepare(LONG_QUERY)
1279
+ result = nil
1280
+ err = nil
1281
+ begin
1282
+ result = q.next
1283
+ rescue => e
1284
+ err = e
1285
+ end
1286
+ t1 = Time.now
1287
+
1288
+ assert_nil result
1289
+ assert_equal 1, ((t1 - t0) * 5).round.to_i
1290
+ assert_kind_of CustomTimeoutError, err
1291
+
1292
+ # try a second time, just to make sure no undefined state is left behind
1293
+ t0 = Time.now
1294
+ q = db.prepare(LONG_QUERY)
1295
+ result = nil
1296
+ err = nil
1297
+ begin
1298
+ result = q.next
1299
+ rescue => e
1300
+ err = e
1301
+ end
1302
+ t1 = Time.now
1303
+
1304
+ assert_nil result
1305
+ assert_equal 1, ((t1 - t0) * 5).round.to_i
1306
+ assert_kind_of CustomTimeoutError, err
1307
+ end
1308
+
1309
+ def test_progress_handler_busy_timeout
1310
+ fn = Tempfile.new('extralite_test_progress_handler_busy_timeout').path
1311
+ db1 = Extralite::Database.new(fn)
1312
+ db2 = Extralite::Database.new(fn)
1313
+
1314
+ db1.query('begin exclusive')
1315
+ assert_raises(Extralite::BusyError) { db2.query('begin exclusive') }
1316
+
1317
+ t0 = Time.now
1318
+ db2.on_progress(1000) do
1319
+ Thread.pass
1320
+ raise CustomTimeoutError if Time.now - t0 >= 0.2
1321
+ end
1322
+
1323
+ result = nil
1324
+ err = nil
1325
+ begin
1326
+ result = db2.execute('begin exclusive')
1327
+ rescue => e
1328
+ err = e
1329
+ end
1330
+ t1 = Time.now
1331
+
1332
+ assert_nil result
1333
+ assert_equal 1, ((t1 - t0) * 5).round.to_i
1334
+ assert_kind_of CustomTimeoutError, err
1335
+
1336
+ # Try a second time, to ensure no undefined state remains behind
1337
+ t0 = Time.now
1338
+ result = nil
1339
+ err = nil
1340
+ begin
1341
+ result = db2.execute('begin exclusive')
1342
+ rescue => e
1343
+ err = e
1344
+ end
1345
+ t1 = Time.now
1346
+
1347
+ assert_nil result
1348
+ assert_equal 1, ((t1 - t0) * 5).round.to_i
1349
+ assert_kind_of CustomTimeoutError, err
1350
+ end
1351
+ end
1352
+
1353
+ class RactorTest < Minitest::Test
1354
+ def test_ractor_simple
1355
+ skip if SKIP_RACTOR_TESTS
1356
+
1357
+ fn = Tempfile.new('extralite_test_database_in_ractor').path
1358
+
1359
+ r = Ractor.new do
1360
+ path = receive
1361
+ db = Extralite::Database.new(path)
1362
+ i = receive
1363
+ db.execute 'insert into foo values (?)', i
1364
+ end
1365
+
1366
+ r << fn
1367
+ db = Extralite::Database.new(fn)
1368
+ db.execute 'create table foo (x)'
1369
+ r << 42
1370
+ r.take # wait for ractor to terminate
1371
+
1372
+ assert_equal 42, db.query_single_value('select x from foo')
1373
+ end
1374
+
1375
+ # Adapted from here: https://github.com/sparklemotion/sqlite3-ruby/pull/365/files
1376
+ def test_ractor_share_database
1377
+ skip if SKIP_RACTOR_TESTS
1378
+
1379
+ db_receiver = Ractor.new do
1380
+ db = Ractor.receive
1381
+ Ractor.yield db.object_id
1382
+ begin
1383
+ db.execute("create table foo (b)")
1384
+ raise "Should have raised an exception in db.execute()"
1385
+ rescue => e
1386
+ Ractor.yield e
1387
+ end
1388
+ end
1389
+ sleep 0.1
1390
+ db_creator = Ractor.new(db_receiver) do |db_receiver|
1391
+ db = Extralite::Database.new(":memory:")
1392
+ Ractor.yield db.object_id
1393
+ db_receiver.send(db)
1394
+ sleep 0.1
1395
+ db.execute("create table foo (a)")
1396
+ end
1397
+ first_oid = db_creator.take
1398
+ second_oid = db_receiver.take
1399
+ refute_equal first_oid, second_oid
1400
+ ex = db_receiver.take
1401
+ assert_kind_of Extralite::Error, ex
1402
+ assert_equal "Database is closed", ex.message
1403
+ end
1404
+
1405
+ STRESS_DB_NAME = Tempfile.new('extralite_test_ractor_stress').path
1406
+
1407
+ # Adapted from here: https://github.com/sparklemotion/sqlite3-ruby/pull/365/files
1408
+ def test_ractor_stress
1409
+ skip if SKIP_RACTOR_TESTS
1410
+
1411
+ Ractor.make_shareable(STRESS_DB_NAME)
1412
+
1413
+ db = Extralite::Database.new(STRESS_DB_NAME)
1414
+ db.execute("PRAGMA journal_mode=WAL") # A little slow without this
1415
+ db.execute("create table stress_test (a integer primary_key, b text)")
1416
+ random = Random.new.freeze
1417
+ ractors = (0..9).map do |ractor_number|
1418
+ Ractor.new(random, ractor_number) do |random, ractor_number|
1419
+ db_in_ractor = Extralite::Database.new(STRESS_DB_NAME)
1420
+ db_in_ractor.busy_timeout = 3
1421
+ 10.times do |i|
1422
+ db_in_ractor.execute("insert into stress_test(a, b) values (#{ractor_number * 100 + i}, '#{random.rand}')")
1423
+ end
1424
+ end
1425
+ end
1426
+ ractors.each { |r| r.take }
1427
+ final_check = Ractor.new do
1428
+ db_in_ractor = Extralite::Database.new(STRESS_DB_NAME)
1429
+ count = db_in_ractor.query_single_value("select count(*) from stress_test")
1430
+ Ractor.yield count
1431
+ end
1432
+ count = final_check.take
1433
+ assert_equal 100, count
1434
+ end
1435
+ end