extralite-bundle 2.5 → 2.7

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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +34 -13
  4. data/Gemfile +4 -0
  5. data/Gemfile-bundle +1 -1
  6. data/LICENSE +1 -1
  7. data/README.md +1059 -247
  8. data/Rakefile +18 -0
  9. data/TODO.md +0 -7
  10. data/examples/kv_store.rb +49 -0
  11. data/examples/multi_fiber.rb +16 -0
  12. data/examples/on_progress.rb +9 -0
  13. data/examples/pubsub_store_polyphony.rb +194 -0
  14. data/examples/pubsub_store_threads.rb +204 -0
  15. data/ext/extralite/changeset.c +463 -0
  16. data/ext/extralite/common.c +177 -91
  17. data/ext/extralite/database.c +745 -276
  18. data/ext/extralite/extconf-bundle.rb +10 -4
  19. data/ext/extralite/extconf.rb +34 -34
  20. data/ext/extralite/extralite.h +104 -47
  21. data/ext/extralite/extralite_ext.c +6 -0
  22. data/ext/extralite/iterator.c +14 -86
  23. data/ext/extralite/query.c +171 -264
  24. data/extralite-bundle.gemspec +1 -1
  25. data/extralite.gemspec +1 -1
  26. data/gemspec.rb +10 -11
  27. data/lib/extralite/version.rb +1 -1
  28. data/lib/extralite.rb +69 -10
  29. data/lib/sequel/adapters/extralite.rb +1 -1
  30. data/test/helper.rb +9 -1
  31. data/test/perf_argv_transform.rb +74 -0
  32. data/test/perf_ary.rb +14 -12
  33. data/test/perf_hash.rb +17 -15
  34. data/test/perf_hash_prepared.rb +58 -0
  35. data/test/perf_hash_transform.rb +66 -0
  36. data/test/perf_polyphony.rb +74 -0
  37. data/test/test_changeset.rb +161 -0
  38. data/test/test_database.rb +720 -104
  39. data/test/test_extralite.rb +2 -2
  40. data/test/test_iterator.rb +28 -13
  41. data/test/test_query.rb +352 -110
  42. data/test/test_sequel.rb +4 -4
  43. metadata +24 -16
  44. data/Gemfile.lock +0 -37
  45. data/test/perf_prepared.rb +0 -64
@@ -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
62
+ end
63
+
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
+ }
56
68
  end
57
69
 
58
- def test_query_single_column
59
- r = @db.query_single_column('select y from t')
60
- assert_equal [2, 5], 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)
61
73
 
62
- r = @db.query_single_column('select y from t where x = 2')
63
- assert_equal [], r
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
@@ -896,9 +915,109 @@ class DatabaseTest < MiniTest::Test
896
915
  assert_kind_of RuntimeError, exception
897
916
  assert_equal 'bar', exception.message
898
917
  end
918
+
919
+ def test_database_transaction_rollback!
920
+ db = Extralite::Database.new(':memory:')
921
+ db.execute('create table foo(x)')
922
+
923
+ exception = nil
924
+ begin
925
+ db.transaction do
926
+ db.execute('insert into foo values (42)')
927
+ db.rollback!
928
+ end
929
+ rescue => e
930
+ exception = e
931
+ end
932
+
933
+ assert_equal [], db.query('select * from foo')
934
+ assert_nil exception
935
+ end
936
+
937
+ def test_database_savepoint
938
+ db = Extralite::Database.new(':memory:')
939
+ db.execute('create table foo(x)')
940
+
941
+ db.transaction do
942
+ assert_equal [], db.query('select * from foo')
943
+
944
+ db.execute('insert into foo values (42)')
945
+ assert_equal [42], db.query_argv('select x from foo')
946
+
947
+ db.savepoint(:a)
948
+
949
+ db.execute('insert into foo values (43)')
950
+ assert_equal [42, 43], db.query_argv('select x from foo')
951
+
952
+ db.savepoint(:b)
953
+
954
+ db.execute('insert into foo values (44)')
955
+ assert_equal [42, 43, 44], db.query_argv('select x from foo')
956
+
957
+ db.rollback_to(:b)
958
+ assert_equal [42, 43], db.query_argv('select x from foo')
959
+
960
+ db.release(:a)
961
+
962
+ assert_equal [42, 43], db.query_argv('select x from foo')
963
+ end
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
899
1018
  end
