rod 0.7.1 → 0.7.2
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/.travis.yml +1 -1
- data/README.rdoc +38 -10
- data/Rakefile +20 -9
- data/changelog.txt +25 -0
- data/contributors.txt +1 -0
- data/data/backward/0.7.0/_join_element.dat +0 -0
- data/data/backward/0.7.0/_polymorphic_join_element.dat +0 -0
- data/data/backward/0.7.0/char.dat +0 -0
- data/data/backward/0.7.0/database.yml +58 -0
- data/data/backward/0.7.0/rod_test__automobile.dat +0 -0
- data/data/backward/0.7.0/rod_test__caveman.dat +0 -0
- data/data/backward/0.7.0/rod_test__dog.dat +0 -0
- data/data/backward/0.7.0/rod_test__test_model.dat +0 -0
- data/data/portability/_join_element.dat +0 -0
- data/data/portability/_polymorphic_join_element.dat +0 -0
- data/data/portability/char.dat +0 -0
- data/data/portability/database.yml +49 -0
- data/data/portability/rod_test__automobile.dat +0 -0
- data/data/portability/rod_test__caveman.dat +0 -0
- data/data/portability/rod_test__dog.dat +0 -0
- data/data/portability/rod_test__test_model.dat +0 -0
- data/features/backward.feature +33 -0
- data/features/basic.feature +3 -0
- data/features/collection_proxy.feature +95 -0
- data/features/flat_indexing.feature +44 -2
- data/features/hash_indexing.feature +63 -9
- data/features/portability.feature +72 -0
- data/features/segmented_indexing.feature +45 -2
- data/features/steps/collection_proxy.rb +1 -1
- data/features/steps/model.rb +48 -5
- data/features/steps/rod.rb +15 -16
- data/lib/rod.rb +11 -1
- data/lib/rod/abstract_database.rb +52 -42
- data/lib/rod/berkeley/collection_proxy.rb +96 -0
- data/lib/rod/berkeley/database.rb +337 -0
- data/lib/rod/berkeley/environment.rb +209 -0
- data/lib/rod/berkeley/sequence.rb +222 -0
- data/lib/rod/berkeley/transaction.rb +233 -0
- data/lib/rod/collection_proxy.rb +76 -1
- data/lib/rod/constants.rb +3 -2
- data/lib/rod/database.rb +127 -14
- data/lib/rod/index/base.rb +12 -3
- data/lib/rod/index/hash_index.rb +295 -70
- data/lib/rod/index/segmented_index.rb +3 -0
- data/lib/rod/model.rb +154 -531
- data/lib/rod/property/base.rb +190 -0
- data/lib/rod/property/field.rb +258 -0
- data/lib/rod/property/plural_association.rb +145 -0
- data/lib/rod/property/singular_association.rb +139 -0
- data/rod.gemspec +6 -4
- data/spec/berkeley/database.rb +83 -0
- data/spec/berkeley/environment.rb +58 -0
- data/spec/berkeley/sequence.rb +101 -0
- data/spec/berkeley/transaction.rb +92 -0
- data/spec/collection_proxy.rb +38 -0
- data/spec/database.rb +36 -0
- data/spec/model.rb +26 -0
- data/spec/property/base.rb +73 -0
- data/spec/property/field.rb +244 -0
- data/spec/property/plural_association.rb +67 -0
- data/spec/property/singular_association.rb +65 -0
- data/tests/class_compatibility_create.rb +2 -2
- data/tests/eff1_test.rb +1 -1
- data/tests/eff2_test.rb +1 -1
- data/tests/full_runs.rb +1 -1
- data/tests/generate_classes_create.rb +14 -14
- data/tests/migration_create.rb +47 -47
- data/tests/migration_verify.rb +1 -1
- data/tests/missing_class_create.rb +6 -6
- data/tests/properties_order_create.rb +4 -4
- data/tests/read_on_create.rb +33 -34
- data/tests/save_struct.rb +40 -39
- data/tests/unit/database.rb +1 -1
- data/tests/unit/model_tests.rb +73 -65
- metadata +71 -15
- data/tests/unit/model.rb +0 -36
data/features/steps/rod.rb
CHANGED
@@ -6,16 +6,14 @@ Given /^the library works in development mode$/ do
|
|
6
6
|
Rod::Database.development_mode = true
|
7
7
|
end
|
8
8
|
|
9
|
-
Given /^(the )?(\w+) is created( in (\
|
9
|
+
Given /^(?:the )?(\w+) is created(?: in (\S+))?$/ do |db_name,location|
|
10
10
|
get_db(db_name).instance.close_database if get_db(db_name).instance.opened?
|
11
|
-
if
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
db_location = db_name
|
16
|
-
end
|
11
|
+
if location
|
12
|
+
db_location = location
|
13
|
+
else
|
14
|
+
db_location = "tmp/#{db_name}"
|
17
15
|
end
|
18
|
-
get_db(db_name).instance.create_database(
|
16
|
+
get_db(db_name).instance.create_database(db_location)
|
19
17
|
@instances = {}
|
20
18
|
end
|
21
19
|
|
@@ -42,27 +40,28 @@ Given /^the class space is cleared$/ do
|
|
42
40
|
end
|
43
41
|
|
44
42
|
# Should be split
|
45
|
-
|
43
|
+
# I reopen the database for reading in tmp/location1
|
44
|
+
When /^I reopen (?:the )?(\w+)( for reading)?(?: in (\S+))?$/ do |db_name,reading,location|
|
46
45
|
if location
|
47
|
-
db_location =
|
46
|
+
db_location = location
|
48
47
|
else
|
49
|
-
db_location = db_name
|
48
|
+
db_location = "tmp/#{db_name}"
|
50
49
|
end
|
51
50
|
get_db(db_name).instance.close_database
|
52
51
|
get_db(db_name).instance.clear_cache
|
53
52
|
readonly = reading.nil? ? false : true
|
54
|
-
get_db(db_name).instance.open_database(
|
53
|
+
get_db(db_name).instance.open_database(db_location,readonly)
|
55
54
|
end
|
56
55
|
|
57
|
-
When /^I open (\w+)( for reading)?( in (\
|
56
|
+
When /^I open (?:the )?(\w+)( for reading)?(?: in (\S+))?$/ do |db_name,reading,location|
|
58
57
|
if location
|
59
|
-
db_location =
|
58
|
+
db_location = location
|
60
59
|
else
|
61
|
-
db_location = db_name
|
60
|
+
db_location = "tmp/#{db_name}"
|
62
61
|
end
|
63
62
|
get_db(db_name).instance.clear_cache
|
64
63
|
readonly = reading.nil? ? false : true
|
65
|
-
get_db(db_name).instance.open_database(
|
64
|
+
get_db(db_name).instance.open_database(db_location,readonly)
|
66
65
|
end
|
67
66
|
|
68
67
|
Then /^database should be opened for reading$/ do
|
data/lib/rod.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'inline'
|
2
2
|
require 'english/inflect'
|
3
|
-
require '
|
3
|
+
require 'active_support/deprecation'
|
4
4
|
require 'active_model/validator'
|
5
5
|
require 'active_model/naming'
|
6
6
|
require 'active_model/translation'
|
@@ -31,3 +31,13 @@ require 'rod/index/base'
|
|
31
31
|
require 'rod/index/flat_index'
|
32
32
|
require 'rod/index/hash_index'
|
33
33
|
require 'rod/index/segmented_index'
|
34
|
+
require 'rod/property/base'
|
35
|
+
require 'rod/property/field'
|
36
|
+
require 'rod/property/singular_association'
|
37
|
+
require 'rod/property/plural_association'
|
38
|
+
|
39
|
+
require 'rod/berkeley/collection_proxy'
|
40
|
+
require 'rod/berkeley/environment'
|
41
|
+
require 'rod/berkeley/database'
|
42
|
+
require 'rod/berkeley/transaction'
|
43
|
+
require 'rod/berkeley/sequence'
|
@@ -66,31 +66,47 @@ module Rod
|
|
66
66
|
#
|
67
67
|
# WARNING: all files in the DB directory are removed during DB creation!
|
68
68
|
def create_database(path)
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
69
|
+
|
70
|
+
if block_given?
|
71
|
+
|
72
|
+
create_database(path)
|
73
|
+
|
74
|
+
begin
|
75
|
+
yield
|
76
|
+
ensure
|
77
|
+
close_database
|
78
|
+
end
|
79
|
+
|
74
80
|
else
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
81
|
+
|
82
|
+
raise DatabaseError.new("Database already opened.") if opened?
|
83
|
+
@readonly = false
|
84
|
+
@path = canonicalize_path(path)
|
85
|
+
if File.exist?(@path)
|
86
|
+
remove_file("#{@path}database.yml")
|
87
|
+
else
|
88
|
+
FileUtils.mkdir_p(@path)
|
82
89
|
end
|
83
|
-
|
84
|
-
|
90
|
+
self.classes.each do |klass|
|
91
|
+
klass.send(:build_structure)
|
92
|
+
remove_file(klass.path_for_data(@path))
|
93
|
+
klass.indexed_properties.each do |property|
|
94
|
+
property.index.destroy
|
95
|
+
end
|
96
|
+
next if special_class?(klass)
|
97
|
+
remove_files_but(klass.inline_library)
|
98
|
+
end
|
99
|
+
remove_files(self.inline_library)
|
100
|
+
generate_c_code(@path, classes)
|
101
|
+
remove_files_but(self.inline_library)
|
102
|
+
@metadata = {}
|
103
|
+
@metadata["Rod"] = {}
|
104
|
+
@metadata["Rod"][:created_at] = Time.now
|
105
|
+
@handler = _init_handler(@path)
|
106
|
+
_create(@handler)
|
107
|
+
|
85
108
|
end
|
86
|
-
|
87
|
-
generate_c_code(@path, classes)
|
88
|
-
remove_files_but(self.inline_library)
|
89
|
-
@metadata = {}
|
90
|
-
@metadata["Rod"] = {}
|
91
|
-
@metadata["Rod"][:created_at] = Time.now
|
92
|
-
@handler = _init_handler(@path)
|
93
|
-
_create(@handler)
|
109
|
+
|
94
110
|
end
|
95
111
|
|
96
112
|
# Opens the database at +path+ with +options+. This allows
|
@@ -223,8 +239,8 @@ module Rod
|
|
223
239
|
end
|
224
240
|
unless skip_indices
|
225
241
|
self.classes.each do |klass|
|
226
|
-
klass.indexed_properties.each do |property
|
227
|
-
|
242
|
+
klass.indexed_properties.each do |property|
|
243
|
+
property.index.save
|
228
244
|
end
|
229
245
|
end
|
230
246
|
end
|
@@ -325,26 +341,20 @@ module Rod
|
|
325
341
|
_allocate_join_elements(size,@handler)
|
326
342
|
end
|
327
343
|
|
344
|
+
# Computes fast intersection for sorted join elements.
|
345
|
+
def fast_intersection_size(first_offset,first_length,second_offset,second_length)
|
346
|
+
_fast_intersection_size(first_offset,first_length,second_offset,
|
347
|
+
second_length,@handler)
|
348
|
+
end
|
349
|
+
|
328
350
|
# Returns the string of given +length+ starting at given +offset+.
|
329
|
-
|
330
|
-
# * +:skip_encoding+ - if set to +true+, the string is left as ASCII-8BIT
|
331
|
-
def read_string(length, offset,options={})
|
332
|
-
# TODO the encoding should be stored in the DB
|
333
|
-
# or configured globally
|
351
|
+
def read_string(length, offset)
|
334
352
|
value = _read_string(length, offset, @handler)
|
335
|
-
if options[:skip_encoding]
|
336
|
-
value.force_encoding("ascii-8bit")
|
337
|
-
else
|
338
|
-
value.force_encoding("utf-8")
|
339
|
-
end
|
340
353
|
end
|
341
354
|
|
342
|
-
# Stores the string in the DB
|
343
|
-
|
344
|
-
# * +:skip_encoding+ - if set to +true+, the string is not encoded
|
345
|
-
def set_string(value,options={})
|
355
|
+
# Stores the string in the DB.
|
356
|
+
def set_string(value)
|
346
357
|
raise DatabaseError.new("Readonly database.") if readonly_data?
|
347
|
-
value = value.encode("utf-8") unless options[:skip_encoding]
|
348
358
|
_set_string(value,@handler)
|
349
359
|
end
|
350
360
|
|
@@ -476,9 +486,9 @@ module Rod
|
|
476
486
|
[:fields,:has_one,:has_many].each do |property_type|
|
477
487
|
next if metadata[property_type].nil?
|
478
488
|
metadata[property_type].each do |property,options|
|
479
|
-
if superclasses.keys.include?(options[:
|
480
|
-
metadata[property_type][property][:
|
481
|
-
prefix + options[:
|
489
|
+
if superclasses.keys.include?(options[:class_name])
|
490
|
+
metadata[property_type][property][:class_name] =
|
491
|
+
prefix + options[:class_name]
|
482
492
|
end
|
483
493
|
end
|
484
494
|
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Rod
|
2
|
+
module Berkeley
|
3
|
+
class CollectionProxy
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
# Initializes the proxy with given Berkeley +database+.
|
7
|
+
def initialize(database,key)
|
8
|
+
@database = database
|
9
|
+
@key = key
|
10
|
+
end
|
11
|
+
|
12
|
+
def [](object_index)
|
13
|
+
if object_index == 0
|
14
|
+
begin
|
15
|
+
return object_for(@database.get_first(@key))
|
16
|
+
rescue KeyMissing => ex
|
17
|
+
return nil
|
18
|
+
end
|
19
|
+
else
|
20
|
+
# TODO This should be optimized!
|
21
|
+
self.each.with_index do |object,index|
|
22
|
+
return object if index == object_index
|
23
|
+
end
|
24
|
+
end
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def |(other)
|
29
|
+
raise "#{self.class}##{__method__} not implemented yet."
|
30
|
+
end
|
31
|
+
|
32
|
+
def &(other)
|
33
|
+
raise "#{self.class}##{__method__} not implemented yet."
|
34
|
+
end
|
35
|
+
|
36
|
+
def <<(element)
|
37
|
+
# TODO #207 this doesn't work for not persited objects
|
38
|
+
rod_id = element.rod_id
|
39
|
+
raise "Not implemented #207" if rod_id.nil? || rod_id == 0
|
40
|
+
@database.put(@key,rod_id)
|
41
|
+
end
|
42
|
+
|
43
|
+
def insert(index,element)
|
44
|
+
raise "#{self.class}##{__method__} not implemented yet."
|
45
|
+
end
|
46
|
+
|
47
|
+
def delete(element)
|
48
|
+
begin
|
49
|
+
@database.delete(@key,element)
|
50
|
+
element
|
51
|
+
rescue KeyMissing => ex
|
52
|
+
nil
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def delete_at(index)
|
57
|
+
raise "#{self.class}##{__method__} not implemented yet."
|
58
|
+
end
|
59
|
+
|
60
|
+
def clear
|
61
|
+
raise "#{self.class}##{__method__} not implemented yet."
|
62
|
+
end
|
63
|
+
|
64
|
+
def each
|
65
|
+
if block_given?
|
66
|
+
begin
|
67
|
+
@database.each_for(@key) do |rod_id|
|
68
|
+
yield object_for(rod_id)
|
69
|
+
end
|
70
|
+
rescue KeyMissing
|
71
|
+
nil
|
72
|
+
end
|
73
|
+
else
|
74
|
+
enum_for(:each)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def to_s
|
79
|
+
raise "#{self.class}##{__method__} not implemented yet."
|
80
|
+
end
|
81
|
+
|
82
|
+
def empty?
|
83
|
+
raise "#{self.class}##{__method__} not implemented yet."
|
84
|
+
end
|
85
|
+
|
86
|
+
def save
|
87
|
+
raise "#{self.class}##{__method__} not implemented yet."
|
88
|
+
end
|
89
|
+
|
90
|
+
protected
|
91
|
+
def object_for(rod_id)
|
92
|
+
@database.klass.find_by_rod_id(rod_id)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,337 @@
|
|
1
|
+
module Rod
|
2
|
+
module Berkeley
|
3
|
+
class Database
|
4
|
+
# The environment of the database.
|
5
|
+
attr_reader :environment
|
6
|
+
|
7
|
+
# The path of the database.
|
8
|
+
attr_reader :path
|
9
|
+
|
10
|
+
# Initializes the database as working within the given
|
11
|
+
# +environment+.
|
12
|
+
def initialize(environment)
|
13
|
+
# TODO allow standalone databases
|
14
|
+
@environment = environment
|
15
|
+
@opened = false
|
16
|
+
end
|
17
|
+
|
18
|
+
# Opens the Berkeley DB database at given +path+ with given +access_method+.
|
19
|
+
#
|
20
|
+
# The followin access methods are supported (see the Berkeley DB documentation
|
21
|
+
# for full description of the access methods):
|
22
|
+
# * +:btree+ - sorted, balanced tree
|
23
|
+
# * +:hash+ - hash table
|
24
|
+
# * +:queue+ - queue
|
25
|
+
# * +:recno+ - recno
|
26
|
+
# * +:heap+ - heap NOT SUPPORTED!
|
27
|
+
#
|
28
|
+
# The following options are supported (see the Berkeley DB documentation
|
29
|
+
# for full description of the flags):
|
30
|
+
# * +:auto_commit: - automaticaly commit each database change,
|
31
|
+
# BDB: +DB_AUTO_COMMIT+
|
32
|
+
# * +:create: - create the database if it doesn't exist, BDB: +DB_CREATE+
|
33
|
+
# * +:create_exclusive: - check if the database exists when creating it,
|
34
|
+
# if it exists - raise an error, BDB: +DB_EXCL+
|
35
|
+
# * +:multiversion: - use multiversion concurrency control to perform transaction
|
36
|
+
# snapshot isolation, BDB: +DB_MULTIVERSION+
|
37
|
+
# * +:no_mmap: - don't map this database to the process memory, BDB: +DB_NOMMAP+
|
38
|
+
# * +:readonly: - work in readonly mode - all database changes will fail,
|
39
|
+
# BDB: +DB_RDONLY+
|
40
|
+
# * +:read_uncommitted: - perform transaction degree 1 isolation,
|
41
|
+
# BDB: +DB_READ_UNCOMMITTED+
|
42
|
+
# * +:threads+ allow multiple threads within one process to access the databases,
|
43
|
+
# BDB: +DB_THREAD+
|
44
|
+
# * +:truncate: - empty the database on creation, BDB: +DB_TRUNCATE+
|
45
|
+
def open(path,access_method,options={})
|
46
|
+
# TODO check for validity of the method
|
47
|
+
# TODO check for validity of options
|
48
|
+
# TODO check for conflicting options
|
49
|
+
raise DatabaseError.new("The database is already opened at #{@path}.") if opened?
|
50
|
+
_open(path,access_method,options)
|
51
|
+
@path = path
|
52
|
+
@opened = true
|
53
|
+
end
|
54
|
+
|
55
|
+
# Closes the database.
|
56
|
+
def close
|
57
|
+
_close
|
58
|
+
@opened = false
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns true if the database is opened.
|
62
|
+
def opened?
|
63
|
+
@opened
|
64
|
+
end
|
65
|
+
|
66
|
+
# Put the +value+ to the database at the specified +key+.
|
67
|
+
# The operation might be protected by the +transaction+.
|
68
|
+
# Both the value and the key are marshaled before being stored.
|
69
|
+
def put(key,value,transaction=nil)
|
70
|
+
marshaled_key = Marshal.dump(key)
|
71
|
+
marshaled_value = Marshal.dump(value)
|
72
|
+
_put_strings(marshaled_key,marshaled_value,transaction)
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
# Return the value of the database for the specified +key+.
|
77
|
+
# The operation might be protected by the +transaction+.
|
78
|
+
# The key is marshaled before being stored looked up in the database.
|
79
|
+
def get(key,transaction=nil)
|
80
|
+
marshaled_key = Marshal.dump(key)
|
81
|
+
Marshal.load(_get_strings(marshaled_key,transaction))
|
82
|
+
end
|
83
|
+
|
84
|
+
class << self
|
85
|
+
# The C definition of the KeyMissing exception.
|
86
|
+
def key_missing_exception
|
87
|
+
str =<<-END
|
88
|
+
|VALUE keyMissingException(){
|
89
|
+
| VALUE klass;
|
90
|
+
|
|
91
|
+
| klass = rb_const_get(rb_cObject, rb_intern("Rod"));
|
92
|
+
| klass = rb_const_get(klass, rb_intern("KeyMissing"));
|
93
|
+
| return klass;
|
94
|
+
|}
|
95
|
+
END
|
96
|
+
str.margin
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
inline(:C) do |builder|
|
101
|
+
Rod::Berkeley::Environment.init_builder(builder)
|
102
|
+
builder.prefix(Rod::Berkeley::Environment.database_error)
|
103
|
+
builder.prefix(self.key_missing_exception)
|
104
|
+
|
105
|
+
str =<<-END
|
106
|
+
|/*
|
107
|
+
|* Closes the database causing the resources to be freed.
|
108
|
+
|*/
|
109
|
+
|void db_free(DB * db_pointer){
|
110
|
+
| int return_value;
|
111
|
+
|
|
112
|
+
| if(db_pointer != NULL){
|
113
|
+
| return_value = db_pointer->close(db_pointer,0);
|
114
|
+
| if(return_value != 0){
|
115
|
+
| rb_raise(databaseError(),"%s",db_strerror(return_value));
|
116
|
+
| }
|
117
|
+
| }
|
118
|
+
|}
|
119
|
+
END
|
120
|
+
builder.prefix(str.margin)
|
121
|
+
|
122
|
+
str =<<-END
|
123
|
+
|/*
|
124
|
+
|* Replaces default allocate with function returning wrapper for the
|
125
|
+
|* database struct.
|
126
|
+
|*/
|
127
|
+
|VALUE allocate(){
|
128
|
+
| // db_mark == NULL - no internal elements have to be marked
|
129
|
+
| // struct == NULL - there is no struct to wrap at the moment
|
130
|
+
| return Data_Wrap_Struct(self,NULL,db_free,NULL);
|
131
|
+
|}
|
132
|
+
END
|
133
|
+
builder.c_singleton(str.margin)
|
134
|
+
|
135
|
+
str =<<-END
|
136
|
+
|/*
|
137
|
+
|* Opens the database on the +path+ given with the access +method+ specified.
|
138
|
+
|* See +open+ for a list of options.
|
139
|
+
|*/
|
140
|
+
|void _open(const char * path, VALUE method, VALUE options){
|
141
|
+
| DB_ENV * env_pointer;
|
142
|
+
| DB * db_pointer;
|
143
|
+
| DBTYPE access_method;
|
144
|
+
| u_int32_t flags;
|
145
|
+
| int return_value;
|
146
|
+
| VALUE environment;
|
147
|
+
|
|
148
|
+
| environment = rb_iv_get(self,"@environment");
|
149
|
+
| if(NIL_P(environment)){
|
150
|
+
| rb_raise(databaseError(),"The environment of the database at %s is nil!",path);
|
151
|
+
| }
|
152
|
+
| Data_Get_Struct(environment,DB_ENV,env_pointer);
|
153
|
+
| if(env_pointer == NULL){
|
154
|
+
| rb_raise(databaseError(),"The environment of the database at %s is NULL!",path);
|
155
|
+
| }
|
156
|
+
| // the flags could be DB_XA_CREATE, but we don't support it so far.
|
157
|
+
| return_value = db_create(&db_pointer, env_pointer, 0);
|
158
|
+
| if(return_value != 0){
|
159
|
+
| rb_raise(databaseError(),"%s",db_strerror(return_value));
|
160
|
+
| }
|
161
|
+
|
|
162
|
+
| flags = 0;
|
163
|
+
| if(rb_hash_aref(options,ID2SYM(rb_intern("auto_commit"))) == Qtrue){
|
164
|
+
| flags |= DB_AUTO_COMMIT;
|
165
|
+
| }
|
166
|
+
| if(rb_hash_aref(options,ID2SYM(rb_intern("create"))) == Qtrue){
|
167
|
+
| flags |= DB_CREATE;
|
168
|
+
| }
|
169
|
+
| if(rb_hash_aref(options,ID2SYM(rb_intern("create_exclusive"))) == Qtrue){
|
170
|
+
| flags |= DB_EXCL;
|
171
|
+
| }
|
172
|
+
| if(rb_hash_aref(options,ID2SYM(rb_intern("multiversion"))) == Qtrue){
|
173
|
+
| flags |= DB_MULTIVERSION;
|
174
|
+
| }
|
175
|
+
| if(rb_hash_aref(options,ID2SYM(rb_intern("no_mmap"))) == Qtrue){
|
176
|
+
| flags |= DB_NOMMAP;
|
177
|
+
| }
|
178
|
+
| if(rb_hash_aref(options,ID2SYM(rb_intern("readonly"))) == Qtrue){
|
179
|
+
| flags |= DB_RDONLY;
|
180
|
+
| }
|
181
|
+
| if(rb_hash_aref(options,ID2SYM(rb_intern("read_uncommitted"))) == Qtrue){
|
182
|
+
| flags |= DB_READ_UNCOMMITTED;
|
183
|
+
| }
|
184
|
+
| if(rb_hash_aref(options,ID2SYM(rb_intern("threads"))) == Qtrue){
|
185
|
+
| flags |= DB_THREAD;
|
186
|
+
| }
|
187
|
+
| if(rb_hash_aref(options,ID2SYM(rb_intern("truncate"))) == Qtrue){
|
188
|
+
| flags |= DB_TRUNCATE;
|
189
|
+
| }
|
190
|
+
|
|
191
|
+
| // access method
|
192
|
+
| if(ID2SYM(rb_intern("btree")) == method){
|
193
|
+
| method = DB_BTREE;
|
194
|
+
| } else if(ID2SYM(rb_intern("hash")) == method){
|
195
|
+
| method = DB_BTREE;
|
196
|
+
| } else if(ID2SYM(rb_intern("heap")) == method){
|
197
|
+
| // DB_HEAP no supported in the library
|
198
|
+
| // method = DB_HEAP;
|
199
|
+
| } else if(ID2SYM(rb_intern("queue")) == method){
|
200
|
+
| method = DB_QUEUE;
|
201
|
+
| } else if(ID2SYM(rb_intern("recno")) == method){
|
202
|
+
| method = DB_RECNO;
|
203
|
+
| } else {
|
204
|
+
| // only for existing databases
|
205
|
+
| method = DB_UNKNOWN;
|
206
|
+
| }
|
207
|
+
| // 1 NULL - transaction pointer
|
208
|
+
| // 2 NULL - logical database name (for many dbs in one file)
|
209
|
+
| // 0 - default file access mode
|
210
|
+
| return_value = db_pointer->open(db_pointer,NULL,path,NULL,method,flags,0);
|
211
|
+
| if(return_value != 0){
|
212
|
+
| rb_raise(databaseError(),"%s",db_strerror(return_value));
|
213
|
+
| }
|
214
|
+
| DATA_PTR(self) = db_pointer;
|
215
|
+
|}
|
216
|
+
END
|
217
|
+
builder.c(str.margin)
|
218
|
+
|
219
|
+
str =<<-END
|
220
|
+
|/*
|
221
|
+
|* Closes the database if it is opened.
|
222
|
+
|*/
|
223
|
+
|void _close(){
|
224
|
+
| DB * db_pointer;
|
225
|
+
| int return_value;
|
226
|
+
|
|
227
|
+
| Data_Get_Struct(self,DB,db_pointer);
|
228
|
+
| db_free(db_pointer);
|
229
|
+
| DATA_PTR(self) = NULL;
|
230
|
+
|}
|
231
|
+
END
|
232
|
+
builder.c(str.margin)
|
233
|
+
|
234
|
+
str =<<-END
|
235
|
+
|void _put(VALUE self,void * key,unsigned int key_size, void * value,
|
236
|
+
| unsigned int value_size, VALUE transaction){
|
237
|
+
| DB *db_pointer;
|
238
|
+
| DB_TXN *txn_pointer;
|
239
|
+
| DBT db_key, db_value;
|
240
|
+
| int return_value;
|
241
|
+
|
|
242
|
+
| Data_Get_Struct(self,DB,db_pointer);
|
243
|
+
|
|
244
|
+
| memset(&db_key, 0, sizeof(DBT));
|
245
|
+
| db_key.data = key;
|
246
|
+
| db_key.size = key_size;
|
247
|
+
|
|
248
|
+
| memset(&db_value, 0, sizeof(DBT));
|
249
|
+
| db_value.data = value;
|
250
|
+
| db_value.size = value_size;
|
251
|
+
|
|
252
|
+
| if(db_pointer == NULL){
|
253
|
+
| rb_raise(databaseError(),"The handle for the database is NULL!");
|
254
|
+
| }
|
255
|
+
| // TODO options
|
256
|
+
| if(NIL_P(transaction)){
|
257
|
+
| return_value = db_pointer->put(db_pointer, NULL, &db_key, &db_value, 0);
|
258
|
+
| } else {
|
259
|
+
| Data_Get_Struct(transaction,DB_TXN,txn_pointer);
|
260
|
+
| return_value = db_pointer->put(db_pointer, txn_pointer, &db_key, &db_value, 0);
|
261
|
+
| }
|
262
|
+
| if(return_value != 0){
|
263
|
+
| rb_raise(databaseError(),"%s",db_strerror(return_value));
|
264
|
+
| }
|
265
|
+
|}
|
266
|
+
END
|
267
|
+
builder.prefix(str.margin)
|
268
|
+
|
269
|
+
str =<<-END
|
270
|
+
|/*
|
271
|
+
|* Put the string key-value pair to the database.
|
272
|
+
|*/
|
273
|
+
|void _put_strings(VALUE key, VALUE value, VALUE transaction){
|
274
|
+
| _put(self,RSTRING_PTR(key),RSTRING_LEN(key),RSTRING_PTR(value),
|
275
|
+
| RSTRING_LEN(value),transaction);
|
276
|
+
|}
|
277
|
+
END
|
278
|
+
builder.c(str.margin)
|
279
|
+
|
280
|
+
str =<<-END
|
281
|
+
|unsigned int _get(VALUE self, void * key, unsigned int key_size,
|
282
|
+
| void * value, unsigned int value_size, VALUE transaction){
|
283
|
+
| DB *db_pointer;
|
284
|
+
| DB_TXN *txn_pointer;
|
285
|
+
| DBT db_key, db_value;
|
286
|
+
| int return_value;
|
287
|
+
|
|
288
|
+
| Data_Get_Struct(self,DB,db_pointer);
|
289
|
+
|
|
290
|
+
| if(db_pointer == NULL){
|
291
|
+
| rb_raise(databaseError(),"The handle for the database is NULL!");
|
292
|
+
| }
|
293
|
+
|
|
294
|
+
| memset(&db_value, 0, sizeof(DBT));
|
295
|
+
| db_value.data = value;
|
296
|
+
| db_value.ulen = value_size;
|
297
|
+
| db_value.flags = DB_DBT_USERMEM;
|
298
|
+
|
|
299
|
+
| memset(&db_key, 0, sizeof(DBT));
|
300
|
+
| db_key.data = key;
|
301
|
+
| db_key.size = key_size;
|
302
|
+
|
|
303
|
+
| if(NIL_P(transaction)){
|
304
|
+
| return_value = db_pointer->get(db_pointer, NULL, &db_key, &db_value, 0);
|
305
|
+
| } else {
|
306
|
+
| Data_Get_Struct(transaction,DB_TXN,txn_pointer);
|
307
|
+
| return_value = db_pointer->get(db_pointer, txn_pointer, &db_key, &db_value, 0);
|
308
|
+
| }
|
309
|
+
| if(return_value == DB_NOTFOUND){
|
310
|
+
| rb_raise(keyMissingException(),"%s",db_strerror(return_value));
|
311
|
+
| } else if(return_value != 0){
|
312
|
+
| rb_raise(databaseError(),"%s",db_strerror(return_value));
|
313
|
+
| }
|
314
|
+
| return db_value.size;
|
315
|
+
|}
|
316
|
+
END
|
317
|
+
builder.prefix(str.margin)
|
318
|
+
|
319
|
+
str =<<-END
|
320
|
+
|/*
|
321
|
+
|* Get the value for given +key+. The key and the value
|
322
|
+
|* are supposed to be strings. The result size is limited to 1024 bytes.
|
323
|
+
|*/
|
324
|
+
|VALUE _get_strings(VALUE key, VALUE transaction){
|
325
|
+
| char buffer[1024];
|
326
|
+
| VALUE result;
|
327
|
+
| unsigned int size;
|
328
|
+
|
|
329
|
+
| size = _get(self,RSTRING_PTR(key),RSTRING_LEN(key),buffer,1024,transaction);
|
330
|
+
| return rb_str_new(buffer,size);
|
331
|
+
|}
|
332
|
+
END
|
333
|
+
builder.c(str.margin)
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|