extralite 2.6 → 2.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,8 +4,9 @@ require_relative 'helper'
4
4
 
5
5
  require 'date'
6
6
  require 'tempfile'
7
+ require 'json'
7
8
 
8
- class DatabaseTest < MiniTest::Test
9
+ class DatabaseTest < Minitest::Test
9
10
  def setup
10
11
  @db = Extralite::Database.new(':memory:')
11
12
  @db.query('create table if not exists t (x,y,z)')
@@ -47,27 +48,38 @@ class DatabaseTest < MiniTest::Test
47
48
  assert_equal [], r
48
49
  end
49
50
 
50
- def test_query_single_row
51
- r = @db.query_single_row('select * from t order by x desc limit 1')
52
- assert_equal({ x: 4, y: 5, z: 6 }, r)
51
+ def test_query_argv
52
+ r = @db.query_argv('select * from t')
53
+ assert_equal [[1, 2, 3], [4, 5, 6]], r
53
54
 
54
- r = @db.query_single_row('select * from t where x = 2')
55
- assert_nil r
55
+ r = @db.query_argv('select * from t where x = 2')
56
+ assert_equal [], r
57
+
58
+ # with block
59
+ r = []
60
+ @db.query_argv('select * from t') { |a, b, c| r << [a, b, c] }
61
+ assert_equal [[1, 2, 3], [4, 5, 6]], r
56
62
  end
57
63
 
58
- def test_query_single_column
59
- r = @db.query_single_column('select y from t')
60
- assert_equal [2, 5], r
64
+ def test_query_argv_with_too_many_columns
65
+ assert_raises(Extralite::Error) {
66
+ @db.query_argv('select 1, 2, 3, 4, 5, 6, 7, 8, 9');
67
+ }
68
+ end
61
69
 
62
- r = @db.query_single_column('select y from t where x = 2')
63
- assert_equal [], r
70
+ def test_query_single
71
+ r = @db.query_single('select * from t order by x desc limit 1')
72
+ assert_equal({ x: 4, y: 5, z: 6 }, r)
73
+
74
+ r = @db.query_single('select * from t where x = 2')
75
+ assert_nil r
64
76
  end
65
77
 
66
- def test_query_single_value
67
- r = @db.query_single_value('select z from t order by Z desc limit 1')
78
+ def test_query_single_argv
79
+ r = @db.query_single_argv('select z from t order by Z desc limit 1')
68
80
  assert_equal 6, r
69
81
 
70
- r = @db.query_single_value('select z from t where x = 2')
82
+ r = @db.query_single_argv('select z from t where x = 2')
71
83
  assert_nil r
72
84
  end
73
85
 
@@ -87,7 +99,7 @@ class DatabaseTest < MiniTest::Test
87
99
  def test_multiple_statements
88
100
  @db.query("insert into t values ('a', 'b', 'c'); insert into t values ('d', 'e', 'f');")
89
101
 
90
- assert_equal [1, 4, 'a', 'd'], @db.query_single_column('select x from t order by x')
102
+ assert_equal [1, 4, 'a', 'd'], @db.query_argv('select x from t order by x')
91
103
  end
92
104
 
93
105
  def test_multiple_statements_with_error
@@ -111,13 +123,13 @@ class DatabaseTest < MiniTest::Test
111
123
 
112
124
  def test_close
113
125
  assert_equal false, @db.closed?
114
- r = @db.query_single_value('select 42')
126
+ r = @db.query_single_argv('select 42')
115
127
  assert_equal 42, r
116
128
 
117
129
  assert_equal @db, @db.close
118
130
  assert_equal true, @db.closed?
119
131
 
120
- assert_raises(Extralite::Error) { @db.query_single_value('select 42') }
132
+ assert_raises(Extralite::Error) { @db.query_single_argv('select 42') }
121
133
  end
122
134
 
123
135
  def test_parameter_binding_simple
@@ -127,7 +139,7 @@ class DatabaseTest < MiniTest::Test
127
139
  r = @db.query('select x, y, z from t where z = ?', 6)
128
140
  assert_equal [{ x: 4, y: 5, z: 6 }], r
129
141
 
130
- error = assert_raises(Extralite::ParameterError) { @db.query_single_value('select ?', Date.today) }
142
+ error = assert_raises(Extralite::ParameterError) { @db.query_single_argv('select ?', Date.today) }
131
143
  assert_equal error.message, 'Cannot bind parameter at position 1 of type Date'
132
144
  end
133
145
 
@@ -161,33 +173,33 @@ class DatabaseTest < MiniTest::Test
161
173
  class Foo; end
162
174
 
163
175
  def test_parameter_binding_from_hash
164
- assert_equal 42, @db.query_single_value('select :bar', foo: 41, bar: 42)
165
- assert_equal 42, @db.query_single_value('select :bar', 'foo' => 41, 'bar' => 42)
166
- assert_equal 42, @db.query_single_value('select ?8', 7 => 41, 8 => 42)
167
- assert_nil @db.query_single_value('select :bar', foo: 41)
176
+ assert_equal 42, @db.query_single_argv('select :bar', foo: 41, bar: 42)
177
+ assert_equal 42, @db.query_single_argv('select :bar', 'foo' => 41, 'bar' => 42)
178
+ assert_equal 42, @db.query_single_argv('select ?8', 7 => 41, 8 => 42)
179
+ assert_nil @db.query_single_argv('select :bar', foo: 41)
168
180
 
169
- error = assert_raises(Extralite::ParameterError) { @db.query_single_value('select ?', Foo.new => 42) }
181
+ error = assert_raises(Extralite::ParameterError) { @db.query_single_argv('select ?', Foo.new => 42) }
170
182
  assert_equal error.message, 'Cannot bind parameter with a key of type DatabaseTest::Foo'
171
183
 
172
- error = assert_raises(Extralite::ParameterError) { @db.query_single_value('select ?', %w[a b] => 42) }
184
+ error = assert_raises(Extralite::ParameterError) { @db.query_single_argv('select ?', %w[a b] => 42) }
173
185
  assert_equal error.message, 'Cannot bind parameter with a key of type Array'
