dbmlite3 2.0.0.pre.alpha.6 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/Rakefile +2 -2
- data/dbmlite3.gemspec +8 -8
- data/doc/Lite3/DBM.html +14 -14
- data/doc/Lite3/Error.html +4 -4
- data/doc/Lite3/InternalError.html +4 -4
- data/doc/Lite3/SQL.html +10 -10
- data/doc/Lite3.html +4 -4
- data/doc/_index.html +5 -5
- data/doc/file.README.html +9 -9
- data/doc/frames.html +1 -1
- data/doc/index.html +9 -9
- data/doc/top-level-namespace.html +4 -4
- data/lib/internal_lite3/dbm.rb +3 -3
- data/lib/internal_lite3/error.rb +0 -4
- data/lib/internal_lite3/handle.rb +2 -2
- data/lib/internal_lite3/sql.rb +4 -1
- data/spec/dbmlite3_spec.rb +21 -10
- metadata +10 -11
- data/extras/benchmark.rb +0 -172
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d54afa78dc0b955258b1af242e139a7a782eadd8eed2158a7858977287f5c074
|
4
|
+
data.tar.gz: 31d89de7932fa529649b3826d4a70bb24ec2c32f13f00fde2d92fb25b11b5508
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5d889fd9d0ae8f09c7812e9e6d8444deabb6891679be8c33674eeceaaf8db00b3f9515d1d3c12e444126ab6c1b2079f77977ebe3d247c631d01a670b5e8826eb
|
7
|
+
data.tar.gz: a482cec8793ba096d9be6070b226d8b1a93c525e075a08af1590dd5f24ac2ae999b9067c028e59ec762291ef06fab95d4b89266aae49d92d385af576d6b158ed
|
data/LICENSE.txt
CHANGED
data/Rakefile
CHANGED
@@ -11,8 +11,8 @@ YARD::Rake::YardocTask.new(:docs_via_yard) do |t|
|
|
11
11
|
end
|
12
12
|
|
13
13
|
task :gem => [:doc] do
|
14
|
-
|
15
|
-
sh "
|
14
|
+
gem_prefix = "JRUBY_GEM=yes " if RUBY_PLATFORM == "java"
|
15
|
+
sh "#{gem_prefix}gem build dbmlite3"
|
16
16
|
end
|
17
17
|
|
18
18
|
task :clean do
|
data/dbmlite3.gemspec
CHANGED
@@ -18,7 +18,7 @@ end
|
|
18
18
|
|
19
19
|
Gem::Specification.new do |s|
|
20
20
|
s.name = "dbmlite3#{SUFFIX}"
|
21
|
-
s.version = '2.0.0
|
21
|
+
s.version = '2.0.0'
|
22
22
|
s.date = '2022-02-21'
|
23
23
|
s.summary = "A DBM-style key-value store using SQLite3#{SUMMARY_EXTRA}"
|
24
24
|
s.description = <<-EOF + DESC_EXTRA
|
@@ -32,7 +32,7 @@ Gem::Specification.new do |s|
|
|
32
32
|
and safety.
|
33
33
|
EOF
|
34
34
|
s.authors = ["Chris Reuter"]
|
35
|
-
s.email = 'chris@
|
35
|
+
s.email = 'chris@remove-this-part.blit.ca'
|
36
36
|
|
37
37
|
# I'm just going to add everything so that if you've got the gem,
|
38
38
|
# you've also got the source distribution. Yay! Open source!
|
@@ -40,23 +40,23 @@ EOF
|
|
40
40
|
Dir.glob('doc/**/*') +
|
41
41
|
`git ls-files`.split.reject {|f| f =~ /\.org$/}
|
42
42
|
|
43
|
-
|
43
|
+
|
44
44
|
s.required_ruby_version = '>= 2.7.0'
|
45
45
|
s.requirements << (JRUBY_GEM ?
|
46
46
|
"Sequel, jdbc-sqlite3, JRuby" :
|
47
47
|
"Sequel, sqlite3, Ruby MRI")
|
48
|
-
|
49
|
-
s.add_runtime_dependency "sequel", '~> 5.
|
48
|
+
|
49
|
+
s.add_runtime_dependency "sequel", '~> 5.71.0'
|
50
50
|
|
51
51
|
if JRUBY_GEM
|
52
|
-
s.add_runtime_dependency "jdbc-sqlite3", "~> 3.
|
52
|
+
s.add_runtime_dependency "jdbc-sqlite3", "~> 3.42.0.0"
|
53
53
|
else
|
54
54
|
s.add_runtime_dependency "sqlite3", "~> 1.6.1"
|
55
55
|
end
|
56
|
-
|
56
|
+
|
57
57
|
s.add_development_dependency "rspec", '~> 3.10', '>= 3.10.0'
|
58
58
|
s.add_development_dependency "yard", '~> 0.9.25', '>= 0.9.25'
|
59
|
-
|
59
|
+
|
60
60
|
s.homepage = 'https://codeberg.org/suetanvil/dbmlite3'
|
61
61
|
s.license = 'MIT'
|
62
62
|
end
|
data/doc/Lite3/DBM.html
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
<title>
|
7
7
|
Class: Lite3::DBM
|
8
8
|
|
9
|
-
— Documentation by YARD 0.9.
|
9
|
+
— Documentation by YARD 0.9.34
|
10
10
|
|
11
11
|
</title>
|
12
12
|
|
@@ -453,7 +453,7 @@
|
|
453
453
|
|
454
454
|
|
455
455
|
<span class="summary_desc"><div class='inline'>
|
456
|
-
<p>Behaves like
|
456
|
+
<p>Behaves like ‘each’ with a block–that is, call it for each key/value pair–but (probably) executes faster.</p>
|
457
457
|
</div></span>
|
458
458
|
|
459
459
|
</li>
|
@@ -579,7 +579,7 @@
|
|
579
579
|
|
580
580
|
|
581
581
|
<span class="summary_desc"><div class='inline'>
|
582
|
-
<p>Return a <code>Hash</code> whose keys are the table
|
582
|
+
<p>Return a <code>Hash</code> whose keys are the table’s values and whose values are the table’s keys.</p>
|
583
583
|
</div></span>
|
584
584
|
|
585
585
|
</li>
|
@@ -1638,11 +1638,11 @@
|
|
1638
1638
|
</h3><div class="docstring">
|
1639
1639
|
<div class="discussion">
|
1640
1640
|
|
1641
|
-
<p>Behaves like
|
1641
|
+
<p>Behaves like ‘each’ with a block–that is, call it for each key/value pair–but (probably) executes faster.</p>
|
1642
1642
|
|
1643
1643
|
<p>The downside is that there is no guarantee of reentrance or safety. The block <em>MUST NOT</em> access the database in any way. In addition, no guarantee is made about element order.</p>
|
1644
1644
|
|
1645
|
-
<p>(You might be able to infer some ways to safely bend the rules by seeing what the underlying database libraries allow, but your code won
|
1645
|
+
<p>(You might be able to infer some ways to safely bend the rules by seeing what the underlying database libraries allow, but your code won’t be future-proof if you do that.)</p>
|
1646
1646
|
|
1647
1647
|
|
1648
1648
|
</div>
|
@@ -1961,7 +1961,7 @@
|
|
1961
1961
|
</h3><div class="docstring">
|
1962
1962
|
<div class="discussion">
|
1963
1963
|
|
1964
|
-
<p>Return a <code>Hash</code> whose keys are the table
|
1964
|
+
<p>Return a <code>Hash</code> whose keys are the table’s values and whose values are the table’s keys.</p>
|
1965
1965
|
|
1966
1966
|
<p><strong>WARNING:</strong> it is possible for tables to be significantly larger than available RAM; in that case, this will likely crash your program.</p>
|
1967
1967
|
|
@@ -2055,7 +2055,7 @@
|
|
2055
2055
|
</h3><div class="docstring">
|
2056
2056
|
<div class="discussion">
|
2057
2057
|
|
2058
|
-
<p>Remove the first key/value pair from <code>self</code> and return it. “First” is defined by <code>self</code
|
2058
|
+
<p>Remove the first key/value pair from <code>self</code> and return it. “First” is defined by <code>self</code>‘s row order, which is the order of insertion as determined by SQLite3.</p>
|
2059
2059
|
|
2060
2060
|
|
2061
2061
|
</div>
|
@@ -2287,11 +2287,11 @@
|
|
2287
2287
|
</h3><div class="docstring">
|
2288
2288
|
<div class="discussion">
|
2289
2289
|
|
2290
|
-
<p>Begins a transaction, evaluates the given block and then ends the transaction. If no error occurred (i.e. an exception was thrown), the transaction is committed; otherwise, it is rolled back. Returns the block
|
2290
|
+
<p>Begins a transaction, evaluates the given block and then ends the transaction. If no error occurred (i.e. an exception was thrown), the transaction is committed; otherwise, it is rolled back. Returns the block’s result.</p>
|
2291
2291
|
|
2292
|
-
<p>It is safe to call <code>DBM.transaction</code> within another <code>DBM.transaction</code> block
|
2292
|
+
<p>It is safe to call <code>DBM.transaction</code> within another <code>DBM.transaction</code> block’s call chain because <code>DBM</code> will not start a new transaction on a database handle that already has one in progress. (It may be possible to trick <code>DBM</code> into trying via fibers or other flow control trickery; don’t do that.)</p>
|
2293
2293
|
|
2294
|
-
<p>Note that it
|
2294
|
+
<p>Note that it’s probably not a good idea to assume too much about the precise semantics; I can’t guarantee that the underlying library(es) won’t change or be replaced outright.</p>
|
2295
2295
|
|
2296
2296
|
<p>That being said, at present, this is simply a wrapper around <code>Sequel::Database.transaction</code> with the default options and so is subject to the quirks therein. In version 1.0.0, transactions were always executed in <code>:deferred</code> mode via the <code>sqlite3</code> gem.</p>
|
2297
2297
|
|
@@ -2330,7 +2330,7 @@
|
|
2330
2330
|
|
2331
2331
|
—
|
2332
2332
|
<div class='inline'>
|
2333
|
-
<p>Returns the block
|
2333
|
+
<p>Returns the block’s result.</p>
|
2334
2334
|
</div>
|
2335
2335
|
|
2336
2336
|
</li>
|
@@ -2550,9 +2550,9 @@
|
|
2550
2550
|
</div>
|
2551
2551
|
|
2552
2552
|
<div id="footer">
|
2553
|
-
Generated on
|
2554
|
-
<a href="
|
2555
|
-
0.9.
|
2553
|
+
Generated on Sat Aug 19 13:22:02 2023 by
|
2554
|
+
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
2555
|
+
0.9.34 (ruby-3.2.1).
|
2556
2556
|
</div>
|
2557
2557
|
|
2558
2558
|
</div>
|
data/doc/Lite3/Error.html
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
<title>
|
7
7
|
Exception: Lite3::Error
|
8
8
|
|
9
|
-
— Documentation by YARD 0.9.
|
9
|
+
— Documentation by YARD 0.9.34
|
10
10
|
|
11
11
|
</title>
|
12
12
|
|
@@ -129,9 +129,9 @@
|
|
129
129
|
</div>
|
130
130
|
|
131
131
|
<div id="footer">
|
132
|
-
Generated on
|
133
|
-
<a href="
|
134
|
-
0.9.
|
132
|
+
Generated on Sat Aug 19 13:22:02 2023 by
|
133
|
+
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
134
|
+
0.9.34 (ruby-3.2.1).
|
135
135
|
</div>
|
136
136
|
|
137
137
|
</div>
|
@@ -6,7 +6,7 @@
|
|
6
6
|
<title>
|
7
7
|
Exception: Lite3::InternalError
|
8
8
|
|
9
|
-
— Documentation by YARD 0.9.
|
9
|
+
— Documentation by YARD 0.9.34
|
10
10
|
|
11
11
|
</title>
|
12
12
|
|
@@ -133,9 +133,9 @@
|
|
133
133
|
</div>
|
134
134
|
|
135
135
|
<div id="footer">
|
136
|
-
Generated on
|
137
|
-
<a href="
|
138
|
-
0.9.
|
136
|
+
Generated on Sat Aug 19 13:22:02 2023 by
|
137
|
+
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
138
|
+
0.9.34 (ruby-3.2.1).
|
139
139
|
</div>
|
140
140
|
|
141
141
|
</div>
|
data/doc/Lite3/SQL.html
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
<title>
|
7
7
|
Module: Lite3::SQL
|
8
8
|
|
9
|
-
— Documentation by YARD 0.9.
|
9
|
+
— Documentation by YARD 0.9.34
|
10
10
|
|
11
11
|
</title>
|
12
12
|
|
@@ -220,10 +220,10 @@
|
|
220
220
|
<pre class="lines">
|
221
221
|
|
222
222
|
|
223
|
-
|
223
|
+
87</pre>
|
224
224
|
</td>
|
225
225
|
<td>
|
226
|
-
<pre class="code"><span class="info file"># File 'lib/internal_lite3/sql.rb', line
|
226
|
+
<pre class="code"><span class="info file"># File 'lib/internal_lite3/sql.rb', line 87</span>
|
227
227
|
|
228
228
|
<span class='kw'>def</span> <span class='kw'>self</span><span class='period'>.</span><span class='id identifier rubyid_close_all'>close_all</span><span class='lparen'>(</span><span class='rparen'>)</span> <span class='kw'>return</span> <span class='const'>HandlePool</span><span class='period'>.</span><span class='id identifier rubyid_close_all'>close_all</span> <span class='kw'>end</span></pre>
|
229
229
|
</td>
|
@@ -245,9 +245,9 @@
|
|
245
245
|
|
246
246
|
<p>Disconnect and delete all database handles and associated metadata that are no longer needed (i.e. because their corresponding <code>DBM</code>s have been closed or reclaimed).</p>
|
247
247
|
|
248
|
-
<p>Returns a hash mapping the path to each open database file to the number of live DBM objects referencing it
|
248
|
+
<p>Returns a hash mapping the path to each open database file to the number of live DBM objects referencing it. (Note that DBM objects that have gone out of scope but are not yet finalized count as “live”; as a result, this will differ across Ruby implementations due to differing garbage collector semantics.)</p>
|
249
249
|
|
250
|
-
<p>You normally won
|
250
|
+
<p>You normally won’t need to explicitly call this, but it’s useful for testing and debugging.</p>
|
251
251
|
|
252
252
|
|
253
253
|
</div>
|
@@ -261,10 +261,10 @@
|
|
261
261
|
<pre class="lines">
|
262
262
|
|
263
263
|
|
264
|
-
|
264
|
+
74</pre>
|
265
265
|
</td>
|
266
266
|
<td>
|
267
|
-
<pre class="code"><span class="info file"># File 'lib/internal_lite3/sql.rb', line
|
267
|
+
<pre class="code"><span class="info file"># File 'lib/internal_lite3/sql.rb', line 74</span>
|
268
268
|
|
269
269
|
<span class='kw'>def</span> <span class='kw'>self</span><span class='period'>.</span><span class='id identifier rubyid_gc'>gc</span><span class='lparen'>(</span><span class='rparen'>)</span> <span class='kw'>return</span> <span class='const'>HandlePool</span><span class='period'>.</span><span class='id identifier rubyid_gc'>gc</span><span class='semicolon'>;</span> <span class='kw'>end</span></pre>
|
270
270
|
</td>
|
@@ -333,9 +333,9 @@
|
|
333
333
|
</div>
|
334
334
|
|
335
335
|
<div id="footer">
|
336
|
-
Generated on
|
337
|
-
<a href="
|
338
|
-
0.9.
|
336
|
+
Generated on Sat Aug 19 13:22:01 2023 by
|
337
|
+
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
338
|
+
0.9.34 (ruby-3.2.1).
|
339
339
|
</div>
|
340
340
|
|
341
341
|
</div>
|
data/doc/Lite3.html
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
<title>
|
7
7
|
Module: Lite3
|
8
8
|
|
9
|
-
— Documentation by YARD 0.9.
|
9
|
+
— Documentation by YARD 0.9.34
|
10
10
|
|
11
11
|
</title>
|
12
12
|
|
@@ -109,9 +109,9 @@
|
|
109
109
|
</div>
|
110
110
|
|
111
111
|
<div id="footer">
|
112
|
-
Generated on
|
113
|
-
<a href="
|
114
|
-
0.9.
|
112
|
+
Generated on Sat Aug 19 13:22:01 2023 by
|
113
|
+
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
114
|
+
0.9.34 (ruby-3.2.1).
|
115
115
|
</div>
|
116
116
|
|
117
117
|
</div>
|
data/doc/_index.html
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
<meta charset="utf-8">
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
6
|
<title>
|
7
|
-
Documentation by YARD 0.9.
|
7
|
+
Documentation by YARD 0.9.34
|
8
8
|
|
9
9
|
</title>
|
10
10
|
|
@@ -52,7 +52,7 @@
|
|
52
52
|
<div class="clear"></div>
|
53
53
|
</div>
|
54
54
|
|
55
|
-
<div id="content"><h1 class="noborder title">Documentation by YARD 0.9.
|
55
|
+
<div id="content"><h1 class="noborder title">Documentation by YARD 0.9.34</h1>
|
56
56
|
<div id="listing">
|
57
57
|
<h1 class="alphaindex">Alphabetic Index</h1>
|
58
58
|
|
@@ -157,9 +157,9 @@
|
|
157
157
|
</div>
|
158
158
|
|
159
159
|
<div id="footer">
|
160
|
-
Generated on
|
161
|
-
<a href="
|
162
|
-
0.9.
|
160
|
+
Generated on Sat Aug 19 13:22:01 2023 by
|
161
|
+
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
162
|
+
0.9.34 (ruby-3.2.1).
|
163
163
|
</div>
|
164
164
|
|
165
165
|
</div>
|
data/doc/file.README.html
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
<title>
|
7
7
|
File: README
|
8
8
|
|
9
|
-
— Documentation by YARD 0.9.
|
9
|
+
— Documentation by YARD 0.9.34
|
10
10
|
|
11
11
|
</title>
|
12
12
|
|
@@ -139,7 +139,7 @@ $ rake
|
|
139
139
|
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_db'>db</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>foo</span><span class='tstring_end'>'</span></span><span class='rbracket'>]</span> <span class='op'>+=</span> <span class='int'>1</span>
|
140
140
|
</code></pre>
|
141
141
|
|
142
|
-
<p>contains a race condition. If (e.g.) two copies of this script are running at the same time, it is possible for both to perform the read before one of them writes, losing the others
|
142
|
+
<p>contains a race condition. If (e.g.) two copies of this script are running at the same time, it is possible for both to perform the read before one of them writes, losing the others’ result.</p>
|
143
143
|
|
144
144
|
<p>There are two ways to deal with this. You can wrap the read-modify-write cycle in a transaction:</p>
|
145
145
|
|
@@ -190,26 +190,26 @@ $ rake
|
|
190
190
|
|
191
191
|
<p>While it is generally safe to treat <code>Lite3::DBM</code> as a wrapper around a file handle (i.e. <code>open</code> and <code>close</code> work as expected), you should be aware that this is not precisely the way things actually work. Instead, the gem maintains a pool of database handles, one per file, and associates them with <code>Lite3::DBM</code> instances as needed. This is necessary for transactions to work correctly.</p>
|
192
192
|
|
193
|
-
<p>Mostly, you don
|
193
|
+
<p>Mostly, you don’t need to care about this. However, it affects you in the following ways:</p>
|
194
194
|
<ol><li>
|
195
195
|
<p>Transactions are done at the file level and not the table level. This means that you can access separate tables in the same transaction, which is a Very Good Thing.</p>
|
196
196
|
</li><li>
|
197
|
-
<p>You can safely fork the current process and keep using existing <code>DBM</code> objects in both processes, provided you
|
197
|
+
<p>You can safely fork the current process and keep using existing <code>DBM</code> objects in both processes, provided you've invoked <code>Lite3::SQL.close_all</code> before the fork. This will have closed the actual database handles (which can't tolerate being carried across a fork) and opens new ones the next time they're needed.</p>
|
198
198
|
</li></ol>
|
199
199
|
|
200
|
-
<p><code>DBM</code> objects that go out of scope without first being closed <strong>will</strong> eventually have their underlying resources cleaned up. However, given that <em>when</em> when that happens depends on the vagaries of the garbage collector and various library internals, it
|
200
|
+
<p><code>DBM</code> objects that go out of scope without first being closed <strong>will</strong> eventually have their underlying resources cleaned up. However, given that <em>when</em> when that happens depends on the vagaries of the garbage collector and various library internals, it’s almost always a bad idea to not explicitly call <code>close</code> first.</p>
|
201
201
|
|
202
202
|
<h3 id="label-Under+the+hood">Under the hood</h3>
|
203
203
|
|
204
204
|
<p>Currently, <code>Lite3::DBM</code> uses <a href="https://sequel.jeremyevans.net">Sequel</a> to access the <code>sqlite3</code> library. On JRuby, it goes through the <code>jdbc</code> interface. The previous version (1.0.0) used <a href="https://github.com/sparklemotion/sqlite3-ruby">sqlite3</a> and only worked on MRI. However, you should make no assumptions about the underlying database libraries this gem uses. It may change in a future release.</p>
|
205
205
|
|
206
|
-
<p>All tables created by <code>Lite3::DBM</code> will have names beginning with <code>dbmlite3_</code> and you should not modify them directly. It <strong>might</strong> be safe to put other tables in the same database file (e.g. via <code>Sequel</code>) provided that you don
|
206
|
+
<p>All tables created by <code>Lite3::DBM</code> will have names beginning with <code>dbmlite3_</code> and you should not modify them directly. It <strong>might</strong> be safe to put other tables in the same database file (e.g. via <code>Sequel</code>) provided that you don’t make global changes or mix transactions across interfaces. However, I make no guarantees.</p>
|
207
207
|
</div></div>
|
208
208
|
|
209
209
|
<div id="footer">
|
210
|
-
Generated on
|
211
|
-
<a href="
|
212
|
-
0.9.
|
210
|
+
Generated on Sat Aug 19 13:22:01 2023 by
|
211
|
+
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
212
|
+
0.9.34 (ruby-3.2.1).
|
213
213
|
</div>
|
214
214
|
|
215
215
|
</div>
|
data/doc/frames.html
CHANGED
data/doc/index.html
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
<title>
|
7
7
|
File: README
|
8
8
|
|
9
|
-
— Documentation by YARD 0.9.
|
9
|
+
— Documentation by YARD 0.9.34
|
10
10
|
|
11
11
|
</title>
|
12
12
|
|
@@ -139,7 +139,7 @@ $ rake
|
|
139
139
|
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_db'>db</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>foo</span><span class='tstring_end'>'</span></span><span class='rbracket'>]</span> <span class='op'>+=</span> <span class='int'>1</span>
|
140
140
|
</code></pre>
|
141
141
|
|
142
|
-
<p>contains a race condition. If (e.g.) two copies of this script are running at the same time, it is possible for both to perform the read before one of them writes, losing the others
|
142
|
+
<p>contains a race condition. If (e.g.) two copies of this script are running at the same time, it is possible for both to perform the read before one of them writes, losing the others’ result.</p>
|
143
143
|
|
144
144
|
<p>There are two ways to deal with this. You can wrap the read-modify-write cycle in a transaction:</p>
|
145
145
|
|
@@ -190,26 +190,26 @@ $ rake
|
|
190
190
|
|
191
191
|
<p>While it is generally safe to treat <code>Lite3::DBM</code> as a wrapper around a file handle (i.e. <code>open</code> and <code>close</code> work as expected), you should be aware that this is not precisely the way things actually work. Instead, the gem maintains a pool of database handles, one per file, and associates them with <code>Lite3::DBM</code> instances as needed. This is necessary for transactions to work correctly.</p>
|
192
192
|
|
193
|
-
<p>Mostly, you don
|
193
|
+
<p>Mostly, you don’t need to care about this. However, it affects you in the following ways:</p>
|
194
194
|
<ol><li>
|
195
195
|
<p>Transactions are done at the file level and not the table level. This means that you can access separate tables in the same transaction, which is a Very Good Thing.</p>
|
196
196
|
</li><li>
|
197
|
-
<p>You can safely fork the current process and keep using existing <code>DBM</code> objects in both processes, provided you
|
197
|
+
<p>You can safely fork the current process and keep using existing <code>DBM</code> objects in both processes, provided you've invoked <code>Lite3::SQL.close_all</code> before the fork. This will have closed the actual database handles (which can't tolerate being carried across a fork) and opens new ones the next time they're needed.</p>
|
198
198
|
</li></ol>
|
199
199
|
|
200
|
-
<p><code>DBM</code> objects that go out of scope without first being closed <strong>will</strong> eventually have their underlying resources cleaned up. However, given that <em>when</em> when that happens depends on the vagaries of the garbage collector and various library internals, it
|
200
|
+
<p><code>DBM</code> objects that go out of scope without first being closed <strong>will</strong> eventually have their underlying resources cleaned up. However, given that <em>when</em> when that happens depends on the vagaries of the garbage collector and various library internals, it’s almost always a bad idea to not explicitly call <code>close</code> first.</p>
|
201
201
|
|
202
202
|
<h3 id="label-Under+the+hood">Under the hood</h3>
|
203
203
|
|
204
204
|
<p>Currently, <code>Lite3::DBM</code> uses <a href="https://sequel.jeremyevans.net">Sequel</a> to access the <code>sqlite3</code> library. On JRuby, it goes through the <code>jdbc</code> interface. The previous version (1.0.0) used <a href="https://github.com/sparklemotion/sqlite3-ruby">sqlite3</a> and only worked on MRI. However, you should make no assumptions about the underlying database libraries this gem uses. It may change in a future release.</p>
|
205
205
|
|
206
|
-
<p>All tables created by <code>Lite3::DBM</code> will have names beginning with <code>dbmlite3_</code> and you should not modify them directly. It <strong>might</strong> be safe to put other tables in the same database file (e.g. via <code>Sequel</code>) provided that you don
|
206
|
+
<p>All tables created by <code>Lite3::DBM</code> will have names beginning with <code>dbmlite3_</code> and you should not modify them directly. It <strong>might</strong> be safe to put other tables in the same database file (e.g. via <code>Sequel</code>) provided that you don’t make global changes or mix transactions across interfaces. However, I make no guarantees.</p>
|
207
207
|
</div></div>
|
208
208
|
|
209
209
|
<div id="footer">
|
210
|
-
Generated on
|
211
|
-
<a href="
|
212
|
-
0.9.
|
210
|
+
Generated on Sat Aug 19 13:22:01 2023 by
|
211
|
+
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
212
|
+
0.9.34 (ruby-3.2.1).
|
213
213
|
</div>
|
214
214
|
|
215
215
|
</div>
|
@@ -6,7 +6,7 @@
|
|
6
6
|
<title>
|
7
7
|
Top Level Namespace
|
8
8
|
|
9
|
-
— Documentation by YARD 0.9.
|
9
|
+
— Documentation by YARD 0.9.34
|
10
10
|
|
11
11
|
</title>
|
12
12
|
|
@@ -100,9 +100,9 @@
|
|
100
100
|
</div>
|
101
101
|
|
102
102
|
<div id="footer">
|
103
|
-
Generated on
|
104
|
-
<a href="
|
105
|
-
0.9.
|
103
|
+
Generated on Sat Aug 19 13:22:01 2023 by
|
104
|
+
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
105
|
+
0.9.34 (ruby-3.2.1).
|
106
106
|
</div>
|
107
107
|
|
108
108
|
</div>
|
data/lib/internal_lite3/dbm.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
|
2
2
|
module Lite3
|
3
|
-
|
3
|
+
|
4
4
|
# Lite3::DBM encapsulates a single table in a single SQLite3
|
5
5
|
# database file and lets you access it as easily as a Hash.
|
6
6
|
# Multiple instances may be opened on different tables in the same
|
@@ -267,7 +267,7 @@ module Lite3
|
|
267
267
|
#
|
268
268
|
# `key` **must** be a String or a Symbol; Symbols are
|
269
269
|
# transparently converted to Strings.
|
270
|
-
#
|
270
|
+
#
|
271
271
|
# If it is not present and a block is given, evaluate the block
|
272
272
|
# with the key as its argument and return that.
|
273
273
|
#
|
@@ -370,7 +370,7 @@ module Lite3
|
|
370
370
|
}
|
371
371
|
end
|
372
372
|
|
373
|
-
|
373
|
+
|
374
374
|
# Calls the given block with each key-value pair in the usual
|
375
375
|
# order, then return self. The entire call takes place in its own
|
376
376
|
# transaction.
|
data/lib/internal_lite3/error.rb
CHANGED
@@ -10,18 +10,14 @@ module Lite3
|
|
10
10
|
|
11
11
|
private
|
12
12
|
|
13
|
-
|
14
13
|
module ErrUtil
|
15
|
-
|
16
14
|
# Error check: if block evaluates to false, raise a Lite3::DBM::Error
|
17
15
|
# with the given message.
|
18
16
|
def check(message, &block)
|
19
17
|
return if block && block.call
|
20
18
|
raise InternalError.new(message)
|
21
19
|
end
|
22
|
-
|
23
20
|
end
|
24
21
|
|
25
|
-
|
26
22
|
private_constant :ErrUtil
|
27
23
|
end
|
@@ -41,7 +41,7 @@ module Lite3
|
|
41
41
|
end
|
42
42
|
alias inspect to_s
|
43
43
|
|
44
|
-
|
44
|
+
|
45
45
|
#
|
46
46
|
# References to the DBM object(s) using this handle.
|
47
47
|
#
|
@@ -85,7 +85,7 @@ module Lite3
|
|
85
85
|
#
|
86
86
|
# Transactions
|
87
87
|
#
|
88
|
-
|
88
|
+
|
89
89
|
# Perform &block in a transaction. See DBM.transaction.
|
90
90
|
def transaction(&block)
|
91
91
|
result = nil
|
data/lib/internal_lite3/sql.rb
CHANGED
@@ -64,7 +64,10 @@ module Lite3
|
|
64
64
|
# corresponding `DBM`s have been closed or reclaimed).
|
65
65
|
#
|
66
66
|
# Returns a hash mapping the path to each open database file to
|
67
|
-
# the number of live DBM objects referencing it.
|
67
|
+
# the number of live DBM objects referencing it. (Note that DBM
|
68
|
+
# objects that have gone out of scope but are not yet finalized
|
69
|
+
# count as "live"; as a result, this will differ across Ruby
|
70
|
+
# implementations due to differing garbage collector semantics.)
|
68
71
|
#
|
69
72
|
# You normally won't need to explicitly call this, but it's
|
70
73
|
# useful for testing and debugging.
|
data/spec/dbmlite3_spec.rb
CHANGED
@@ -5,6 +5,10 @@ require_relative '../lib/dbmlite3.rb'
|
|
5
5
|
require 'fileutils'
|
6
6
|
require 'set'
|
7
7
|
|
8
|
+
def jruby?
|
9
|
+
return RUBY_PLATFORM == "java"
|
10
|
+
end
|
11
|
+
|
8
12
|
module Tmp
|
9
13
|
@root = File.join( File.dirname(__FILE__), "tmpdata")
|
10
14
|
@count = 0
|
@@ -884,7 +888,8 @@ describe Lite3::SQL do
|
|
884
888
|
}
|
885
889
|
|
886
890
|
it "lets you close the actual handle without impeding database use" do
|
887
|
-
|
891
|
+
jruby? or # JRuby GC semantics throw this off
|
892
|
+
expect( Lite3::SQL.gc.size ) .to eq 0
|
888
893
|
|
889
894
|
file = Tmp.file
|
890
895
|
db1 = newbasic.call(file, "first")
|
@@ -892,14 +897,17 @@ describe Lite3::SQL do
|
|
892
897
|
|
893
898
|
# The above should be using the same handle, which is currently
|
894
899
|
# open.
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
900
|
+
#
|
901
|
+
# (Depends on GC wierdness so we skip this part for JRuby.)
|
902
|
+
unless jruby?
|
903
|
+
stats = Lite3::SQL.gc
|
904
|
+
expect( stats.keys.size ) .to eq 1
|
905
|
+
|
906
|
+
# Referencing DBM objects should be db1 and db2
|
907
|
+
path, refs = stats.to_a[0]
|
908
|
+
expect( path ) .to eq file
|
909
|
+
expect( refs ) .to eq 2
|
910
|
+
end
|
903
911
|
|
904
912
|
# We can no longer test if the underlying file handles are still
|
905
913
|
# open, so we don't.
|
@@ -915,7 +923,8 @@ describe Lite3::SQL do
|
|
915
923
|
db1.close
|
916
924
|
db2.close
|
917
925
|
|
918
|
-
|
926
|
+
jruby? or
|
927
|
+
expect( Lite3::SQL.gc.keys.size ) .to eq 0
|
919
928
|
end
|
920
929
|
|
921
930
|
it "allows multiple table accesses in the same transaction" do
|
@@ -978,6 +987,7 @@ describe Lite3::SQL do
|
|
978
987
|
#
|
979
988
|
# (Dropping into the debugger after GC.start seems to help.)
|
980
989
|
|
990
|
+
skip "JRuby GC is capricious" if jruby?
|
981
991
|
|
982
992
|
file = Tmp.file
|
983
993
|
db1 = newbasic.call(file, "first")
|
@@ -1017,6 +1027,7 @@ end
|
|
1017
1027
|
|
1018
1028
|
describe self do
|
1019
1029
|
it "(this test) closes all handles when done with them" do
|
1030
|
+
skip "JRuby GC is capricious"if jruby?
|
1020
1031
|
expect( Lite3::SQL.gc.size ) .to eq 0
|
1021
1032
|
end
|
1022
1033
|
end
|
metadata
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dbmlite3
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.0
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Reuter
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
date: 2022-02-21 00:00:00.000000000 Z
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 5.
|
19
|
+
version: 5.71.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 5.
|
26
|
+
version: 5.71.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: sqlite3
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -87,7 +87,7 @@ description: |2
|
|
87
87
|
(almost) any Ruby type to be stored. In addition, there is
|
88
88
|
rudimentary support for transactions with emphasis on simplicity
|
89
89
|
and safety.
|
90
|
-
email: chris@
|
90
|
+
email: chris@remove-this-part.blit.ca
|
91
91
|
executables: []
|
92
92
|
extensions: []
|
93
93
|
extra_rdoc_files: []
|
@@ -116,7 +116,6 @@ files:
|
|
116
116
|
- doc/js/jquery.js
|
117
117
|
- doc/method_list.html
|
118
118
|
- doc/top-level-namespace.html
|
119
|
-
- extras/benchmark.rb
|
120
119
|
- lib/dbmlite3.rb
|
121
120
|
- lib/internal_lite3/dbm.rb
|
122
121
|
- lib/internal_lite3/error.rb
|
@@ -128,7 +127,7 @@ homepage: https://codeberg.org/suetanvil/dbmlite3
|
|
128
127
|
licenses:
|
129
128
|
- MIT
|
130
129
|
metadata: {}
|
131
|
-
post_install_message:
|
130
|
+
post_install_message:
|
132
131
|
rdoc_options: []
|
133
132
|
require_paths:
|
134
133
|
- lib
|
@@ -139,13 +138,13 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
139
138
|
version: 2.7.0
|
140
139
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
141
140
|
requirements:
|
142
|
-
- - "
|
141
|
+
- - ">="
|
143
142
|
- !ruby/object:Gem::Version
|
144
|
-
version:
|
143
|
+
version: '0'
|
145
144
|
requirements:
|
146
145
|
- Sequel, sqlite3, Ruby MRI
|
147
|
-
rubygems_version: 3.
|
148
|
-
signing_key:
|
146
|
+
rubygems_version: 3.4.6
|
147
|
+
signing_key:
|
149
148
|
specification_version: 4
|
150
149
|
summary: A DBM-style key-value store using SQLite3
|
151
150
|
test_files: []
|
data/extras/benchmark.rb
DELETED
@@ -1,172 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
# Low-effort benchmark comparing Lite3::DBM in various modes against
|
4
|
-
# other equivalent Hash-like Ruby storage mechanisms.
|
5
|
-
|
6
|
-
require 'dbmlite3'
|
7
|
-
require 'yaml/dbm' unless RUBY_PLATFORM == "java"
|
8
|
-
require 'fileutils'
|
9
|
-
require 'optparse'
|
10
|
-
|
11
|
-
COUNT = 10000
|
12
|
-
|
13
|
-
Opts = proc do
|
14
|
-
opt_defaults = {
|
15
|
-
count: COUNT,
|
16
|
-
transaction_only: true,
|
17
|
-
}
|
18
|
-
|
19
|
-
opts = Struct.new(*opt_defaults.keys).new(*opt_defaults.values)
|
20
|
-
|
21
|
-
OptionParser.new do |opo|
|
22
|
-
opo.banner = "Usage: bench_1.rb [options]"
|
23
|
-
|
24
|
-
opo.on("-t", "--multi-transaction",
|
25
|
-
"Don't batch Lite3 accesses in one big transaction.") {
|
26
|
-
opts.transaction_only = false
|
27
|
-
}
|
28
|
-
|
29
|
-
opo.on("-c", "--count N", Integer, "Set test count.") { |c|
|
30
|
-
opts.count = c
|
31
|
-
}
|
32
|
-
end.parse!
|
33
|
-
|
34
|
-
next opts
|
35
|
-
end.call
|
36
|
-
|
37
|
-
|
38
|
-
module Tmp
|
39
|
-
@root = File.join( File.dirname(__FILE__), "tmpdata")
|
40
|
-
@count = 0
|
41
|
-
|
42
|
-
def self.file
|
43
|
-
FileUtils.mkdir(@root) unless File.directory?(@root)
|
44
|
-
|
45
|
-
file = "testfile_#{@count}_#{$$}.sqlite3"
|
46
|
-
@count += 1
|
47
|
-
|
48
|
-
return File.join(@root, file)
|
49
|
-
end
|
50
|
-
|
51
|
-
def self.cleanup
|
52
|
-
return unless File.directory?(@root)
|
53
|
-
FileUtils.rm_rf(@root)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
def insert(count, db, offset)
|
60
|
-
for n in 0 .. count
|
61
|
-
db["k_#{n}"] = "#{n + offset}"
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
|
66
|
-
def lookup(count, db)
|
67
|
-
rnd = Random.new(69_420)
|
68
|
-
sz = db.size
|
69
|
-
|
70
|
-
for _ in 0 .. count * 3
|
71
|
-
idx = rnd.rand(sz)
|
72
|
-
v = db["k_#{idx}"]
|
73
|
-
raise "Invalid value: #{v}" unless v.to_s == "#{idx + 1}"
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
|
78
|
-
Times = {}
|
79
|
-
|
80
|
-
def time_it(type, task, db, use_tr, &block)
|
81
|
-
print "#{type} #{task} - "
|
82
|
-
STDOUT.flush
|
83
|
-
|
84
|
-
start = Time.now
|
85
|
-
|
86
|
-
if use_tr && db.respond_to?(:transaction)
|
87
|
-
db.transaction { block.call }
|
88
|
-
else
|
89
|
-
block.call
|
90
|
-
end
|
91
|
-
|
92
|
-
finish = Time.now
|
93
|
-
|
94
|
-
elapsed = (finish - start).to_f
|
95
|
-
Times[type] = Times.fetch(type, 0) + elapsed
|
96
|
-
|
97
|
-
puts "#{elapsed.round(4)}"
|
98
|
-
end
|
99
|
-
|
100
|
-
def bench(count, desc, db, use_tr)
|
101
|
-
time_it(desc, "insert", db, use_tr) {
|
102
|
-
insert(count, db, 0)
|
103
|
-
}
|
104
|
-
time_it(desc, "upsert", db, use_tr) {
|
105
|
-
insert(count, db, 1)
|
106
|
-
}
|
107
|
-
time_it(desc, "lookup", db, use_tr) {
|
108
|
-
lookup(count, db)
|
109
|
-
}
|
110
|
-
time_it(desc, "delete_if", db, use_tr) {
|
111
|
-
rnd = Random.new(69_420)
|
112
|
-
db.delete_if{|k,v| rnd.rand(2) == 0}
|
113
|
-
}
|
114
|
-
puts
|
115
|
-
end
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
def main
|
120
|
-
puts "Count = #{Opts.count}\n"
|
121
|
-
|
122
|
-
Tmp.cleanup
|
123
|
-
|
124
|
-
bench(Opts.count, "hash", {}, false)
|
125
|
-
|
126
|
-
if RUBY_PLATFORM != "java"
|
127
|
-
DBM.open(Tmp.file) {|dbm|
|
128
|
-
bench(Opts.count, "DBM", dbm, false)
|
129
|
-
}
|
130
|
-
|
131
|
-
YAML::DBM.open(Tmp.file) {|dbm|
|
132
|
-
bench(Opts.count, "YAML::DBM", dbm, false)
|
133
|
-
}
|
134
|
-
end
|
135
|
-
|
136
|
-
Lite3::DBM.open(Tmp.file, "benchmark", :yaml) { |dbm|
|
137
|
-
bench(Opts.count, "Lite3::DBM(yaml)", dbm, true)
|
138
|
-
}
|
139
|
-
|
140
|
-
Lite3::DBM.open(Tmp.file, "benchmark", :marshal) { |dbm|
|
141
|
-
bench(Opts.count, "Lite3::DBM(marshal)", dbm, true)
|
142
|
-
}
|
143
|
-
|
144
|
-
Lite3::DBM.open(Tmp.file, "benchmark", :string) { |dbm|
|
145
|
-
bench(Opts.count, "Lite3::DBM(string)", dbm, true)
|
146
|
-
}
|
147
|
-
|
148
|
-
if !Opts.transaction_only
|
149
|
-
Lite3::DBM.open(Tmp.file, "benchmark", :yaml) { |dbm|
|
150
|
-
bench(Opts.count, "Lite3::DBM(yaml, single-trans)", dbm, false)
|
151
|
-
}
|
152
|
-
|
153
|
-
Lite3::DBM.open(Tmp.file, "benchmark", :marshal) { |dbm|
|
154
|
-
bench(Opts.count, "Lite3::DBM(marshal, single-trans)", dbm, false)
|
155
|
-
}
|
156
|
-
|
157
|
-
Lite3::DBM.open(Tmp.file, "benchmark", :string) { |dbm|
|
158
|
-
bench(Opts.count, "Lite3::DBM(string, single-trans)", dbm, false)
|
159
|
-
}
|
160
|
-
end
|
161
|
-
|
162
|
-
|
163
|
-
puts
|
164
|
-
puts "Totals:"
|
165
|
-
Times.each{|k,v|
|
166
|
-
puts " #{k} - #{v.round(4)}"
|
167
|
-
}
|
168
|
-
|
169
|
-
Tmp.cleanup
|
170
|
-
end
|
171
|
-
|
172
|
-
main
|