dbmlite3 1.0.0 → 2.0.0.pre.alpha.3

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.
data/lib/dbmlite3.rb CHANGED
@@ -1,958 +1,18 @@
1
1
 
2
- gem "sqlite3", "~> 1.4"
3
-
4
- require 'sqlite3'
2
+ require 'sequel'
5
3
  require 'psych'
6
4
  require 'set'
7
-
5
+ require 'yaml'
8
6
 
9
7
  module Lite3
8
+ IS_JRUBY = (RUBY_PLATFORM == "java")
10
9
 
11
- # Exception class for errors specific `Lite3::DBM`. Note that
12
- # `Lite3::*` methods may also throw `SQLite3` exceptions.
13
- class Error < StandardError; end
14
-
15
- #
16
- # Private classes
17
- #
18
-
19
-
20
- # Wrapper around a SQLite3::Database object.
21
- #
22
- # We do this instead of using them directly because transactions
23
- # happen at the handle level rather than the file level and this
24
- # lets us share the transaction across multiple tables in the same
25
- # file.
26
- #
27
- # In addition, we can use this to cache prepared SQL statements and
28
- # to transparently close and reopen the underlying database file
29
- # when (e.g.) forking the process.
30
- #
31
- # Instances contain references to DBM objects using them. When the
32
- # set becomes empty, the handle is closed; adding a reference will
33
- # ensure the handle is open.
34
- class Handle
35
- attr_reader :path
36
- def initialize(path)
37
- @path = path
38
- @db = nil
39
- @refs = Set.new
40
- @statement_cache = {}
41
-
42
- open!
43
- end
44
-
45
- def to_s
46
- "<#{self.class}:0x#{object_id.to_s(16)} path=#{@path} open=#{!!@db}>"
47
- end
48
- alias inspect to_s
49
-
50
- #
51
- # Back-references to the DBM object(s) using this handle.
52
- #
53
- # `delref` will close the handle if there are no more references.
54
- #
55
-
56
- def addref(parent)
57
- @refs.add parent
58
- open!
59
- end
60
-
61
- def delref(parent)
62
- @refs.delete parent
63
- close! if @refs.empty?
64
- end
65
-
66
- # Return the list of active parents;
67
- def refs
68
- return @refs.to_a
69
- end
70
-
71
-
72
- #
73
- # Opening and closing
74
- #
75
-
76
- def closed?
77
- return @db == nil
78
- end
79
-
80
- # Close the underlying SQLite3::Database handle. Does *not*
81
- # render `self` unusable; it will recreate the handle the next
82
- # time it's needed.
83
- def close!
84
- return unless @db
85
-
86
- @statement_cache.values.each{|ps| ps.close }
87
- @statement_cache = {}
88
-
89
- @db.close
90
- @db = nil
91
- end
92
-
93
- private
94
-
95
- # Open
96
- def open!
97
- return if @db # already open
98
- @db = SQLite3::Database.new @path
99
- end
100
-
101
- public
102
-
103
- # Perform &block in a transaction. See DBM.transaction.
104
- #
105
- # Always evalautes the block. If no transaction is in progress,
106
- # starts one first and ends it afterward. In other words: always
107
- # finishes what it starts and doesn't mess with things already in
108
- # progress.
109
- def transaction(&block)
110
- open!
111
-
112
- return block.call if @db.transaction_active?
113
-
114
- begin
115
- @db.transaction
116
- result = block.call
117
- return result
118
-
119
- rescue => e
120
- @db.rollback
121
- raise e
122
-
123
- ensure
124
- @db.commit if @db.transaction_active?
125
- end
126
- end
127
-
128
- # Test if there is currently a transaction in progress
129
- def transaction_active?
130
- return !closed? && @db.transaction_active?
131
- end
132
-
133
- # Execute query with bindvars. The corresponding Statement is cached
134
- # and reused if present.
135
- def sql(query, bindvars = [], &block)
136
- open!
137
- unless @statement_cache.has_key? query
138
- stmt = @db.prepare(query)
139
- @statement_cache[query] = stmt
140
- end
141
-
142
- return @statement_cache[query].execute!(bindvars, &block)
143
- end
144
- end
145
-
146
-
147
- # Dummy `Handle` that throws an `Error` exception whenever something
148
- # tries to treat it as an open handle. This replaces a `DBM`'s
149
- # `Handle` object when `DBM.close` is called so that the error
150
- # message will be useful if something tries to access a closed
151
- # handle.
152
- class ClosedHandle
153
- def initialize(filename, table)
154
- @filename, @table = [filename, table]
155
- end
156
-
157
- def closed?() return true; end
158
-
159
- # We clone the rest of Handle's interface with methods that throw
160
- # an Error.
161
- Handle.instance_methods(false).each { |name|
162
- next if method_defined? name
163
- define_method(name) { |*args|
164
- raise Error.new("Use of closed database at #{@filename}/#{@table}")
165
- }
166
- }
167
- end
168
-
169
-
170
- # Module to manage the collection of active Handle objects. See the
171
- # docs for `Lite3::SQL` for an overview; this module hold the actual
172
- # code and data.
173
- module HandlePool
174
- @@handles = {} # The hash of `Handle` objects keyed by filename
175
-
176
- # Retrieve the `Handle` associated with `filename`, creating it
177
- # first if necessary. `filename` is normalized with
178
- # `File.realpath` before using as a key and so is as good or bad
179
- # as that for detecting an existing file.
180
- def self.get(filename)
181
-
182
- # Scrub @@handles of all inactive Handles
183
- self.gc
184
-
185
- # We need to convert the filename to a canonical
186
- # form. `File.realpath` does this for us but only if the file
187
- # exists. If not, we use it on the parent directory instead and
188
- # use `File.join` to create the full path.
189
- if File.exist?(filename)
190
- File.file?(filename) or
191
- raise Error.new("Filename '#{filename}' exists but is not a file.")
192
-
193
- filename = File.realpath(filename)
194
- else
195
- dn = File.dirname(filename)
196
- File.directory?(dn) or
197
- raise Error.new("Parent directory '#{dn}' nonexistant or " +
198
- "not a directory.")
199
-
200
- filename = File.join(File.realpath(dn), File.basename(filename))
201
- end
202
-
203
- @@handles[filename] = Handle.new(filename) unless
204
- @@handles.has_key?(filename)
205
-
206
- return @@handles[filename]
207
- end
208
-
209
- # Close all underlying SQLite3::Database handles.
210
- def self.close_all
211
- @@handles.values.each{|h| h.close!}
212
- end
213
-
214
- # Close and remove all Handle objects with no refs and return a
215
- # hash mapping the filename for each live Handle to the DBM
216
- # objects that currently reference it. Does **NOT** perform a
217
- # Ruby GC.
218
- def self.gc
219
- results = {}
220
- @@handles.select!{|path, handle|
221
- refs = handle.refs
222
- if refs.empty?
223
- handle.close!
224
- next false
225
- end
226
-
227
- results[path] = refs
228
- true
229
- }
230
-
231
- return results
232
- end
233
- end
234
-
235
-
236
- # This module provides some basic access to the underlying
237
- # `SQLite3::Database` objects used by `Lite3::DBM` to actually store
238
- # and retrieve data.
239
- #
240
- # Things you need to care about are:
241
- #
242
- # 1. Use `threadsafe?` to see if the underlying `SQLite3` lib was
243
- # compiled to be threadsafe.
244
- #
245
- # 2. Invoke `Lite3::SQL.close_all` before forking the process if you
246
- # have ever opened a `Lite3::DBM` object and intend on using
247
- # `Lite3` in both the parent and the child process.
248
- #
249
- # More details:
250
- #
251
- # `Lite3` maintains a pool of private handle objects (private class
252
- # `Lite3::Handle`) which in turn manage the `SQLite3::Database`
253
- # objects that actually do the work. There is one handle per
254
- # SQLite3 database file; since each `DBM` represents one table in a
255
- # SQLite3 file, multiple `DBM` objects will use the same handle.
256
- #
257
- # Handle objects can themselves close and replace their
258
- # `SQLite3::Database` objects transparently.
259
- #
260
- # The underlying system keeps track of which `DBM` objects reference
261
- # which files and will close a file's `SQLite3::Database` when all
262
- # of the `DBM`s using it have been closed. (It does **not** handle
263
- # the case where a `DBM` object remains open and goes out of scope;
264
- # that object will be kept around for the life of the process.)
265
- #
266
- # Mostly, you don't need to care about this. However, it affects
267
- # you in two ways:
268
- #
269
- # 1. Transactions are done at the file level and not the table level.
270
- # This means that you can access separate tables in the same
271
- # transaction, which is a Very Good Thing.
272
- #
273
- # 2. You can safely fork the current process and keep using existing
274
- # `DBM` objects in both processes, provided you've invoked
275
- # `close_all` before the fork. This will have closed the actual
276
- # database handles (which can't tolerate being carried across a
277
- # fork) and opens new ones the next time they're needed.
278
- #
279
- # If you find yourself needing to be sure that you don't have any
280
- # unexpected open file handles (e.g. before forking or if you need
281
- # Windows to unlock it), you should call `close_all`.
282
- #
283
- # Otherwise, it's safe to ignore this stuff.
284
- module SQL
285
- # Test if the SQLite3 lib we are using has been compiled to be
286
- # thread safe. Just a wrapper around `SQLite3.threadsafe?`
287
- def self.threadsafe?
288
- return SQLite3.threadsafe?
289
- end
290
-
291
- # Clean up lingering unused database handle metadata.
292
- #
293
- # This is usually not a significant amount of space unless you
294
- # have opened and closed a lot of different database files, but
295
- # it's here if you need it.
296
- #
297
- # Returns a hash mapping each remaining handle's canonical file
298
- # path to a list of `DBM` objects that reference it. This is
299
- # probably not useful to you; it's there for the unit tests.
300
- def self.gc() return HandlePool.gc; end
301
-
302
- # Close and remove the underlying `SQLite3::Database` associated
303
- # with each `DBM`. A new `SQLite3::Database` will be created on
304
- # the file the first time the `DBM` is used.
305
- #
306
- # This **should not** be called while a database operation is in
307
- # progress. (E.g. do **not** call this from the block of
308
- # `DBM.each`.)
309
- def self.close_all() return HandlePool.close_all end
310
- end
311
-
312
-
313
- # Lite3::DBM encapsulates a single table in a single SQLite3
314
- # database file and lets you access it as easily as a Hash:
315
- #
316
- # require 'dbmlite3'
317
- #
318
- # db = Lite3::DBM.new("database.sqlite3", "table")
319
- # db["speed"] = 88
320
- # db["date"] = Date.new(1955, 11, 5)
321
- # db["power_threshold"] = 2.2
322
- #
323
- # db.each{|k, v| puts "#{k} -> #{v}"}
324
- #
325
- # double_speed = db["speed"] * 2
326
- #
327
- # db.close
328
- #
329
- # Note that keys must be Strings. As a special exception, Symbols
330
- # are also allowed but are transparently converted to Strings
331
- # first. This means that while something like this will work:
332
- #
333
- # db[:foo] = 42
334
- #
335
- # a subseqent
336
- #
337
- # db.keys.include?(:foo) or raise AbjectFailure.new
338
- #
339
- # will raise an exception because the key `:foo` was turned into a
340
- # string. you will need to do this instead:
341
- #
342
- # db.keys.include?('foo') or raise AbjectFailure.new
343
- #
344
- # Unlike DBM, values may (optionally) be any serializable Ruby type.
345
- #
346
- # You can select the serialization method with an optional third
347
- # constructor argument. Options are YAML (the default), `Marshal`
348
- # or simple string conversion with `to_s`. Each of these methods
349
- # will have their own pros and cons.
350
- #
351
- # **WARNING:** Both YAML and Marshal serialization have the usual
352
- # security caveats as described in the documentation for `Marshal`
353
- # and `Psych`. If you are going to let an untrusted entity modify
354
- # the database, you should not use these methods and instead stick
355
- # to string conversion.
356
- #
357
- # The table name must be a valid name identifier (i.e. matches
358
- # /^[a-zA-Z_]\w*$/).
359
- #
360
- # Lite3::DBM also provides access to SQLite3 transactions with the
361
- # `transaction` method:
362
- #
363
- # db.transaction {
364
- # (1..1000).each{ |n| db["key_#{n}"] = [n, n*2, n-1] }
365
- # }
366
- #
367
- # Outside of a transaction statement, each Lite3::DBM method that accesses
368
- # the database does so in a single transaction.
369
- #
370
- # Note that if you need to do a large number of database operations,
371
- # there is often a significant performance advantage to grouping them
372
- # together in a transaction.
373
- #
374
- # The underlying table will have the given name prefixed with the
375
- # string `dbmlite3_`. In addition, the table `meta_dbmlite3` holds
376
- # some related metadata. If your program also uses the SQLite3 gem to
377
- # access the database directly, you should not touch these tables or
378
- # expect their format to remain consistant.
379
- class Lite3::DBM
380
- include Enumerable
381
-
382
- PREFIX = "dbmlite3_"
383
- META = "meta_dbmlite3"
384
- private_constant(:PREFIX, :META)
385
-
386
- #
387
- # Construction and setup
388
- #
389
-
390
-
391
- # Create a new `Lite3::DBM` object that opens database file
392
- # `filename` and performs subsequent operations on `table`. Both
393
- # the database file and the table will be created if they do not
394
- # yet exist.
395
- #
396
- # The optional third argument `serializer` is used to choose the
397
- # serialization method for converting Ruby values into storable
398
- # strings. There are three options:
399
- #
400
- # * `:yaml` uses the `Psych` module.
401
- # * `:marshal` uses the `Marshal` module.
402
- # * `:string` simply uses the default `to_s` method, just like the
403
- # stock `DBM`.
404
- #
405
- # Each of these will have their pros and cons. The default is
406
- # `:yaml` because that is the most portable. `:marshal` tends to
407
- # be faster but is incompatible across minor Ruby versions.
408
- #
409
- # (Note that `DBM` does not check your Marshal version.)
410
- #
411
- # Your serializer choice is registered in a metadata table when
412
- # `tablename` is created in the SQLite3 file. Afterward, it is an
413
- # error to attempt to open the table with a different serializer
414
- # and will result in a Lite3::Error exception.
415
- #
416
- def initialize(filename, tablename, serializer = :yaml)
417
- @filename = filename
418
- @tablename = tablename
419
- @valenc,
420
- @valdec = value_encoders(serializer)
421
- @handle = HandlePool.get(filename)
422
-
423
- @handle.addref(self)
424
-
425
- check("Malformed table name '#{tablename}'; must be a valid identifer") {
426
- tablename =~ /^[a-zA-Z_]\w*$/
427
- }
428
-
429
- transaction {
430
- register_serialization_scheme(serializer)
431
- create_table_if_not_present()
432
- }
433
- rescue Error => e
434
- self.close if @handle
435
- raise e
436
- end
437
-
438
-
439
- # Identical to `initialize` except that if a block is provided, it
440
- # is evaluated with a new Lite3::DBM which is then closed afterward.
441
- # This is analagous to `File.open`.
442
- def self.open(filename, tablename, serializer = :yaml, &block)
443
- instance = self.new(filename, tablename, serializer)
444
- return instance unless block
445
-
446
- begin
447
- return block.call(instance)
448
- ensure
449
- instance.close
450
- end
451
- end
452
-
453
- private
454
-
455
- # Return encode and decode procs for the requested serialization
456
- # scheme.
457
- def value_encoders(serializer)
458
- case serializer
459
- when :yaml
460
- enc = proc{ |val| Psych.dump(val) }
461
-
462
- # Psych (and module YAML) has gradually moved from defaulting
463
- # from unsafe loading to safe loading. This is a pain for us
464
- # because old versions don't provide `unsafe_load` as an alias
465
- # to `load` and new versions default `load` to `safe_load`.
466
- # So we have to do this thing to pick `unsafe_load` if it's
467
- # available and `load` otherwise.
468
- if Psych.respond_to? :unsafe_load
469
- dec = proc{ |val| Psych.unsafe_load(val) }
470
- else
471
- dec = proc{ |val| Psych.load(val) }
472
- end
473
-
474
- when :marshal
475
- enc = proc { |val| Marshal.dump(val) }
476
- dec = proc { |val| Marshal.load(val) }
477
-
478
- when :string
479
- enc = proc { |val| val.to_s }
480
- dec = proc { |val| val.to_s } # sqlite preserves some types
481
-
482
- else
483
- raise Error.new("Invalid serializer selected: '#{serializer}'")
484
- end
485
-
486
- return enc, dec
487
- end
488
-
489
- # Create @table if it does not exist yet.
490
- def create_table_if_not_present
491
- @handle.sql <<-SQL
492
- create table if not exists #{actual_tbl} (
493
- key string primary key,
494
- value string
495
- );
496
- SQL
497
- end
498
-
499
- # Add the serialization scheme for this table to META
500
- def register_serialization_scheme(req_ser)
501
- @handle.sql <<-SQL
502
- create table if not exists #{META} (
503
- tbl string primary key,
504
- serializer string
505
- );
506
- SQL
507
-
508
- matched = false
509
- @handle.sql("select * from #{META} where tbl = ?", [@tablename]) { |row|
510
- tbl, ser = row
511
-
512
- msg = "Table '#{@tablename}' uses serializer '#{ser}'; " +
513
- "expecting '#{req_ser}'."
514
- raise Error.new(msg) unless req_ser.to_s == ser
515
-
516
- matched = true
517
- }
518
-
519
- matched or
520
- @handle.sql("insert into #{META} (tbl, serializer) values (?,?)",
521
- [@tablename,req_ser.to_s]);
522
- end
523
-
524
-
525
-
526
- #
527
- # Helpers
528
- #
529
-
530
-
531
- # Return the actual table name we are using.
532
- def actual_tbl() return "#{PREFIX}#{@tablename}"; end
533
-
534
-
535
- public
536
-
537
- def to_s
538
- openstr = closed? ? 'CLOSED' : 'OPEN'
539
- return "<#{self.class}:0x#{object_id.to_s(16)} file='#{@filename}'" +
540
- " tablename='#{@tablename}' #{openstr}>"
541
- end
542
- alias inspect to_s
543
-
544
- # Close `self`. Subsequent attempts at database operations will
545
- # fail with an exception but `closed?` will still work.
546
- #
547
- # Note that the underlying file handle (via the
548
- # `SQLite3::Database` object) will **only** be closed if there are
549
- # no other `DBM` objects using that file.
550
- def close
551
- @handle.delref(self)
552
- @handle = ClosedHandle.new(@filename, @tablename)
553
- end
554
-
555
- # Test if this object has been closed. This is safe to call on a
556
- # closed `DBM`.
557
- def closed?()
558
- return @handle.is_a? ClosedHandle
559
- end
560
-
561
- # Test if the underlying `SQLite3::Database` is closed. You
562
- # probably don't need to care about this method; it's mostly here
563
- # to help with unit tests.
564
- #
565
- # This will **not** work if `self` has been closed.
566
- def handle_closed?
567
- return @handle.closed?
568
- end
569
-
570
-
571
- #
572
- # Transactions
573
- #
574
-
575
-
576
- # Begins a transaction, evaluates the given block and then ends the
577
- # transaction. If no error occurred, the transaction is committed;
578
- # otherwise, it is rolled back.
579
- #
580
- # It is safe to call `DBM.transaction` within another
581
- # `DBM.transaction` block's call chain because `DBM` will not
582
- # start a new transaction on a database handle that already has
583
- # one in progress. (It may be possible to trick `DBM` into trying
584
- # via fibers or other flow control trickery; don't do that.)
585
- #
586
- # It is also not safe to mix `DBM` transactions and bare `SQLite3`
587
- # transactions.
588
- #
589
- # Transactions are always executed in `:deferred` mode.
590
- #
591
- # @yield [db] The block takes a reference to the receiver as an
592
- # argument.
593
- #
594
- def transaction(&block)
595
- return @handle.transaction { block.call(self) }
596
- end
597
-
598
- # Test if there is currently a transaction in progress
599
- def transaction_active?
600
- return @handle.transaction_active?
601
- end
602
-
603
-
604
- #
605
- # Basic hash-like access
606
- #
607
-
608
-
609
- # Store `value` at `key` in the database.
610
- #
611
- # `key` **must** be a String or a Symbol.
612
- #
613
- # `value` **must** be convertable to string by whichever
614
- # serialization method you have chosen.
615
- def []=(key, value)
616
- key = check_key(key)
617
- valstr = SQLite3::Blob.new( @valenc.call(value) )
618
-
619
- # At one point, this operation was done with SQLite3's UPSERT:
620
- #
621
- # insert into #{actual_tbl} (key, value) values (?,?)
622
- # on conflict(key) do update set value = ?;
623
- #
624
- # Unfortunately, this capability was only added to SQLite3 in
625
- # 2018, which means that at the time of this writing (2022)
626
- # there are still a lot of systems out there that have older
627
- # versions of SQLite3 and so can't do this.
628
- #
629
- # The venerable `insert or replace` feature **almost** does what
630
- # I want:
631
- #
632
- # insert or replace into #{actual_tbl} (key, value) values (?, ?);
633
- #
634
- # The one problem is that it changes the order of the rows,
635
- # which we need to preserve in order to remain consistent with
636
- # `Hash` semantics (and because it's useful).
637
- #
638
- # So we kick it old school and do a `select` followed by an
639
- # `insert` or `update` wrapped in a transaction.
640
- transaction {
641
- rowid = nil
642
- select = "select rowid, key, value from #{actual_tbl} where key = ?;"
643
- @handle.sql(select, [key]) { |row|
644
- rowid = row[0]
645
- }
646
-
647
- if rowid
648
- update = "update #{actual_tbl} set value = ? where rowid = ?;"
649
- @handle.sql(update, [valstr, rowid])
650
- else
651
- insert = "insert into #{actual_tbl} (key, value) values (?,?);"
652
- @handle.sql(insert, [key, valstr])
653
- end
654
- }
655
-
656
- return value
657
- end
658
- alias store :'[]='
659
-
660
- # Retrieve the value associated with `key` from the database or
661
- # nil if it is not present.
662
- def [](key)
663
- return fetch(key, nil)
664
- end
665
-
666
- # Retrieve the value associated with `key`.
667
- #
668
- # If it is not present and a block is given, evaluate the block
669
- # with the key as its argument and return that.
670
- #
671
- # If no block was given either but one extra parameter was given,
672
- # that value is returned instead.
673
- #
674
- # Finally, if none of these was given, it throws an `IndexError`
675
- # exception.
676
- #
677
- # It is an error if `fetch` is called with more than two arguments.
678
- #
679
- # @yield [key] The fallback block.
680
- def fetch(key, *args, &default_block)
681
-
682
- # Ensure there are no extra arguments
683
- nargs = args.size + 1
684
- check("Too many arguments for 'fetch'; expected 1 or 2; got #{nargs}") {
685
- nargs <= 2
686
- }
687
-
688
- # Retrieve the value
689
- key = check_key(key)
690
- rows = @handle.sql("select * from #{actual_tbl} where key=?" +
691
- " order by rowid;", [key])
692
- check("Multiple matches for key '#{key}'!") { rows.size <= 1 }
693
-
694
- # Return the value if found
695
- return @valdec.call(rows[0][1]) unless rows.empty?
696
-
697
- # Otherwise, evaluate the block and use its result if a block was
698
- # given
699
- return default_block.call(key) if default_block
700
-
701
- # Next, see if we have a default value we can return
702
- return args[0] if args.size > 0
703
-
704
- # And if all else fails, raise an IndexError.
705
- raise IndexError.new("key '#{key}' not found.")
706
- end
707
-
708
- # Return a new `Array` containing the values corresponding to the
709
- # given keys.
710
- def values_at(*keys)
711
- return keys.map{|k| self[k]}
712
- end
713
-
714
- # Return an `Array` of all of the keys in the table.
715
- #
716
- # **WARNING:** since this list is being read from disk, it is possible
717
- # that the result could exceed available memory.
718
- def keys
719
- keys = []
720
- each_key{|k| keys.push k}
721
- return keys
722
- end
723
-
724
- # Return an array of all values in the table.
725
- #
726
- # **WARNING:** since this list is being read from disk, it is possible
727
- # that the result could exceed available memory.
728
- def values
729
- values = []
730
- each_value {|val| values.push val }
731
- return values
732
- end
733
-
734
- # Return `true` if the table contains `key`; otherwise, return
735
- # `false`.
736
- def has_key?(key)
737
- return false unless key.class == String || key.class == Symbol
738
- fetch( key ) { return false }
739
- return true
740
- end
741
- alias include? has_key?
742
- alias member? has_key?
743
- alias key? has_key?
744
-
745
- # Delete all entries from the table.
746
- def clear
747
- transaction { @handle.sql("delete from #{actual_tbl};", []) }
748
- end
749
-
750
- # Calls the given block with each key-value pair in the usual
751
- # order, then return self. The entire call takes place in its own
752
- # transaction.
753
- #
754
- # If no block is given, returns an Enumerator instead. The
755
- # Enumerator does *not* start a transaction but individual
756
- # accesses of it (e.g. calling `next`) each take place in their
757
- # own transaction.
758
- #
759
- # @yield [key, value] The block to evaluate
760
- def each(&block)
761
- return self.to_enum(:nt_each) unless block
762
- transaction { nt_each(&block) }
763
- return self
764
- end
765
- alias each_pair each
766
-
767
- private
768
-
769
- # Back-end for `each`; does not explicitly start a transaction.
770
- def nt_each(&block)
771
- #return self.to_enum unless block
772
-
773
- front = "select rowid, key, value from #{actual_tbl} "
774
- back = " order by rowid limit 1;"
775
-
776
- # We do enumeration by looking up each item in a separate query
777
- # rather than attaching a block to the SQL query. This is so that
778
- # it is safe to access `self` from inside the block.
779
- get_row = proc do |first, last_id|
780
- query = front
781
- query += "where rowid > ?" unless first
782
- query += back
783
-
784
- vars = []
785
- vars.push last_id unless first
786
-
787
- row = @handle.sql(query, vars)
788
- return self if row.empty?
789
-
790
- rowid, key, value = row[0]
791
- block.call(key, @valdec.call(value))
792
-
793
- next rowid
794
- end
795
-
796
- rowid = get_row.call(true, nil)
797
- while true
798
- rowid = get_row.call(false, rowid)
799
- end
800
-
801
- return self # not reached
802
- end
803
-
804
- public
805
-
806
- # Calls the given block with each key; returns self. Exactly like
807
- # `each` except for the block argument.
808
- #
809
- # @yield [key] The block to evaluate
810
- def each_key(&block)
811
- return Enumerator.new{|y| nt_each{ |k,v| y << k } } unless block
812
- return each{ |k,v| block.call(k) }
813
- end
814
-
815
- # Calls the given block with each value; returns self. Exactly like
816
- # `each` except for the block argument.
817
- #
818
- # @yield [value] The block to evaluate
819
- def each_value(&block)
820
- return Enumerator.new{|y| nt_each{ |k,v| y << v } } unless block
821
- return each{ |k,v| block.call(v) }
822
- end
823
-
824
- # Updates the database with multiple values from the specified
825
- # object. Takes any object which implements the each_pair method,
826
- # including `Hash` and `DBM` objects.
827
- def update(hash)
828
- transaction {
829
- hash.each{|k, v| self[k] = v }
830
- }
831
- end
832
-
833
- # Remove `key` and its associated value from `self`. If `key` is
834
- # not present, does nothing.
835
- def delete(key)
836
- transaction {
837
- @handle.sql("delete from #{actual_tbl} where key = ?", [key])
838
- }
839
- end
840
-
841
- # Evaluate the block on each key-value pair in `self` end delete
842
- # each entry for which the block returns true.
843
- #
844
- # @yield [value] The block to evaluate
845
- def delete_if(&block)
846
- transaction {
847
- self.each{ |k, v| block.call(k,v) and delete(k) }
848
- }
849
- end
850
- alias reject! delete_if
851
-
852
- # Return the number of entries (key-value pairs) in `self`.
853
- def size
854
- @handle.sql("select count(*) from #{actual_tbl};", []) { |row|
855
- return row[0]
856
- }
857
- check("count query failed!")
858
- end
859
- alias length size
860
-
861
- # Test if `self` is empty.
862
- def empty?
863
- return size == 0
864
- end
865
-
866
-
867
- #
868
- # Conversion to internal types
869
- #
870
-
871
-
872
- # Copies the table into a `Hash` and returns it.
873
- #
874
- # **WARNING:** it is possible for tables to be significantly larger
875
- # than available RAM; in that case, this will likely crash your
876
- # program.
877
- def to_hash
878
- result = {}
879
- each{|k,v| result[k] = v}
880
- return result
881
- end
882
-
883
-
884
- # Returns an `Array` of 2-element `Array` objects each containing a
885
- # key-value pair from `self`.
886
- #
887
- # **WARNING:** it is possible for tables to be significantly larger
888
- # than available RAM; in that case, this will likely crash your
889
- # program.
890
- def to_a
891
- result = []
892
- each { |k,v| result.push [k,v] }
893
- return result
894
- end
895
-
896
-
897
- #
898
- # Hacky odds and ends
899
- #
900
-
901
-
902
- # Test if `val` is one of the values in this table.
903
- #
904
- # Potentially very slow, especially on large tables.
905
- def has_value?(val)
906
- self.each{|k,v| return true if v == val}
907
- return false
908
- end
909
- alias value? has_value?
910
-
911
- # Return a `Hash` whose keys are the table's values and whose values
912
- # are the table's keys.
913
- #
914
- # **WARNING:** it is possible for tables to be significantly larger
915
- # than available RAM; in that case, this will likely crash your
916
- # program.
917
- def invert
918
- result = {}
919
- each{|k,v| result[v] = k}
920
- return result
921
- end
922
-
923
- # Remove the first key/value pair from `self` and return it. "First"
924
- # is defined by `self`'s row order, which is the order of insertion
925
- # as determined by SQLite3.
926
- def shift
927
- transaction {
928
- return nil if empty?
929
-
930
- key, value = self.each.first
931
- delete(key)
932
-
933
- return [key, value]
934
- }
935
- end
936
-
937
- private
938
-
939
- # Attempt to turn 'key' to a valid key and raise an exception if
940
- # that isn't possible.
941
- def check_key(key)
942
- key = key.to_s if key.class == Symbol
943
- raise TypeError.new("Key '#{key}' is not a string or symbol!") unless
944
- key.class == String
10
+ private_constant :IS_JRUBY
11
+ end
945
12
 
946
- return key
947
- end
948
13
 
949
- # Error check: if block evaluates to false, raise a Lite3::DBM::Error
950
- # with the given message.
951
- def check(message, &block)
952
- return if block && block.call
953
- raise Error.new(message)
954
- end
955
- end
14
+ require_relative 'internal_lite3/error.rb'
15
+ require_relative 'internal_lite3/handle.rb'
16
+ require_relative 'internal_lite3/sql.rb'
17
+ require_relative 'internal_lite3/dbm.rb'
956
18
 
957
- private_constant :Handle, :ClosedHandle, :HandlePool
958
- end