174
186
  end
175
187
 
176
188
  def test_parameter_binding_from_struct
177
- foo_bar = Struct.new(:":foo", :bar)
189
+ foo_bar = Struct.new(:':foo', :bar)
178
190
  value = foo_bar.new(41, 42)
179
- assert_equal 41, @db.query_single_value('select :foo', value)
180
- assert_equal 42, @db.query_single_value('select :bar', value)
181
- assert_nil @db.query_single_value('select :baz', value)
191
+ assert_equal 41, @db.query_single_argv('select :foo', value)
192
+ assert_equal 42, @db.query_single_argv('select :bar', value)
193
+ assert_nil @db.query_single_argv('select :baz', value)
182
194
  end
183
195
 
184
196
  def test_parameter_binding_from_data_class
185
197
  skip "Data isn't supported in Ruby < 3.2" if RUBY_VERSION < '3.2'
186
198
 
187
- foo_bar = Data.define(:":foo", :bar)
188
- value = foo_bar.new(":foo": 41, bar: 42)
189
- assert_equal 42, @db.query_single_value('select :bar', value)
190
- assert_nil @db.query_single_value('select :baz', value)
199
+ foo_bar = Data.define(:':foo', :bar)
200
+ value = foo_bar.new(':foo': 41, bar: 42)
201
+ assert_equal 42, @db.query_single_argv('select :bar', value)
202
+ assert_nil @db.query_single_argv('select :baz', value)
191
203
  end
192
204
 
193
205
  def test_parameter_binding_for_blobs
@@ -197,69 +209,69 @@ class DatabaseTest < MiniTest::Test
197
209
 
198
210
  # it's a string, not a blob
199
211
  @db.execute('INSERT INTO blobs VALUES (?)', 'Hello, 世界!')
200
- result = @db.query_single_row(sql, @db.last_insert_rowid)
212
+ result = @db.query_single(sql, @db.last_insert_rowid)
201
213
  assert_equal 'text', result[:type]
202
214
  assert_equal Encoding::UTF_8, result[:data].encoding
203
215
 
204
216
  data = File.binread(blob_path)
205
217
  @db.execute('INSERT INTO blobs VALUES (?)', data)
206
- result = @db.query_single_row(sql, @db.last_insert_rowid)
218
+ result = @db.query_single(sql, @db.last_insert_rowid)
207
219
  assert_equal 'blob', result[:type]
208
220
  assert_equal data, result[:data]
209
221
 
210
222
  data = (+'Hello, 世界!').force_encoding(Encoding::ASCII_8BIT)
211
223
  @db.execute('INSERT INTO blobs VALUES (?)', data)
212
- result = @db.query_single_row(sql, @db.last_insert_rowid)
224
+ result = @db.query_single(sql, @db.last_insert_rowid)
213
225
  assert_equal 'blob', result[:type]
214
226
  assert_equal Encoding::ASCII_8BIT, result[:data].encoding
215
227
  assert_equal 'Hello, 世界!', result[:data].force_encoding(Encoding::UTF_8)
216
228
 
217
229
  data = Extralite::Blob.new('Hello, 世界!')
218
230
  @db.execute('INSERT INTO blobs VALUES (?)', data)
219
- result = @db.query_single_row(sql, @db.last_insert_rowid)
231
+ result = @db.query_single(sql, @db.last_insert_rowid)
220
232
  assert_equal 'blob', result[:type]
221
233
  assert_equal Encoding::ASCII_8BIT, result[:data].encoding
222
234
  assert_equal 'Hello, 世界!', result[:data].force_encoding(Encoding::UTF_8)
223
235
  end
224
236
 
225
237
  def test_parameter_binding_for_simple_types
226
- assert_nil @db.query_single_value('select ?', nil)
238
+ assert_nil @db.query_single_argv('select ?', nil)
227
239
 
228
240
  # 32-bit integers
229
- assert_equal -2** 31, @db.query_single_value('select ?', -2**31)
230
- assert_equal 2**31 - 1, @db.query_single_value('select ?', 2**31 - 1)
241
+ assert_equal(-2** 31, @db.query_single_argv('select ?', -2**31))
242
+ assert_equal(2**31 - 1, @db.query_single_argv('select ?', 2**31 - 1))
231
243
 
232
244
  # 64-bit integers
233
- assert_equal -2 ** 63, @db.query_single_value('select ?', -2 ** 63)
234
- assert_equal 2**63 - 1, @db.query_single_value('select ?', 2**63 - 1)
245
+ assert_equal(-2 ** 63, @db.query_single_argv('select ?', -2 ** 63))
246
+ assert_equal(2**63 - 1, @db.query_single_argv('select ?', 2**63 - 1))
235
247
 
236
248
  # floats
237
- assert_equal Float::MIN, @db.query_single_value('select ?', Float::MIN)
238
- assert_equal Float::MAX, @db.query_single_value('select ?', Float::MAX)
249
+ assert_equal Float::MIN, @db.query_single_argv('select ?', Float::MIN)
250
+ assert_equal Float::MAX, @db.query_single_argv('select ?', Float::MAX)
239
251
 
240
252
  # boolean
241
- assert_equal 1, @db.query_single_value('select ?', true)
242
- assert_equal 0, @db.query_single_value('select ?', false)
253
+ assert_equal 1, @db.query_single_argv('select ?', true)
254
+ assert_equal 0, @db.query_single_argv('select ?', false)
243
255
 
244
256
  # strings and symbols
245
- assert_equal 'foo', @db.query_single_value('select ?', 'foo')
246
- assert_equal 'foo', @db.query_single_value('select ?', :foo)
257
+ assert_equal 'foo', @db.query_single_argv('select ?', 'foo')
258
+ assert_equal 'foo', @db.query_single_argv('select ?', :foo)
247
259
  end
248
260
 
249
261
  def test_value_casting
250
- r = @db.query_single_value("select 'abc'")
262
+ r = @db.query_single_argv("select 'abc'")
251
263
  assert_equal 'abc', r
252
264
 
