extralite 2.4 → 2.5
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.
- checksums.yaml +4 -4
- data/.github/workflows/test-bundle.yml +30 -0
- data/.github/workflows/test.yml +2 -12
- data/CHANGELOG.md +39 -10
- data/Gemfile.lock +1 -1
- data/README.md +45 -11
- data/TODO.md +0 -3
- data/ext/extralite/common.c +222 -15
- data/ext/extralite/database.c +185 -16
- data/ext/extralite/extralite.h +5 -1
- data/ext/extralite/extralite_ext.c +4 -0
- data/ext/extralite/query.c +213 -12
- data/gemspec.rb +1 -1
- data/lib/extralite/version.rb +1 -1
- data/lib/extralite.rb +14 -6
- data/test/helper.rb +1 -0
- data/test/issue-54.rb +21 -0
- data/test/issue-59.rb +70 -0
- data/test/test_database.rb +471 -12
- data/test/test_query.rb +362 -2
- metadata +6 -3
data/test/issue-54.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "./lib/extralite"
|
4
|
+
|
5
|
+
puts 'Connecting to database...'
|
6
|
+
|
7
|
+
connection_1 = Extralite::Database.new("test.sqlite3")
|
8
|
+
puts "#{connection_1} connected"
|
9
|
+
connection_2 = Extralite::Database.new("test.sqlite3")
|
10
|
+
connection_2.busy_timeout = 0
|
11
|
+
puts "#{connection_2} connected"
|
12
|
+
|
13
|
+
[connection_1, connection_2].each do |connection|
|
14
|
+
puts "#{connection} beginning transaction..."
|
15
|
+
connection.execute "begin immediate transaction"
|
16
|
+
end
|
17
|
+
|
18
|
+
[connection_1, connection_2].each do |connection|
|
19
|
+
puts "#{connection} rolling back transaction..."
|
20
|
+
connection.execute "rollback transaction"
|
21
|
+
end
|
data/test/issue-59.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "./lib/extralite"
|
4
|
+
require "benchmark"
|
5
|
+
require "tempfile"
|
6
|
+
require "fileutils"
|
7
|
+
|
8
|
+
p sqlite_version: Extralite.sqlite3_version
|
9
|
+
|
10
|
+
N = (ENV['N'] || 1000).to_i
|
11
|
+
p N: N
|
12
|
+
|
13
|
+
fn1 = '/tmp/db1'
|
14
|
+
fn2 = '/tmp/db2'
|
15
|
+
|
16
|
+
FileUtils.rm(fn1) rescue nil
|
17
|
+
FileUtils.rm(fn2) rescue nil
|
18
|
+
|
19
|
+
p fn1: fn1
|
20
|
+
p fn2: fn2
|
21
|
+
|
22
|
+
db1 = Extralite::Database.new fn1
|
23
|
+
db1.execute "pragma journal_mode = wal;"
|
24
|
+
db1.transaction do
|
25
|
+
db1.execute "create table t1 ( a integer primary key, b text );"
|
26
|
+
values = N.times.map { |i| "#{i}-#{rand(1000)}" }
|
27
|
+
db1.execute_multi "insert into t1 ( b ) values ( ? );", values
|
28
|
+
|
29
|
+
p count: db1.query_single_value("select count(*) from t1")
|
30
|
+
p some_rows: db1.query("select * from t1 limit 5")
|
31
|
+
end
|
32
|
+
|
33
|
+
db2 = Extralite::Database.new fn2
|
34
|
+
db2.execute "pragma journal_mode = wal;"
|
35
|
+
db2.execute "attach '#{fn1}' as db1;"
|
36
|
+
db2.execute "create table t2 ( a integer primary key, b text );"
|
37
|
+
|
38
|
+
p main_tables: db2.tables
|
39
|
+
p db1_tables: db2.tables('db1')
|
40
|
+
|
41
|
+
overall = Benchmark.realtime do
|
42
|
+
t1 = Thread.new do
|
43
|
+
time1 = Benchmark.realtime do
|
44
|
+
db2.execute "create unique index db1.t1_b_unique on t1 (b);"
|
45
|
+
end
|
46
|
+
p({ indexing: time1 })
|
47
|
+
end
|
48
|
+
|
49
|
+
t2 = Thread.new do
|
50
|
+
time2 = Benchmark.realtime do
|
51
|
+
(N / 10000).times do |i|
|
52
|
+
values = 10000.times.map { |i| "#{i}-#{rand(1000)}" }
|
53
|
+
db2.transaction do
|
54
|
+
db2.execute_multi "insert into main.t2 ( b ) values ( ? );", values
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
p({ inserting: time2 })
|
59
|
+
p count_t2: db2.query_single_value("select count(*) from main.t2")
|
60
|
+
p some_rows_t2: db2.query("select * from main.t2 limit 5")
|
61
|
+
end
|
62
|
+
|
63
|
+
t1.join
|
64
|
+
t2.join
|
65
|
+
end
|
66
|
+
|
67
|
+
p({ overall: overall })
|
68
|
+
|
69
|
+
db1.close
|
70
|
+
db2.close
|
data/test/test_database.rb
CHANGED
@@ -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
|
293
|
-
assert_equal
|
314
|
+
assert_equal 'memory', @db.pragma('journal_mode')
|
315
|
+
assert_equal 2, @db.pragma('synchronous')
|
294
316
|
|
295
|
-
assert_equal
|
296
|
-
assert_equal
|
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
|
300
|
-
assert_equal
|
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
|
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.
|
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
|
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.
|
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
|
@@ -616,6 +965,32 @@ class ScenarioTest < MiniTest::Test
|
|
616
965
|
assert_equal [1, 4, 7], result
|
617
966
|
end
|
618
967
|
|
968
|
+
def test_concurrent_queries
|
969
|
+
@db.query('delete from t')
|
970
|
+
@db.gvl_release_threshold = 1
|
971
|
+
q1 = @db.prepare('insert into t values (?, ?, ?)')
|
972
|
+
q2 = @db.prepare('insert into t values (?, ?, ?)')
|
973
|
+
|
974
|
+
t1 = Thread.new do
|
975
|
+
data = (1..50).each_slice(10).map { |a| a.map { |i| [i, i + 1, i + 2] } }
|
976
|
+
data.each do |params|
|
977
|
+
q1.batch_execute(params)
|
978
|
+
end
|
979
|
+
end
|
980
|
+
|
981
|
+
t2 = Thread.new do
|
982
|
+
data = (51..100).each_slice(10).map { |a| a.map { |i| [i, i + 1, i + 2] } }
|
983
|
+
data.each do |params|
|
984
|
+
q2.batch_execute(params)
|
985
|
+
end
|
986
|
+
end
|
987
|
+
|
988
|
+
t1.join
|
989
|
+
t2.join
|
990
|
+
|
991
|
+
assert_equal (1..100).to_a, @db.query_single_column('select x from t order by x')
|
992
|
+
end
|
993
|
+
|
619
994
|
def test_database_trace
|
620
995
|
sqls = []
|
621
996
|
@db.trace { |sql| sqls << sql }
|
@@ -773,4 +1148,88 @@ class GVLReleaseThresholdTest < Minitest::Test
|
|
773
1148
|
db.gvl_release_threshold = nil
|
774
1149
|
assert_equal 1000, db.gvl_release_threshold
|
775
1150
|
end
|
776
|
-
end
|
1151
|
+
end
|
1152
|
+
|
1153
|
+
class RactorTest < Minitest::Test
|
1154
|
+
def test_ractor_simple
|
1155
|
+
skip if SKIP_RACTOR_TESTS
|
1156
|
+
|
1157
|
+
fn = Tempfile.new('extralite_test_database_in_ractor').path
|
1158
|
+
|
1159
|
+
r = Ractor.new do
|
1160
|
+
path = receive
|
1161
|
+
db = Extralite::Database.new(path)
|
1162
|
+
i = receive
|
1163
|
+
db.execute 'insert into foo values (?)', i
|
1164
|
+
end
|
1165
|
+
|
1166
|
+
r << fn
|
1167
|
+
db = Extralite::Database.new(fn)
|
1168
|
+
db.execute 'create table foo (x)'
|
1169
|
+
r << 42
|
1170
|
+
r.take # wait for ractor to terminate
|
1171
|
+
|
1172
|
+
assert_equal 42, db.query_single_value('select x from foo')
|
1173
|
+
end
|
1174
|
+
|
1175
|
+
# Adapted from here: https://github.com/sparklemotion/sqlite3-ruby/pull/365/files
|
1176
|
+
def test_ractor_share_database
|
1177
|
+
skip if SKIP_RACTOR_TESTS
|
1178
|
+
|
1179
|
+
db_receiver = Ractor.new do
|
1180
|
+
db = Ractor.receive
|
1181
|
+
Ractor.yield db.object_id
|
1182
|
+
begin
|
1183
|
+
db.execute("create table foo (b)")
|
1184
|
+
raise "Should have raised an exception in db.execute()"
|
1185
|
+
rescue => e
|
1186
|
+
Ractor.yield e
|
1187
|
+
end
|
1188
|
+
end
|
1189
|
+
sleep 0.1
|
1190
|
+
db_creator = Ractor.new(db_receiver) do |db_receiver|
|
1191
|
+
db = Extralite::Database.new(":memory:")
|
1192
|
+
Ractor.yield db.object_id
|
1193
|
+
db_receiver.send(db)
|
1194
|
+
sleep 0.1
|
1195
|
+
db.execute("create table foo (a)")
|
1196
|
+
end
|
1197
|
+
first_oid = db_creator.take
|
1198
|
+
second_oid = db_receiver.take
|
1199
|
+
refute_equal first_oid, second_oid
|
1200
|
+
ex = db_receiver.take
|
1201
|
+
assert_kind_of Extralite::Error, ex
|
1202
|
+
assert_equal "Database is closed", ex.message
|
1203
|
+
end
|
1204
|
+
|
1205
|
+
STRESS_DB_NAME = Tempfile.new('extralite_test_ractor_stress').path
|
1206
|
+
|
1207
|
+
# Adapted from here: https://github.com/sparklemotion/sqlite3-ruby/pull/365/files
|
1208
|
+
def test_ractor_stress
|
1209
|
+
skip if SKIP_RACTOR_TESTS
|
1210
|
+
|
1211
|
+
Ractor.make_shareable(STRESS_DB_NAME)
|
1212
|
+
|
1213
|
+
db = Extralite::Database.new(STRESS_DB_NAME)
|
1214
|
+
db.execute("PRAGMA journal_mode=WAL") # A little slow without this
|
1215
|
+
db.execute("create table stress_test (a integer primary_key, b text)")
|
1216
|
+
random = Random.new.freeze
|
1217
|
+
ractors = (0..9).map do |ractor_number|
|
1218
|
+
Ractor.new(random, ractor_number) do |random, ractor_number|
|
1219
|
+
db_in_ractor = Extralite::Database.new(STRESS_DB_NAME)
|
1220
|
+
db_in_ractor.busy_timeout = 3
|
1221
|
+
10.times do |i|
|
1222
|
+
db_in_ractor.execute("insert into stress_test(a, b) values (#{ractor_number * 100 + i}, '#{random.rand}')")
|
1223
|
+
end
|
1224
|
+
end
|
1225
|
+
end
|
1226
|
+
ractors.each { |r| r.take }
|
1227
|
+
final_check = Ractor.new do
|
1228
|
+
db_in_ractor = Extralite::Database.new(STRESS_DB_NAME)
|
1229
|
+
count = db_in_ractor.query_single_value("select count(*) from stress_test")
|
1230
|
+
Ractor.yield count
|
1231
|
+
end
|
1232
|
+
count = final_check.take
|
1233
|
+
assert_equal 100, count
|
1234
|
+
end
|
1235
|
+
end
|