dbmlite3 1.0.0 → 2.0.0.pre.alpha.4

Sign up to get free protection for your applications and to get access to all the features.
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