253
- r = @db.query_single_value('select 123')
265
+ r = @db.query_single_argv('select 123')
254
266
  assert_equal 123, r
255
267
 
256
- r = @db.query_single_value('select 12.34')
268
+ r = @db.query_single_argv('select 12.34')
257
269
  assert_equal 12.34, r
258
270
 
259
- r = @db.query_single_value('select zeroblob(4)')
271
+ r = @db.query_single_argv('select zeroblob(4)')
260
272
  assert_equal "\x00\x00\x00\x00", r
261
273
 
262
- r = @db.query_single_value('select null')
274
+ r = @db.query_single_argv('select null')
263
275
  assert_nil r
264
276
  end
265
277
 
@@ -271,7 +283,7 @@ class DatabaseTest < MiniTest::Test
271
283
  @db.load_extension(File.join(__dir__, 'extensions/text.dylib'))
272
284
  end
273
285
 
274
- r = @db.query_single_value("select reverse('abcd')")
286
+ r = @db.query_single_argv("select reverse('abcd')")
275
287
  assert_equal 'dcba', r
276
288
  end
277
289
 
@@ -592,7 +604,7 @@ class DatabaseTest < MiniTest::Test
592
604
  ], array
593
605
  end
594
606
 
595
- def test_batch_query_single_column_with_array
607
+ def test_batch_query_argv_with_array
596
608
  @db.query('create table foo (a, b, c)')
597
609
  assert_equal [], @db.query('select * from foo')
598
610
 
@@ -600,20 +612,20 @@ class DatabaseTest < MiniTest::Test
600
612
  [1, '2', 3],
601
613
  ['4', 5, 6]
602
614
  ]
603
- results = @db.batch_query_single_column('insert into foo values (?, ?, ?) returning c', data)
615
+ results = @db.batch_query_argv('insert into foo values (?, ?, ?) returning c', data)
604
616
  assert_equal [
605
617
  [3],
606
618
  [6]
607
619
  ], results
608
620
 
609
- results = @db.batch_query_single_column('update foo set c = ? returning c * 10 + cast(b as integer)', [42, 43])
621
+ results = @db.batch_query_argv('update foo set c = ? returning c * 10 + cast(b as integer)', [42, 43])
610
622
  assert_equal [
611
623
  [422, 425],
612
624
  [432, 435]
613
625
  ], results
614
626
 
615
627
  array = []
616
- changes = @db.batch_query_single_column('update foo set c = ? returning c * 10 + cast(b as integer)', [44, 45]) do |rows|
628
+ changes = @db.batch_query_argv('update foo set c = ? returning c * 10 + cast(b as integer)', [44, 45]) do |rows|
617
629
  array << rows
618
630
  end
619
631
  assert_equal 4, changes
@@ -623,25 +635,25 @@ class DatabaseTest < MiniTest::Test
623
635
  ], array
624
636
  end
625
637
 
626
- def test_batch_query_single_column_with_enumerable
638
+ def test_batch_query_argv_with_enumerable
627
639
  @db.query('create table foo (a integer primary key, b)')
628
640
  assert_equal [], @db.query('select * from foo')
629
641
 
630
- results = @db.batch_query_single_column('insert into foo (b) values (?) returning b * 10 + a', 11..13)
642
+ results = @db.batch_query_argv('insert into foo (b) values (?) returning b * 10 + a', 11..13)
631
643
  assert_equal [
632
644
  [111],
633
645
  [122],
634
646
  [133]
635
647
  ], results
636
648
 
637
- results = @db.batch_query_single_column('update foo set b = ? returning b * 10 + a', 42..43)
649
+ results = @db.batch_query_argv('update foo set b = ? returning b * 10 + a', 42..43)
638
650
  assert_equal [
639
651
  [421, 422, 423],
640
652
  [431, 432, 433]
641
653
  ], results
642
654
 
643
655
  array = []
644
- changes = @db.batch_query_single_column('update foo set b = ? returning b * 10 + a', 44..45) do |rows|
656
+ changes = @db.batch_query_argv('update foo set b = ? returning b * 10 + a', 44..45) do |rows|
645
657
  array << rows
646
658
  end
647
659
  assert_equal 6, changes
@@ -651,13 +663,13 @@ class DatabaseTest < MiniTest::Test
651
663
  ], array
652
664
  end
653
665
 
654
- def test_batch_query_single_column_with_proc
666
+ def test_batch_query_argv_with_proc
655
667
  @db.query('create table foo (a integer primary key, b)')
656
668
  assert_equal [], @db.query('select * from foo')
657
669
 
658
670
  pr = parameter_source_proc([5, 4, 3])
659
671
  assert_kind_of Proc, pr
660
- results = @db.batch_query_single_column('insert into foo (b) values (?) returning b', pr)
672
+ results = @db.batch_query_argv('insert into foo (b) values (?) returning b', pr)
661
673
  assert_equal [
662
674
  [5],
663
675
  [4],
@@ -665,7 +677,7 @@ class DatabaseTest < MiniTest::Test
665
677
  ], results
666
678
 
667
679
  pr = parameter_source_proc([42, 43])
668
- results = @db.batch_query_single_column('update foo set b = ? returning b * 10 + a', pr)
680
+ results = @db.batch_query_argv('update foo set b = ? returning b * 10 + a', pr)
669
681
  assert_equal [
670
682
  [421, 422, 423],
671
683
  [431, 432, 433]
@@ -673,7 +685,7 @@ class DatabaseTest < MiniTest::Test
673
685
 
674
686
  array = []
675
687
  pr = parameter_source_proc([44, 45])
676
- changes = @db.batch_query_single_column('update foo set b = ? returning b * 10 + a', pr) do |rows|
688
+ changes = @db.batch_query_argv('update foo set b = ? returning b * 10 + a', pr) do |rows|
677
689
  array << rows
678
690
  end
679
691
  assert_equal 6, changes
@@ -703,6 +715,8 @@ class DatabaseTest < MiniTest::Test
703
715
  SQL
704
716
  }
705
717
  t.join
718
+ ensure
719
+ t&.kill
706
720
  end
707
721
 
708
722
  def test_database_status
