bdb 0.1.0

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.
@@ -0,0 +1,74 @@
1
+ require 'bdb/base'
2
+
3
+ class Bdb::PartitionedDatabase < Bdb::Base
4
+ SEPARATOR = '__'
5
+ PARTITION_PATTERN = /^[-\w]*$/
6
+
7
+ def initialize(base_name, opts = {})
8
+ @base_name = base_name
9
+ @partition_by = opts.delete(:partition_by)
10
+ super(opts)
11
+ end
12
+ attr_reader :base_name, :partition_by, :partition
13
+
14
+ def databases
15
+ @databases ||= {}
16
+ end
17
+
18
+ def database(partition = nil)
19
+ partition ||= self.partition
20
+ raise 'partition value required' if partition.nil?
21
+ partition = partition.to_s
22
+ raise "invalid partition value: #{partition}" unless partition =~ PARTITION_PATTERN
23
+
24
+ databases[partition] ||= begin
25
+ name = [partition, base_name].join(SEPARATOR)
26
+ database = Bdb::Database.new(name, config)
27
+ indexes.each do |field, opts|
28
+ database.index_by(field, opts)
29
+ end
30
+ database
31
+ end
32
+ end
33
+
34
+ def partitions
35
+ Dir[environment.path + "/*#{SEPARATOR}#{base_name}"].collect do |file|
36
+ File.basename(file).split(SEPARATOR).first
37
+ end
38
+ end
39
+
40
+ def with_partition(partition)
41
+ @partition, old_partition = partition, @partition
42
+ yield
43
+ ensure
44
+ @partition = old_partition
45
+ end
46
+
47
+ def close
48
+ databases.each do |partition, database|
49
+ database.close
50
+ end
51
+ @databases.clear
52
+ end
53
+
54
+ def get(*keys, &block)
55
+ opts = keys.last.kind_of?(Hash) ? keys.last : {}
56
+ database(opts[partition_by]).get(*keys, &block)
57
+ end
58
+
59
+ def set(key, value, opts = {})
60
+ partition = get_field(partition_by, value)
61
+ database(partition).set(key, value, opts)
62
+ end
63
+
64
+ def delete(key, opts = {})
65
+ database(opts[partition_by]).delete(key)
66
+ end
67
+
68
+ # Deletes all records in the database. Beware!
69
+ def truncate!
70
+ partitions.each do |partition|
71
+ database(partition).truncate!
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,41 @@
1
+ class Bdb::ResultSet
2
+ class LimitReached < Exception; end
3
+
4
+ def initialize(opts, &block)
5
+ @block = block
6
+ @count = 0
7
+ @limit = opts[:limit] || opts[:per_page]
8
+ @limit = @limit.to_i if @limit
9
+ @offset = opts[:offset] || (opts[:page] ? @limit * (opts[:page] - 1) : 0)
10
+ @offset = @offset.to_i if @offset
11
+
12
+ if @group = opts[:group]
13
+ raise 'block not supported with group' if @block
14
+ @results = hash_class.new
15
+ else
16
+ @results = []
17
+ end
18
+ end
19
+ attr_reader :count, :group, :limit, :offset, :results
20
+
21
+ def hash_class
22
+ @hash_class ||= defined?(ActiveSupport::OrderedHash) ? ActiveSupport::OrderedHash : Hash
23
+ end
24
+
25
+ def <<(item)
26
+ @count += 1
27
+ return if count <= offset
28
+
29
+ raise LimitReached if limit and count > limit + offset
30
+
31
+ if group
32
+ key = item.bdb_locator_key
33
+ group_key = group.is_a?(Fixnum) ? key[0,group] : key
34
+ (results[group_key] ||= []) << item
35
+ elsif @block
36
+ @block.call(item)
37
+ else
38
+ results << item
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,31 @@
1
+ require 'rubygems'
2
+ require File.dirname(__FILE__) + '/../lib/bdb/simple'
3
+ require 'benchmark'
4
+
5
+ N = 10_000
6
+ A = [5, 6, "foo", :bar, "bar", {}, :foo, [1,2,4], true, [1,2,3], false, [1], [2], nil].collect {|i| Marshal.dump(i)}
7
+
8
+ puts "compare_absolute (#{N} times)"
9
+ t = Benchmark.measure do
10
+ N.times do
11
+ A.sort {|a,b| Bdb::Simple.compare_absolute(Marshal.load(a), Marshal.load(b)) }
12
+ end
13
+ end
14
+ puts t
15
+
16
+ puts "compare_hash (#{N} times)"
17
+ t = Benchmark.measure do
18
+ N.times do
19
+ A.sort {|a,b| Marshal.load(a).hash <=> Marshal.load(b).hash }
20
+ end
21
+ end
22
+ puts t
23
+
24
+ puts "compare_raw (#{N} times)"
25
+ t = Benchmark.measure do
26
+ N.times do
27
+ A.sort
28
+ end
29
+ end
30
+ puts t
31
+
@@ -0,0 +1,150 @@
1
+ require 'test_helper'
2
+
3
+ class CursorTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ mkdir File.join(File.dirname(__FILE__), 'tmp')
7
+ @db = Bdb::Db.new
8
+ @db.open(nil, File.join(File.dirname(__FILE__), 'tmp', 'test.db'), nil, Bdb::Db::BTREE, Bdb::DB_CREATE, 0)
9
+ 10.times { |i| @db.put(nil, i.to_s, "data-#{i}", 0)}
10
+ @cursor = @db.cursor(nil, 0)
11
+ end
12
+
13
+ def teardown
14
+ @cursor.close if @cursor
15
+ assert(@db.close(0)) if @db
16
+ rm_rf File.join(File.dirname(__FILE__), 'tmp')
17
+ end
18
+
19
+ def test_get
20
+ key, value = @cursor.get(nil, nil, Bdb::DB_FIRST)
21
+ assert_equal '0', key
22
+ assert_equal 'data-0', value
23
+ end
24
+
25
+ def test_get_range
26
+ keys = []
27
+ key, value = @cursor.get("4", nil, Bdb::DB_SET_RANGE)
28
+ while key and key <= "9"
29
+ keys << key
30
+ key, value = @cursor.get(nil, nil, Bdb::DB_NEXT)
31
+ end
32
+
33
+ assert_equal (4..9).collect {|i| i.to_s}, keys
34
+ end
35
+
36
+ def test_pget
37
+ @db1 = Bdb::Db.new
38
+ @db1.flags = Bdb::DB_DUPSORT
39
+ @db1.open(nil, File.join(File.dirname(__FILE__), 'tmp', 'test1.db'), nil, Bdb::Db::HASH, Bdb::DB_CREATE, 0)
40
+
41
+ @db.associate(nil, @db1, 0, proc { |sdb, key, data| key.split('-')[0] })
42
+
43
+ @db.put(nil, '1234-5678', 'data', 0)
44
+ @db.put(nil, '5678-1234', 'atad', 0)
45
+
46
+ @cursor1 = @db1.cursor(nil, 0)
47
+ key, pkey, value = @cursor1.pget(nil, nil, Bdb::DB_FIRST)
48
+ assert_equal '1234', key
49
+ assert_equal '1234-5678', pkey
50
+ assert_equal 'data', value
51
+
52
+ @cursor1.close
53
+ @db1.close(0)
54
+ end
55
+
56
+ def test_put
57
+ @cursor.put('10_000', 'data-10_000', Bdb::DB_KEYLAST)
58
+ value = @db.get(nil, '10_000', nil, 0)
59
+ assert_equal 'data-10_000', value
60
+ end
61
+
62
+ def test_del
63
+ key, value = @cursor.get(nil, nil, Bdb::DB_FIRST)
64
+ value = @db.get(nil, '0', nil, 0)
65
+ assert_equal '0', key
66
+ assert_equal 'data-0', value
67
+ @cursor.del
68
+ value = @db.get(nil, '0', nil, 0)
69
+ assert_nil value
70
+ end
71
+
72
+ def test_count
73
+ @cursor.get(nil, nil, Bdb::DB_FIRST)
74
+ assert_equal 1, @cursor.count
75
+ end
76
+
77
+ def test_get_all_in_order
78
+ all = []
79
+ while pair = @cursor.get(nil, nil, Bdb::DB_NEXT)
80
+ all << pair.first
81
+ end
82
+ assert_equal (0..9).collect {|i| i.to_s}, all
83
+ end
84
+
85
+ def test_get_all_with_btree_compare
86
+ @db1 = Bdb::Db.new
87
+ @db1.btree_compare = proc {|db, key1, key2| key2 <=> key1}
88
+ @db1.open(nil, File.join(File.dirname(__FILE__), 'tmp', 'test1.db'), nil, Bdb::Db::BTREE, Bdb::DB_CREATE, 0)
89
+ 10.times { |i| @db1.put(nil, i.to_s, "data-#{i}", 0)}
90
+ @cursor1 = @db1.cursor(nil, 0)
91
+
92
+ all = []
93
+ while pair = @cursor1.get(nil, nil, Bdb::DB_NEXT)
94
+ all << pair.first
95
+ end
96
+ assert_equal (0..9).collect {|i| i.to_s}.reverse, all
97
+ @cursor1.close
98
+ @db1.close(0)
99
+ end
100
+
101
+ def test_btree_compare_raises_if_fixnum_not_returned
102
+ @db1 = Bdb::Db.new
103
+ @db1.btree_compare = proc {|db, key1, key2| key1}
104
+ @db1.open(nil, File.join(File.dirname(__FILE__), 'tmp', 'test1.db'), nil, Bdb::Db::BTREE, Bdb::DB_CREATE, 0)
105
+
106
+ assert_raises(TypeError) do
107
+ @db1.put(nil, "no", "way", 0)
108
+ @db1.put(nil, "ho", "say", 0)
109
+ end
110
+ @db1.close(Bdb::DB_NOSYNC)
111
+ end
112
+
113
+ def test_join
114
+ @personnel_db = Bdb::Db.new
115
+ @personnel_db.open(nil, File.join(File.dirname(__FILE__), 'tmp', 'personnel_db.db'), nil, Bdb::Db::HASH, Bdb::DB_CREATE, 0)
116
+
117
+ @names_db = Bdb::Db.new
118
+ @names_db.flags = Bdb::DB_DUPSORT
119
+ @names_db.open(nil, File.join(File.dirname(__FILE__), 'tmp', 'names_db.db'), nil, Bdb::Db::HASH, Bdb::DB_CREATE, 0)
120
+
121
+ @jobs_db = Bdb::Db.new
122
+ @jobs_db.flags = Bdb::DB_DUPSORT
123
+ @jobs_db.open(nil, File.join(File.dirname(__FILE__), 'tmp', 'jobs_db.db'), nil, Bdb::Db::HASH, Bdb::DB_CREATE, 0)
124
+
125
+ [{:ssn => '111-11-1111', :lastname => 'Smith', :job => 'welder'},
126
+ {:ssn => '222-22-2222', :lastname => 'Jones', :job => 'welder'},
127
+ {:ssn => '333-33-3333', :lastname => 'Smith', :job => 'painter'}].each do |e|
128
+ @personnel_db.put(nil, e[:ssn], Marshal.dump([e[:ssn], e[:lastname], e[:job]]), 0)
129
+ @names_db.put(nil, e[:lastname], e[:ssn], 0)
130
+ @jobs_db.put(nil, e[:job], e[:ssn], 0)
131
+ end
132
+
133
+ @name_cursor = @names_db.cursor(nil, 0)
134
+ @name_cursor.get('Smith', nil, Bdb::DB_SET)
135
+ assert_equal 2, @name_cursor.count
136
+ @job_cursor = @jobs_db.cursor(nil, 0)
137
+ @job_cursor.get('welder', nil, Bdb::DB_SET)
138
+ assert_equal 2, @job_cursor.count
139
+ @personnel_cursor = @personnel_db.join([@name_cursor, @job_cursor], 0)
140
+ assert_equal '111-11-1111', @personnel_cursor.get(nil, nil, 0).first
141
+
142
+ @personnel_cursor.close
143
+ @name_cursor.close
144
+ @job_cursor.close
145
+
146
+ @jobs_db.close(0)
147
+ @names_db.close(0)
148
+ @personnel_db.close(0)
149
+ end
150
+ end
@@ -0,0 +1,157 @@
1
+ require 'test_helper'
2
+
3
+ class DbTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ mkdir File.join(File.dirname(__FILE__), 'tmp')
7
+ @db = Bdb::Db.new
8
+ @db.open(nil, File.join(File.dirname(__FILE__), 'tmp', 'test.db'), nil, Bdb::Db::BTREE, Bdb::DB_CREATE, 0)
9
+ end
10
+
11
+ def teardown
12
+ assert(@db.close(0)) if @db
13
+ rm_rf File.join(File.dirname(__FILE__), 'tmp')
14
+ end
15
+
16
+ def test_put_and_get
17
+ @db.put(nil, 'key', 'data', 0)
18
+ result = @db.get(nil, 'key', nil, 0)
19
+ assert_equal 'data', result
20
+ end
21
+
22
+ def test_del
23
+ @db.put(nil, 'key', 'data', 0)
24
+ result = @db.get(nil, 'key', nil, 0)
25
+ assert_equal 'data', result
26
+ @db.del(nil, 'key', 0)
27
+ result = @db.get(nil, 'key', nil, 0)
28
+ assert_nil result
29
+ end
30
+
31
+ def test_flags_set_and_get
32
+ @db1 = Bdb::Db.new
33
+ @db1.flags = Bdb::DB_DUPSORT
34
+ assert Bdb::DB_DUPSORT, @db1.flags
35
+ end
36
+
37
+ def test_associate_and_pget
38
+ @db1 = Bdb::Db.new
39
+ @db1.flags = Bdb::DB_DUPSORT
40
+ @db1.open(nil, File.join(File.dirname(__FILE__), 'tmp', 'test1.db'), nil, Bdb::Db::HASH, Bdb::DB_CREATE, 0)
41
+
42
+ @db.associate(nil, @db1, 0, proc { |sdb, key, data| key.split('-')[0] })
43
+
44
+ @db.put(nil, '1234-5678', 'data', 0)
45
+ @db.put(nil, '5678-1234', 'atad', 0)
46
+
47
+ result = @db.get(nil, '1234-5678', nil, 0)
48
+ assert_equal 'data', result
49
+ result = @db1.get(nil, '5678', nil, 0)
50
+ assert_equal 'atad', result
51
+
52
+ result = @db1.pget(nil, '1234', nil, 0)
53
+ assert_equal ['1234-5678', 'data'], result
54
+
55
+ @db1.close(0)
56
+ end
57
+
58
+ def test_associate_with_multiple_keys
59
+ @db1 = Bdb::Db.new
60
+ @db1.flags = Bdb::DB_DUPSORT
61
+ @db1.open(nil, File.join(File.dirname(__FILE__), 'tmp', 'test1.db'), nil, Bdb::Db::HASH, Bdb::DB_CREATE, 0)
62
+
63
+ @db.associate(nil, @db1, 0, proc { |sdb, key, data| key.split('-') })
64
+
65
+ @db.put(nil, '1234-5678', 'data', 0)
66
+ @db.put(nil, '8765-4321', 'atad', 0)
67
+
68
+ result = @db.get(nil, '1234-5678', nil, 0)
69
+ assert_equal 'data', result
70
+ result = @db1.get(nil, '5678', nil, 0)
71
+ assert_equal 'data', result
72
+ result = @db1.get(nil, '1234', nil, 0)
73
+ assert_equal 'data', result
74
+ result = @db1.get(nil, '8765', nil, 0)
75
+ assert_equal 'atad', result
76
+ result = @db1.get(nil, '4321', nil, 0)
77
+ assert_equal 'atad', result
78
+
79
+ @db1.close(0)
80
+ end
81
+
82
+ def test_aset_and_aget
83
+ @db['key'] = 'data'
84
+ result = @db.get(nil, 'key', nil, 0)
85
+ assert_equal 'data', result
86
+ result = @db['key']
87
+ assert_equal 'data', result
88
+ @db['key'] = 'data1'
89
+ result = @db['key']
90
+ assert_equal 'data1', result
91
+ end
92
+
93
+ def test_get_byteswapped
94
+ @db.get_byteswapped
95
+ end
96
+
97
+ def test_get_type
98
+ assert_equal Bdb::Db::BTREE, @db.get_type
99
+ end
100
+
101
+ def test_remove
102
+ @db1 = Bdb::Db.new
103
+ @db1.open(nil, File.join(File.dirname(__FILE__), 'tmp', 'other_test.db'), nil, Bdb::Db::BTREE, Bdb::DB_CREATE, 0)
104
+ @db1.close(0)
105
+ Bdb::Db.new.remove(File.join(File.dirname(__FILE__), 'tmp', 'other_test.db'), nil, 0)
106
+ assert !File.exists?(File.join(File.dirname(__FILE__), 'tmp', 'other_test.db'))
107
+ end
108
+
109
+ def test_key_range
110
+ 10.times { |i| @db.put(nil, i.to_s, 'data', 0) }
111
+ @db.key_range(nil, '2', 0)
112
+ end
113
+
114
+ def test_rename
115
+ @db1 = Bdb::Db.new
116
+ @db1.open(nil, File.join(File.dirname(__FILE__), 'tmp', 'other_test.db'), nil, Bdb::Db::BTREE, Bdb::DB_CREATE, 0)
117
+ @db1.close(0)
118
+ assert Bdb::Db.new.rename(File.join(File.dirname(__FILE__), 'tmp', 'other_test.db'), nil, File.join(File.dirname(__FILE__), 'tmp', 'other2_test.db'), 0)
119
+ assert File.exists?(File.join(File.dirname(__FILE__), 'tmp', 'other2_test.db'))
120
+ end
121
+
122
+ def test_pagesize_get_and_set
123
+ @db1 = Bdb::Db.new
124
+ @db1.pagesize = 1024
125
+ assert_equal 1024, @db1.pagesize
126
+ end
127
+
128
+ def test_h_ffactor_get_and_set
129
+ @db1 = Bdb::Db.new
130
+ @db1.h_ffactor = 5
131
+ assert_equal 5, @db1.h_ffactor
132
+ end
133
+
134
+ def test_h_nelem_get_and_set
135
+ @db1 = Bdb::Db.new
136
+ @db1.h_nelem = 10_000
137
+ assert_equal 10_000, @db1.h_nelem
138
+ end
139
+
140
+ def test_sync
141
+ assert @db.sync
142
+ end
143
+
144
+ def test_truncate
145
+ @db.put(nil, 'key', 'data', 0)
146
+ result = @db.get(nil, 'key', nil, 0)
147
+ assert_equal 'data', result
148
+ @db.truncate(nil)
149
+ result = @db.get(nil, 'key', nil, 0)
150
+ assert_nil result
151
+ end
152
+
153
+ def test_compact
154
+ assert @db.compact(nil, nil, nil, nil, 0)
155
+ end
156
+
157
+ end