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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +34 -13
- data/Gemfile +4 -0
- data/Gemfile-bundle +1 -1
- data/LICENSE +1 -1
- data/README.md +1059 -247
- data/Rakefile +18 -0
- data/TODO.md +0 -7
- data/examples/kv_store.rb +49 -0
- data/examples/multi_fiber.rb +16 -0
- data/examples/on_progress.rb +9 -0
- data/examples/pubsub_store_polyphony.rb +194 -0
- data/examples/pubsub_store_threads.rb +204 -0
- data/ext/extralite/changeset.c +463 -0
- data/ext/extralite/common.c +177 -91
- data/ext/extralite/database.c +745 -276
- data/ext/extralite/extconf-bundle.rb +10 -4
- data/ext/extralite/extconf.rb +34 -34
- data/ext/extralite/extralite.h +104 -47
- data/ext/extralite/extralite_ext.c +6 -0
- data/ext/extralite/iterator.c +14 -86
- data/ext/extralite/query.c +171 -264
- data/extralite-bundle.gemspec +1 -1
- data/extralite.gemspec +1 -1
- data/gemspec.rb +10 -11
- data/lib/extralite/version.rb +1 -1
- data/lib/extralite.rb +69 -10
- data/lib/sequel/adapters/extralite.rb +1 -1
- data/test/helper.rb +9 -1
- data/test/perf_argv_transform.rb +74 -0
- data/test/perf_ary.rb +14 -12
- data/test/perf_hash.rb +17 -15
- data/test/perf_hash_prepared.rb +58 -0
- data/test/perf_hash_transform.rb +66 -0
- data/test/perf_polyphony.rb +74 -0
- data/test/test_changeset.rb +161 -0
- data/test/test_database.rb +720 -104
- data/test/test_extralite.rb +2 -2
- data/test/test_iterator.rb +28 -13
- data/test/test_query.rb +352 -110
- data/test/test_sequel.rb +4 -4
- metadata +24 -16
- data/Gemfile.lock +0 -37
- data/test/perf_prepared.rb +0 -64
data/test/test_database.rb
CHANGED
@@ -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 <
|
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
|
51
|
-
r = @db.
|
52
|
-
assert_equal
|
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.
|
55
|
-
|
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
|
59
|
-
r = @db.
|
60
|
-
assert_equal
|
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.
|
63
|
-
|
74
|
+
r = @db.query_single('select * from t where x = 2')
|
75
|
+
assert_nil r
|
64
76
|
end
|
65
77
|
|
66
|
-
def
|
67
|
-
r = @db.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
165
|
-
assert_equal 42, @db.
|
166
|
-
assert_equal 42, @db.
|
167
|
-
assert_nil @db.
|
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.
|
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.
|
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(:
|
189
|
+
foo_bar = Struct.new(:':foo', :bar)
|
178
190
|
value = foo_bar.new(41, 42)
|
179
|
-
assert_equal 41, @db.
|
180
|
-
assert_equal 42, @db.
|
181
|
-
assert_nil @db.
|
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(:
|
188
|
-
value = foo_bar.new(
|
189
|
-
assert_equal 42, @db.
|
190
|
-
assert_nil @db.
|
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.
|
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.
|
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.
|
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.
|
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.
|
238
|
+
assert_nil @db.query_single_argv('select ?', nil)
|
227
239
|
|
228
240
|
# 32-bit integers
|
229
|
-
assert_equal
|
230
|
-
assert_equal
|
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
|
234
|
-
assert_equal
|
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.
|
238
|
-
assert_equal Float::MAX, @db.
|
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.
|
242
|
-
assert_equal 0, @db.
|
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.
|
246
|
-
assert_equal 'foo', @db.
|
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.
|
262
|
+
r = @db.query_single_argv("select 'abc'")
|
251
263
|
assert_equal 'abc', r
|
252
264
|
|
253
|
-
r = @db.
|
265
|
+
r = @db.query_single_argv('select 123')
|
254
266
|
assert_equal 123, r
|
255
267
|
|
256
|
-
r = @db.
|
268
|
+
r = @db.query_single_argv('select 12.34')
|
257
269
|
assert_equal 12.34, r
|
258
270
|
|
259
|
-
r = @db.
|
271
|
+
r = @db.query_single_argv('select zeroblob(4)')
|
260
272
|
assert_equal "\x00\x00\x00\x00", r
|
261
273
|
|
262
|
-
r = @db.
|
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.
|
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
|
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.
|
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.
|
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.
|
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
|
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.
|
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.
|
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.
|
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
|
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.
|
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.
|
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.
|
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('
|
821
|
-
db = Extralite::Database.new(fn,
|
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
|
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
|
853
|
+
assert_match(/^\#\<Extralite::Database:0x[0-9a-f]+ :memory:\>$/, db.inspect)
|
837
854
|
db.close
|
838
|
-
assert_match
|
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.
|
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
|
-
|
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
|
-
|
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 <
|
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.
|
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.
|
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.
|
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 <
|
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
|
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.
|
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
|
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(
|
1184
|
-
raise
|
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(
|
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(
|
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
|
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
|
-
|
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
|
-
|
1219
|
-
|
1220
|
-
|
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
|
-
|
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
|
-
|
1227
|
-
|
1228
|
-
|
1229
|
-
|
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
|