extralite 2.4 → 2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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