900
1019
 
901
- class ScenarioTest < MiniTest::Test
1020
+ class ScenarioTest < Minitest::Test
902
1021
  def setup
903
1022
  @fn = Tempfile.new('extralite_scenario_test').path
904
1023
  @db = Extralite::Database.new(@fn)
@@ -927,7 +1046,7 @@ class ScenarioTest < MiniTest::Test
927
1046
 
928
1047
  sleep 0.1
929
1048
  @db.query 'begin deferred'
930
- result = @db.query_single_column('select x from t')
1049
+ result = @db.query_argv('select x from t')
931
1050
  assert_equal [1, 4], result
932
1051
 
933
1052
  assert_raises(Extralite::BusyError) do
@@ -961,8 +1080,10 @@ class ScenarioTest < MiniTest::Test
961
1080
  @db.query('insert into t values (7, 8, 9)')
962
1081
  @db.query('commit')
963
1082
 
964
- result = @db.query_single_column('select x from t')
1083
+ result = @db.query_argv('select x from t')
965
1084
  assert_equal [1, 4, 7], result
1085
+ ensure
1086
+ t&.kill
966
1087
  end
967
1088
 
968
1089
  def test_concurrent_queries
@@ -988,7 +1109,10 @@ class ScenarioTest < MiniTest::Test
988
1109
  t1.join
989
1110
  t2.join
990
1111
 
991
- 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
992
1116
  end
993
1117
 
994
1118
  def test_database_trace
@@ -1017,7 +1141,7 @@ class ScenarioTest < MiniTest::Test
1017
1141
  end
1018
1142
  end
1019
1143
 
1020
- class BackupTest < MiniTest::Test
1144
+ class BackupTest < Minitest::Test
1021
1145
  def setup
1022
1146
  @src = Extralite::Database.new(':memory:')
1023
1147
  @dst = Extralite::Database.new(':memory:')
@@ -1053,7 +1177,7 @@ class BackupTest < MiniTest::Test
1053
1177
  end
1054
1178
  end
1055
1179
 
1056
- class GVLReleaseThresholdTest < Minitest::Test
1180
+ class ConcurrencyTest < Minitest::Test
1057
1181
  def setup
1058
1182
  @sql = <<~SQL