@@ -766,6 +780,8 @@ class DatabaseTest < MiniTest::Test
766
780
 
767
781
  db2.busy_timeout = nil
768
782
  assert_raises(Extralite::BusyError) { db2.query('begin exclusive') }
783
+ ensure
784
+ t&.kill
769
785
  end
770
786
 
771
787
  def test_database_total_changes
@@ -817,30 +833,31 @@ class DatabaseTest < MiniTest::Test
817
833
  db = Extralite::Database.new(':memory:', gvl_release_threshold: 23)
818
834
  assert_equal 23, db.gvl_release_threshold
819
835
 
820
- fn = Tempfile.new('extralite_test_database_initialize_options_1').path
821
- db = Extralite::Database.new(fn, wal_journal_mode: true)
836
+ fn = Tempfile.new('extralite_test_database_initialize_options_wal').path
837
+ db = Extralite::Database.new(fn, wal: true)
822
838
  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
839
  assert_equal 1, db.pragma(:synchronous)
840
+
841
+ fn = Tempfile.new('extralite_test_database_initialize_options_pragma').path
842
+ db = Extralite::Database.new(fn, pragma: { application_id: 42 })
843
+ assert_equal 42, db.pragma(:application_id)
827
844
  end
828
845
 
829
846
  def test_database_inspect
830
847
  db = Extralite::Database.new(':memory:')
