extralite-bundle 2.4 → 2.5

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
@@ -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