1059
1183
  WITH RECURSIVE r(i) AS (
@@ -1097,6 +1221,9 @@ class GVLReleaseThresholdTest < Minitest::Test
1097
1221
 
1098
1222
  assert delays.size > 4
1099
1223
  assert_equal 0, delays.select { |d| d > 0.15 }.size
1224
+ ensure
1225
+ t1&.kill
1226
+ t2&.kill
1100
1227
  end
1101
1228
 
1102
1229
  def test_gvl_always_hold
@@ -1131,6 +1258,9 @@ class GVLReleaseThresholdTest < Minitest::Test
1131
1258
 
1132
1259
  assert delays.size >= 1
1133
1260
  assert delays.first > 0.2
1261
+ ensure
1262
+ t1&.kill
1263
+ t2&.kill
1134
1264
  end
1135
1265
 
1136
1266
  def test_gvl_mode_get_set
@@ -1148,6 +1278,327 @@ class GVLReleaseThresholdTest < Minitest::Test
1148
1278
  db.gvl_release_threshold = nil
1149
1279
  assert_equal 1000, db.gvl_release_threshold
1150
1280
  end
1281
+
1282
+ def test_progress_handler_simple
1283
+ db = Extralite::Database.new(':memory:')
1284
+
1285
+ buf = []
1286
+ db.on_progress(period: 1) { buf << :progress }
1287
+
1288
+ result = db.query_single('select 1 as a, 2 as b, 3 as c')
1289
+ assert_equal({ a: 1, b: 2, c: 3 }, result)
1290
+ assert_in_range 5..7, buf.size
1291
+
1292
+ buf = []
1293
+ db.on_progress(period: 2) { buf << :progress }
1294
+
1295
+ result = db.query_single('select 1 as a, 2 as b, 3 as c')
1296
+ assert_equal({ a: 1, b: 2, c: 3 }, result)
1297
+ assert_in_range 2..4, buf.size
1298
+ end
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
+
1445
+ LONG_QUERY = <<~SQL
1446
+ WITH RECURSIVE
1447
+ fibo (curr, next)
1448
+ AS
1449
+ ( SELECT 1,1
1450
+ UNION ALL
1451
+ SELECT next, curr + next FROM fibo
1452
+ LIMIT 10000000 )
1453
+ SELECT curr, next FROM fibo LIMIT 1 OFFSET 10000000-1;
1454
+ SQL
1455
+
1456
+ def test_progress_handler_timeout_interrupt
1457
+ db = Extralite::Database.new(':memory:')
1458
+ t0 = Time.now
1459
+ db.on_progress do
1460
+ Thread.pass
1461
+ db.interrupt if Time.now - t0 >= 0.2
1462
+ end
1463
+
1464
+ q = db.prepare(LONG_QUERY)
1465
+ result = nil
1466
+ err = nil
1467
+ begin
1468
+ result = q.next
1469
+ rescue => e
1470
+ err = e
1471
+ end
1472
+ t1 = Time.now
1473
+
1474
+ assert_nil result
1475
+ assert_equal 1, ((t1 - t0) * 5).round.to_i
1476
+ assert_kind_of Extralite::InterruptError, err
1477
+
1478
+ # try a second time, just to make sure no undefined state is left behind
1479
+ t0 = Time.now
1480
+ q = db.prepare(LONG_QUERY)
1481
+ result = nil
1482
+ err = nil
1483
+ begin
1484
+ result = q.next
1485
+ rescue => e
1486
+ err = e
1487
+ end
1488
+ t1 = Time.now
1489
+
1490
+ assert_nil result
1491
+ assert_equal 1, ((t1 - t0) * 5).round.to_i
1492
+ assert_kind_of Extralite::InterruptError, err
1493
+ end
1494
+
1495
+ class CustomTimeoutError < RuntimeError
1496
+ end
1497
+
1498
+ def test_progress_handler_timeout_raise
1499
+ db = Extralite::Database.new(':memory:')
1500
+ t0 = Time.now
1501
+ db.on_progress do
1502
+ Thread.pass
1503
+ raise CustomTimeoutError if Time.now - t0 >= 0.2
1504
+ end
1505
+
1506
+ q = db.prepare(LONG_QUERY)
1507
+ result = nil
1508
+ err = nil
1509
+ begin
1510
+ result = q.next
1511
+ rescue => e
1512
+ err = e
1513
+ end
1514
+ t1 = Time.now
1515
+
1516
+ assert_nil result
1517
+ assert_equal 1, ((t1 - t0) * 5).round.to_i
1518
+ assert_kind_of CustomTimeoutError, err
1519
+
1520
+ # try a second time, just to make sure no undefined state is left behind
1521
+ t0 = Time.now
1522
+ q = db.prepare(LONG_QUERY)
1523
+ result = nil
1524
+ err = nil
1525
+ begin
1526
+ result = q.next
1527
+ rescue => e
1528
+ err = e
1529
+ end
1530
+ t1 = Time.now
1531
+
1532
+ assert_nil result
1533
+ assert_equal 1, ((t1 - t0) * 5).round.to_i
1534
+ assert_kind_of CustomTimeoutError, err
1535
+ end
1536
+
1537
+ def test_progress_handler_busy_timeout
1538
+ fn = Tempfile.new('extralite_test_progress_handler_busy_timeout').path
1539
+ db1 = Extralite::Database.new(fn)
1540
+ db2 = Extralite::Database.new(fn)
1541
+
1542
+ db1.query('begin exclusive')
1543
+ assert_raises(Extralite::BusyError) { db2.query('begin exclusive') }
1544
+
1545
+ t0 = Time.now
1546
+ db2.on_progress do
1547
+ Thread.pass
1548
+ raise CustomTimeoutError if Time.now - t0 >= 0.2
1549
+ end
1550
+
1551
+ result = nil
1552
+ err = nil
1553
+ begin
1554
+ result = db2.execute('begin exclusive')
1555
+ rescue => e
1556
+ err = e
1557
+ end
1558
+ t1 = Time.now
1559
+
1560
+ assert_nil result
1561
+ assert_equal 1, ((t1 - t0) * 5).round.to_i
1562
+ assert_kind_of CustomTimeoutError, err
1563
+
1564
+ # Try a second time, to ensure no undefined state remains behind
1565
+ t0 = Time.now
1566
+ result = nil
1567
+ err = nil
1568
+ begin
1569
+ result = db2.execute('begin exclusive')
1570
+ rescue => e
1571
+ err = e
1572
+ end
1573
+ t1 = Time.now
1574
+
1575
+ assert_nil result
1576
+ assert_equal 1, ((t1 - t0) * 5).round.to_i
1577
+ assert_kind_of CustomTimeoutError, err
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
1151
1602
  end
1152
1603
 
1153
1604
  class RactorTest < Minitest::Test
@@ -1169,67 +1620,232 @@ class RactorTest < Minitest::Test
1169
1620
  r << 42
1170
1621
  r.take # wait for ractor to terminate
1171
1622
 
1172
- assert_equal 42, db.query_single_value('select x from foo')
1623
+ assert_equal 42, db.query_single_argv('select x from foo')
1173
1624
  end
1174
1625
 
1175
1626
  # Adapted from here: https://github.com/sparklemotion/sqlite3-ruby/pull/365/files
1176
1627
  def test_ractor_share_database
1177
- skip if SKIP_RACTOR_TESTS
1628
+ skip "skipped for now as ractors seem kinda flakey (failing sporadically)"
1178
1629
 
1179
1630
  db_receiver = Ractor.new do
1180
1631
  db = Ractor.receive
1181
1632
  Ractor.yield db.object_id
1182
1633
  begin
1183
- db.execute("create table foo (b)")
1184
- 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()'
1185
1636
  rescue => e
1186
1637
  Ractor.yield e
1187
1638
  end
1188
1639
  end
1189
1640
  sleep 0.1
1190
1641
  db_creator = Ractor.new(db_receiver) do |db_receiver|
1191
- db = Extralite::Database.new(":memory:")
1642
+ db = Extralite::Database.new(':memory:')
1192
1643
  Ractor.yield db.object_id
1193
1644
  db_receiver.send(db)
1194
1645
  sleep 0.1
1195
- db.execute("create table foo (a)")
1646
+ db.execute('create table foo (a)')
1196
1647
  end
1197
1648
  first_oid = db_creator.take
1198
1649
  second_oid = db_receiver.take
1199
1650
  refute_equal first_oid, second_oid
1200
1651
  ex = db_receiver.take
1201
1652
  assert_kind_of Extralite::Error, ex
1202
- assert_equal "Database is closed", ex.message
1653
+ assert_equal 'Database is closed', ex.message
1203
1654
  end
1204
1655
 
1205
- STRESS_DB_NAME = Tempfile.new('extralite_test_ractor_stress').path
1206
-
1207
1656
  # Adapted from here: https://github.com/sparklemotion/sqlite3-ruby/pull/365/files
1208
1657
  def test_ractor_stress
1209
1658
  skip if SKIP_RACTOR_TESTS
1210
-
1211
- Ractor.make_shareable(STRESS_DB_NAME)
1212
1659
 
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)")
1660
+ fn = Tempfile.new('extralite_test_ractor_stress').path
1216
1661
  random = Random.new.freeze
1662
+
1217
1663
  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
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
1221
1671
  10.times do |i|
1222
- 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)
1223
1673
  end
1674
+ Ractor.yield changes
1224
1675
  end
1225
1676
  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")
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')
1230
1685
  Ractor.yield count
1231
1686
  end
1232
1687
  count = final_check.take
1233
1688
  assert_equal 100, count
1234
1689
  end
1235
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