831
- assert_match /^\#\<Extralite::Database:0x[0-9a-f]+ :memory:\>$/, db.inspect
848
+ assert_match(/^\#\<Extralite::Database:0x[0-9a-f]+ :memory:\>$/, db.inspect)
832
849
  end
833
850
 
834
851
  def test_database_inspect_on_closed_database
835
852
  db = Extralite::Database.new(':memory:')
836
- assert_match /^\#\<Extralite::Database:0x[0-9a-f]+ :memory:\>$/, db.inspect
853
+ assert_match(/^\#\<Extralite::Database:0x[0-9a-f]+ :memory:\>$/, db.inspect)
837
854
  db.close
838
- assert_match /^\#\<Extralite::Database:0x[0-9a-f]+ \(closed\)\>$/, db.inspect
855
+ assert_match(/^\#\<Extralite::Database:0x[0-9a-f]+ \(closed\)\>$/, db.inspect)
839
856
  end
840
857
 
841
858
  def test_string_encoding
842
859
  db = Extralite::Database.new(':memory:')
843
- v = db.query_single_value("select 'foo'")
860
+ v = db.query_single_argv("select 'foo'")
844
861
  assert_equal 'foo', v
845
862
  assert_equal 'UTF-8', v.encoding.name
846
863
  end
@@ -856,7 +873,7 @@ class DatabaseTest < MiniTest::Test
856
873
 
857
874
  q1 = Queue.new
858
875
  q2 = Queue.new
859
- th = Thread.new do
876
+ t = Thread.new do
860
877
  db1.transaction do
861
878
  assert_equal true, db1.transaction_active?
862
879
  db1.execute('insert into foo values (42)')
@@ -871,9 +888,11 @@ class DatabaseTest < MiniTest::Test
871
888
  assert_equal [], db2.query('select * from foo')
872
889
 
873
890
  q2 << true
874
- th.join
891
+ t.join
875
892
  # transaction now committed
876
893
  assert_equal [{ x: 42 }], db2.query('select * from foo')
894
+ ensure
895
+ t&.kill
877
896
  end
878
897
 
879
898
  def test_database_transaction_rollback
@@ -923,29 +942,82 @@ class DatabaseTest < MiniTest::Test
923
942
  assert_equal [], db.query('select * from foo')
924
943
 
925
944
  db.execute('insert into foo values (42)')
926
- assert_equal [42], db.query_single_column('select x from foo')
945
+ assert_equal [42], db.query_argv('select x from foo')
927
946
 
928
947
  db.savepoint(:a)
929
948
 
930
949
  db.execute('insert into foo values (43)')
931
- assert_equal [42, 43], db.query_single_column('select x from foo')
950
+ assert_equal [42, 43], db.query_argv('select x from foo')
932
951
 
933
952
  db.savepoint(:b)
934
953
 
935
954
  db.execute('insert into foo values (44)')
936
- assert_equal [42, 43, 44], db.query_single_column('select x from foo')
955
+ assert_equal [42, 43, 44], db.query_argv('select x from foo')
937
956
 
938
957
  db.rollback_to(:b)
939
- assert_equal [42, 43], db.query_single_column('select x from foo')
958
+ assert_equal [42, 43], db.query_argv('select x from foo')
940
959
 
941
960
  db.release(:a)
942
961
 
943
- assert_equal [42, 43], db.query_single_column('select x from foo')
962
+ assert_equal [42, 43], db.query_argv('select x from foo')
944
963
  end
945
964
  end
965
+
966
+ def test_prepare
967
+ q = @db.prepare('select * from t order by x')
968
+ assert_kind_of Extralite::Query, q
969
+
970
+ assert_equal [
971
+ { x: 1, y: 2, z: 3},
972
+ { x: 4, y: 5, z: 6}
973
+ ], q.to_a
974
+ end
975
+
976
+ def test_prepare_argv
977
+ q = @db.prepare_argv('select * from t order by x')
978
+ assert_kind_of Extralite::Query, q
979
+
980
+ buf = []
981
+ q.each { |x, y, z| buf << z }
982
+ assert_equal [3, 6], buf
983
+ end
984
+
985
+ def test_prepare_ary
986
+ q = @db.prepare_ary('select * from t order by x')
987
+ assert_kind_of Extralite::Query, q
988
+
989
+ assert_equal [
990
+ [1, 2, 3],
991
+ [4, 5, 6]
992
+ ], q.to_a
993
+ end
994
+
995
+ def test_prepare_with_transform
996
+ q = @db.prepare('select * from t order by x') { |h| h[:x] * 100 + h[:y] * 10 + h[:z] }
997
+ assert_equal [
998
+ 123,
999
+ 456
1000
+ ], q.to_a
1001
+ end
1002
+
1003
+ def test_prepare_argv_with_transform
1004
+ q = @db.prepare_argv('select * from t order by x') { |x, y, z| x * 100 + y * 10 + z }
1005
+ assert_equal [
1006
+ 123,
1007
+ 456
1008
+ ], q.to_a
1009
+ end
1010
+
1011
+ def test_prepare_ary_with_transform
1012
+ q = @db.prepare_ary('select * from t order by x') { |r| r * 2 }
1013
+ assert_equal [
1014
+ [1, 2, 3, 1, 2, 3],
1015
+ [4, 5, 6, 4, 5, 6]
1016
+ ], q.to_a
1017
+ end
946
1018
  end
947
1019
 
948
- class ScenarioTest < MiniTest::Test
1020
+ class ScenarioTest < Minitest::Test
949
1021
  def setup
950
1022
  @fn = Tempfile.new('extralite_scenario_test').path
951
1023
  @db = Extralite::Database.new(@fn)
@@ -974,7 +1046,7 @@ class ScenarioTest < MiniTest::Test
974
1046
 
975
1047
  sleep 0.1
976
1048
  @db.query 'begin deferred'
977
- result = @db.query_single_column('select x from t')
1049
+ result = @db.query_argv('select x from t')
978
1050
  assert_equal [1, 4], result
979
1051
 
980
1052
  assert_raises(Extralite::BusyError) do
@@ -1008,8 +1080,10 @@ class ScenarioTest < MiniTest::Test
1008
1080
  @db.query('insert into t values (7, 8, 9)')
1009
1081
  @db.query('commit')
1010
1082
 
1011
- result = @db.query_single_column('select x from t')
1083
+ result = @db.query_argv('select x from t')
1012
1084
  assert_equal [1, 4, 7], result
1085
+ ensure
1086
+ t&.kill
1013
1087
  end
1014
1088
 
1015
1089
  def test_concurrent_queries
@@ -1035,7 +1109,10 @@ class ScenarioTest < MiniTest::Test
1035
1109
  t1.join
1036
1110
  t2.join
1037
1111
 
1038
- assert_equal (1..100).to_a, @db.query_single_column('select x from t order by x')
1112
+ assert_equal (1..100).to_a, @db.query_argv('select x from t order by x')
1113
+ ensure
1114
+ t1&.kill
1115
+ t2&.kill
1039
1116
  end
1040
1117
 
1041
1118
  def test_database_trace
@@ -1064,7 +1141,7 @@ class ScenarioTest < MiniTest::Test
1064
1141
  end
1065
1142
  end
1066
1143
 
1067
- class BackupTest < MiniTest::Test
1144
+ class BackupTest < Minitest::Test
1068
1145
  def setup
1069
1146
  @src = Extralite::Database.new(':memory:')
1070
1147
  @dst = Extralite::Database.new(':memory:')
@@ -1144,6 +1221,9 @@ class ConcurrencyTest < Minitest::Test
1144
1221
 
1145
1222
  assert delays.size > 4
1146
1223
  assert_equal 0, delays.select { |d| d > 0.15 }.size
1224
+ ensure
1225
+ t1&.kill
1226
+ t2&.kill
1147
1227
  end
1148
1228
 
1149
1229
  def test_gvl_always_hold
@@ -1178,6 +1258,9 @@ class ConcurrencyTest < Minitest::Test
1178
1258
 
1179
1259
  assert delays.size >= 1
1180
1260
  assert delays.first > 0.2
1261
+ ensure
1262
+ t1&.kill
1263
+ t2&.kill
1181
1264
  end
1182
1265
 
1183
1266
  def test_gvl_mode_get_set
@@ -1200,20 +1283,165 @@ class ConcurrencyTest < Minitest::Test
1200
1283
  db = Extralite::Database.new(':memory:')
1201
1284
 
1202
1285
  buf = []
1203
- db.on_progress(1) { buf << :progress }
1286
+ db.on_progress(period: 1) { buf << :progress }
1204
1287
 
1205
- result = db.query_single_row('select 1 as a, 2 as b, 3 as c')
1288
+ result = db.query_single('select 1 as a, 2 as b, 3 as c')
1206
1289
  assert_equal({ a: 1, b: 2, c: 3 }, result)
1207
1290
  assert_in_range 5..7, buf.size
1208
1291
 
1209
1292
  buf = []
1210
- db.on_progress(2) { buf << :progress }
1293
+ db.on_progress(period: 2) { buf << :progress }
1211
1294
 
1212
- result = db.query_single_row('select 1 as a, 2 as b, 3 as c')
1295
+ result = db.query_single('select 1 as a, 2 as b, 3 as c')
1213
1296
  assert_equal({ a: 1, b: 2, c: 3 }, result)
1214
1297
  assert_in_range 2..4, buf.size
1215
1298
  end
1216
1299
 
1300
+ def test_progress_handler_normal_mode
1301
+ db = Extralite::Database.new(':memory:')
1302
+
1303
+ count = 0
1304
+ db.on_progress(period: 1) { count += 1 }
1305
+ db.query('select 1 as a')
1306
+ assert count > 0
1307
+ base_count = count
1308
+
1309
+ count = 0
1310
+ db.on_progress(period: 1) { count += 1 }
1311
+ 10.times { db.query('select 1 as a') }
1312
+ assert_equal base_count * 10, count
1313
+
1314
+ count = 0
1315
+ db.on_progress(period: 10, tick: 1) { count += 1 }
1316
+ 10.times { db.query('select 1 as a') }
1317
+ assert_equal base_count, count
1318
+ end
1319
+
1320
+ def test_progress_handler_at_least_once_mode
1321
+ db = Extralite::Database.new(':memory:')
1322
+
1323
+ count = 0
1324
+ db.on_progress(period: 1) { count += 1 }
1325
+ db.query('select 1 as a')
1326
+ assert count > 0
1327
+ base_count = count
1328
+
1329
+ count = 0
1330
+ db.on_progress(period: 1, mode: :at_least_once) { count += 1 }
1331
+ 10.times { db.query('select 1 as a') }
1332
+ assert_equal base_count * 10 + 10, count
1333
+
1334
+ count = 0
1335
+ db.on_progress(period: 10, tick: 1, mode: :at_least_once) { count += 1 }
1336
+ 10.times { db.query('select 1 as a') }
1337
+ assert_equal base_count + 10, count
1338
+ end
1339
+
1340
+ def test_progress_handler_once_mode
1341
+ db = Extralite::Database.new(':memory:')
1342
+
1343
+ count = 0
1344
+ db.on_progress(mode: :once) { count += 1 }
1345
+ 10.times { db.query('select 1 as a') }
1346
+ assert_equal 10, count
1347
+
1348
+ count = 0
1349
+ db.on_progress(period: 1, mode: :once) { count += 1 }
1350
+ 10.times { db.query('select 1 as a') }
1351
+ assert_equal 10, count
1352
+
1353
+ count = 0
1354
+ db.on_progress(period: 10, tick: 1, mode: :once) { count += 1 }
1355
+ 10.times { db.query('select 1 as a') }
1356
+ assert_equal 10, count
1357
+ end
1358
+
1359
+ def test_progress_handler_once_mode_with_batch_query
1360
+ db = Extralite::Database.new(':memory:')
1361
+
1362
+ count = 0
1363
+ db.on_progress(period: 1, mode: :once) { count += 1 }
1364
+ db.batch_query('select ?', 1..10)
1365
+ assert_equal 10, count
1366
+
1367
+ db.batch_query('select ?', 1..3)
1368
+ assert_equal 13, count
1369
+ end
1370
+
1371
+ def test_progress_handler_reset
1372
+ db = Extralite::Database.new(':memory:')
1373
+
1374
+ count = 0
1375
+ set_progress = -> {
1376
+ count = 0
1377
+ db.on_progress(mode: :once) { count += 1 }
1378
+ }
1379
+
1380
+ set_progress.()
1381
+ 10.times { db.query('select 1 as a') }
1382
+ assert_equal 10, count
1383
+
1384
+ count = 0
1385
+ db.on_progress(mode: :none)
1386
+ 10.times { db.query('select 1 as a') }
1387
+ assert_equal 0, count
1388
+
1389
+ set_progress.()
1390
+ 10.times { db.query('select 1 as a') }
1391
+ assert_equal 10, count
1392
+
1393
+ count = 0
1394
+ db.on_progress(period: 0) { foo }
1395
+ 10.times { db.query('select 1 as a') }
1396
+ assert_equal 0, count
1397
+
1398
+ set_progress.()
1399
+ 10.times { db.query('select 1 as a') }
1400
+ assert_equal 10, count
1401
+
1402
+ count = 0
1403
+ db.on_progress
1404
+ 10.times { db.query('select 1 as a') }
1405
+ assert_equal 0, count
1406
+ end
1407
+
1408
+ def test_progress_handler_invalid_arg
1409
+ db = Extralite::Database.new(':memory:')
1410
+
1411
+ assert_raises(TypeError) { db.on_progress(period: :foo) }
1412
+ assert_raises(TypeError) { db.on_progress(tick: :foo) }
1413
+ assert_raises(ArgumentError) { db.on_progress(mode: :foo) }
1414
+ end
1415
+
1416
+ def test_progress_handler_once_mode_with_prepared_query
1417
+ db = Extralite::Database.new(':memory:')
1418
+ db.execute 'create table foo (x)'
1419
+ db.batch_query('insert into foo values (?)', 1..10)
1420
+ q = db.prepare('select x from foo')
1421
+
1422
+ count = 0
1423
+ db.on_progress(period: 1, mode: :once) { count += 1 }
1424
+
1425
+ q.to_a
1426
+ assert_equal 1, count
1427
+
1428
+ q.reset
1429
+ record_count = 0
1430
+ assert_equal 2, count
1431
+ while q.next
1432
+ record_count += 1
1433
+ end
1434
+ assert_equal 10, record_count
1435
+ assert_equal 2, count
1436
+
1437
+ q.reset
1438
+ assert_equal 3, count
1439
+ while q.next
1440
+ record_count += 1
1441
+ end
1442
+ assert_equal 3, count
1443
+ end
1444
+
1217
1445
  LONG_QUERY = <<~SQL
1218
1446
  WITH RECURSIVE
1219
1447
  fibo (curr, next)
@@ -1228,7 +1456,7 @@ class ConcurrencyTest < Minitest::Test
1228
1456
  def test_progress_handler_timeout_interrupt
1229
1457
  db = Extralite::Database.new(':memory:')
1230
1458
  t0 = Time.now
1231
- db.on_progress(1000) do
1459
+ db.on_progress do
1232
1460
  Thread.pass
1233
1461
  db.interrupt if Time.now - t0 >= 0.2
1234
1462
  end
@@ -1270,7 +1498,7 @@ class ConcurrencyTest < Minitest::Test
1270
1498
  def test_progress_handler_timeout_raise
1271
1499
  db = Extralite::Database.new(':memory:')
1272
1500
  t0 = Time.now
1273
- db.on_progress(1000) do
1501
+ db.on_progress do
1274
1502
  Thread.pass
1275
1503
  raise CustomTimeoutError if Time.now - t0 >= 0.2
1276
1504
  end
@@ -1315,7 +1543,7 @@ class ConcurrencyTest < Minitest::Test
1315
1543
  assert_raises(Extralite::BusyError) { db2.query('begin exclusive') }
1316
1544
 
1317
1545
  t0 = Time.now
1318
- db2.on_progress(1000) do
1546
+ db2.on_progress do
1319
1547
  Thread.pass
1320
1548
  raise CustomTimeoutError if Time.now - t0 >= 0.2
1321
1549
  end
@@ -1348,6 +1576,29 @@ class ConcurrencyTest < Minitest::Test
1348
1576
  assert_equal 1, ((t1 - t0) * 5).round.to_i
1349
1577
  assert_kind_of CustomTimeoutError, err
1350
1578
  end
1579
+
1580
+ def test_global_progress_handler
1581
+ count = 0
1582
+ Extralite.on_progress(tick: 1, period: 1) { count += 1 }
1583
+
1584
+ db = Extralite::Database.new(':memory:')
1585
+ 10.times { db.query('select 1') }
1586
+ refute_equal 0, count
1587
+
1588
+ old_count = count
1589
+ Extralite.on_progress # remove global progress handler
1590
+
1591
+ # already opened db should preserve progress handler behaviour
1592
+ 10.times { db.query('select 1') }
1593
+ refute_equal old_count, count
1594
+
1595
+ old_count = count
1596
+ db2 = Extralite::Database.new(':memory:')
1597
+ 10.times { db2.query('select 1') }
1598
+ assert_equal old_count, count
1599
+ ensure
1600
+ Extralite.on_progress(mode: :none)
1601
+ end
1351
1602
  end
1352
1603
 
1353
1604
  class RactorTest < Minitest::Test
@@ -1369,67 +1620,232 @@ class RactorTest < Minitest::Test
1369
1620
  r << 42
1370
1621
  r.take # wait for ractor to terminate
1371
1622
 
1372
- assert_equal 42, db.query_single_value('select x from foo')
1623
+ assert_equal 42, db.query_single_argv('select x from foo')
1373
1624
  end
1374
1625
 
1375
1626
  # Adapted from here: https://github.com/sparklemotion/sqlite3-ruby/pull/365/files
1376
1627
  def test_ractor_share_database
1377
- skip if SKIP_RACTOR_TESTS
1628
+ skip "skipped for now as ractors seem kinda flakey (failing sporadically)"
1378
1629
 
1379
1630
  db_receiver = Ractor.new do
1380
1631
  db = Ractor.receive
1381
1632
  Ractor.yield db.object_id
1382
1633
  begin
1383
- db.execute("create table foo (b)")
1384
- raise "Should have raised an exception in db.execute()"
1634
+ db.execute('create table foo (b)')
1635
+ raise 'Should have raised an exception in db.execute()'
1385
1636
  rescue => e
1386
1637
  Ractor.yield e
1387
1638
  end
1388
1639
  end
1389
1640
  sleep 0.1
1390
1641
  db_creator = Ractor.new(db_receiver) do |db_receiver|
1391
- db = Extralite::Database.new(":memory:")
1642
+ db = Extralite::Database.new(':memory:')
1392
1643
  Ractor.yield db.object_id
1393
1644
  db_receiver.send(db)
1394
1645
  sleep 0.1
1395
- db.execute("create table foo (a)")
1646
+ db.execute('create table foo (a)')
1396
1647
  end
1397
1648
  first_oid = db_creator.take
1398
1649
  second_oid = db_receiver.take
1399
1650
  refute_equal first_oid, second_oid
1400
1651
  ex = db_receiver.take
1401
1652
  assert_kind_of Extralite::Error, ex
1402
- assert_equal "Database is closed", ex.message
1653
+ assert_equal 'Database is closed', ex.message
1403
1654
  end
1404
1655
 
1405
- STRESS_DB_NAME = Tempfile.new('extralite_test_ractor_stress').path
1406
-
1407
1656
  # Adapted from here: https://github.com/sparklemotion/sqlite3-ruby/pull/365/files
1408
1657
  def test_ractor_stress
1409
1658
  skip if SKIP_RACTOR_TESTS
1410
-
1411
- Ractor.make_shareable(STRESS_DB_NAME)
1412
1659
 
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)")
1660
+ fn = Tempfile.new('extralite_test_ractor_stress').path
1416
1661
  random = Random.new.freeze
1662
+
1417
1663
  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
1664
+ sleep 0.05
1665
+ Ractor.new(fn, random, ractor_number) do |rfn, r, n|
1666
+ rdb = Extralite::Database.new(rfn)
1667
+ rdb.busy_timeout = 3
1668
+ rdb.pragma(journal_mode: 'wal', synchronous: 1)
1669
+ rdb.execute('create table if not exists stress_test (a integer primary_key, b text)')
1670
+ changes = 0
1421
1671
  10.times do |i|
1422
- db_in_ractor.execute("insert into stress_test(a, b) values (#{ractor_number * 100 + i}, '#{random.rand}')")
1672
+ changes += rdb.execute('insert into stress_test(a, b) values (?, ?)', n * 100 + i, r.rand)
1423
1673
  end
1674
+ Ractor.yield changes
1424
1675
  end
1425
1676
  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")
1677
+
1678
+ buf = []
1679
+ ractors.each { |r| buf << r.take }
1680
+ assert_equal [10] * 10, buf
1681
+
1682
+ final_check = Ractor.new(fn) do |rfn|
1683
+ rdb = Extralite::Database.new(rfn, wal: true)
1684
+ count = rdb.query_single_argv('select count(*) from stress_test')
1430
1685
  Ractor.yield count
1431
1686
  end
1432
1687
  count = final_check.take
1433
1688
  assert_equal 100, count
1434
1689
  end
1435
1690
  end
1691
+
1692
+ class DatabaseTransformTest < Minitest::Test
1693
+ def setup
1694
+ @db = Extralite::Database.new(':memory:')
1695
+ @db.query('create table t (a, b, c)')
1696
+
1697
+ @q3 = @db.prepare('select * from t where a = ?')
1698
+ @q4 = @db.prepare('select * from t order by a')
1699
+
1700
+ @db.batch_execute('insert into t (a, b, c) values (?, ?, ?)', [
1701
+ [1, 2, { foo: 42, bar: 43 }.to_json],
1702
+ [4, 5, { foo: 45, bar: 46 }.to_json]
1703
+ ])
1704
+ end
1705
+
1706
+ class MyModel
1707
+ def initialize(h)
1708
+ @h = h
1709
+ end
1710
+
1711
+ def values
1712
+ @h
1713
+ end
1714
+ end
1715
+
1716
+ def test_query_hash_transform
1717
+ transform = ->(h) { MyModel.new(h) }
1718
+
1719
+ sql = 'select a, b from t where a = ?'
1720
+ o = @db.query(transform, sql, 1).first
1721
+ assert_kind_of MyModel, o
1722
+ assert_equal({ a: 1, b: 2 }, o.values)
1723
+
1724
+ o = @db.query(transform, sql, 4).first
1725
+ assert_kind_of MyModel, o
1726
+ assert_equal({ a: 4, b: 5 }, o.values)
1727
+
1728
+ sql = 'select a, b from t order by a'
1729
+ assert_equal [
1730
+ { a: 1, b: 2 },
1731
+ { a: 4, b: 5 }
1732
+ ], @db.query(transform, sql).map(&:values)
1733
+
1734
+ buf = []
1735
+ @db.query(transform, sql) { |r| buf << r.values }
1736
+ assert_equal [
1737
+ { a: 1, b: 2 },
1738
+ { a: 4, b: 5 }
1739
+ ], buf
1740
+ end
1741
+
1742
+ def test_query_ary_transform
1743
+ transform = ->(h) { MyModel.new(h) }
1744
+
1745
+ sql = 'select a, b from t where a = ?'
1746
+ o = @db.query_ary(transform, sql, 1).first
1747
+ assert_kind_of MyModel, o
1748
+ assert_equal([1, 2], o.values)
1749
+
1750
+ o = @db.query_ary(transform, sql, 4).first
1751
+ assert_kind_of MyModel, o
1752
+ assert_equal([4, 5], o.values)
1753
+
1754
+ sql = 'select a, b from t order by a'
1755
+ assert_equal [
1756
+ [1, 2],
1757
+ [4, 5]
1758
+ ], @db.query_ary(transform, sql).map(&:values)
1759
+
1760
+ buf = []
1761
+ @db.query_ary(transform, sql) { |r| buf << r.values }
1762
+ assert_equal [
1763
+ [1, 2],
1764
+ [4, 5]
1765
+ ], buf
1766
+ end
1767
+
1768
+ def test_query_hash_single_row_transform
1769
+ transform = ->(h) { MyModel.new(h) }
1770
+
1771
+ sql = 'select a, b from t where a = ?'
1772
+ o = @db.query_single(transform, sql, 1)
1773
+ assert_kind_of MyModel, o
1774
+ assert_equal({ a: 1, b: 2 }, o.values)
1775
+
1776
+ o = @db.query_single(transform, sql, 4)
1777
+ assert_kind_of MyModel, o
1778
+ assert_equal({ a: 4, b: 5 }, o.values)
1779
+ end
1780
+
1781
+ def test_query_ary_single_row_transform
1782
+ transform = ->(h) { MyModel.new(h) }
1783
+
1784
+ sql = 'select a, b from t where a = ?'
1785
+ o = @db.query_single_ary(transform, sql, 1)
1786
+ assert_kind_of MyModel, o
1787
+ assert_equal([1, 2], o.values)
1788
+
1789
+ o = @db.query_single_ary(transform, sql, 4)
1790
+ assert_kind_of MyModel, o
1791
+ assert_equal([4, 5], o.values)
1792
+ end
1793
+
1794
+ def test_query_argv_single_column_transform
1795
+ transform = ->(c) { JSON.parse(c, symbolize_names: true) }
1796
+ sql = 'select c from t where a = ?'
1797
+
1798
+ assert_equal([{ foo: 42, bar: 43 }], @db.query_argv(transform, sql, 1))
1799
+ assert_equal([{ foo: 45, bar: 46 }], @db.query_argv(transform, sql, 4))
1800
+
1801
+ sql = 'select c from t order by a'
1802
+ assert_equal [
1803
+ { foo: 42, bar: 43 },
1804
+ { foo: 45, bar: 46 }
1805
+ ], @db.query_argv(transform, sql)
1806
+
1807
+ buf = []
1808
+ @db.query_argv(transform, sql) { |r| buf << r }
1809
+ assert_equal [
1810
+ { foo: 42, bar: 43 },
1811
+ { foo: 45, bar: 46 }
1812
+ ], buf
1813
+ end
1814
+
1815
+ def test_query_argv_single_row_single_column
1816
+ transform = ->(c) { JSON.parse(c, symbolize_names: true) }
1817
+ sql = 'select c from t where a = ?'
1818
+
1819
+ assert_equal({ foo: 42, bar: 43 }, @db.query_single_argv(transform, sql, 1))
1820
+ assert_equal({ foo: 45, bar: 46 }, @db.query_single_argv(transform, sql, 4))
1821
+ end
1822
+
1823
+ def test_query_argv_multi_column
1824
+ transform = ->(a, b, c) { { a: a, b: b, c: JSON.parse(c, symbolize_names: true) } }
1825
+ sql = 'select * from t where a = ?'
1826
+
1827
+ assert_equal([{ a: 1, b: 2, c: { foo: 42, bar: 43 }}], @db.query_argv(transform, sql, 1))
1828
+ assert_equal([{ a: 4, b: 5, c: { foo: 45, bar: 46 }}], @db.query_argv(transform, sql, 4))
1829
+
1830
+ sql = 'select * from t order by a'
1831
+ assert_equal [
1832
+ { a: 1, b: 2, c: { foo: 42, bar: 43 }},
1833
+ { a: 4, b: 5, c: { foo: 45, bar: 46 }}
1834
+ ], @db.query_argv(transform, sql)
1835
+
1836
+ buf = []
1837
+ @db.query_argv(transform, sql) { |r| buf << r }
1838
+ assert_equal [
1839
+ { a: 1, b: 2, c: { foo: 42, bar: 43 }},
1840
+ { a: 4, b: 5, c: { foo: 45, bar: 46 }}
1841
+ ], buf
1842
+ end
1843
+
1844
+ def test_query_argv_single_row_multi_column
1845
+ transform = ->(a, b, c) { { a: a, b: b, c: JSON.parse(c, symbolize_names: true) } }
1846
+ sql = 'select * from t where a = ?'
1847
+
1848
+ assert_equal({ a: 1, b: 2, c: { foo: 42, bar: 43 }}, @db.query_single_argv(transform, sql, 1))
1849
+ assert_equal({ a: 4, b: 5, c: { foo: 45, bar: 46 }}, @db.query_single_argv(transform, sql, 4))
1850
+ end
1851
+ end