bdb 0.1.0

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