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.
Files changed (76) hide show
  1. data/.travis.yml +1 -1
  2. data/README.rdoc +38 -10
  3. data/Rakefile +20 -9
  4. data/changelog.txt +25 -0
  5. data/contributors.txt +1 -0
  6. data/data/backward/0.7.0/_join_element.dat +0 -0
  7. data/data/backward/0.7.0/_polymorphic_join_element.dat +0 -0
  8. data/data/backward/0.7.0/char.dat +0 -0
  9. data/data/backward/0.7.0/database.yml +58 -0
  10. data/data/backward/0.7.0/rod_test__automobile.dat +0 -0
  11. data/data/backward/0.7.0/rod_test__caveman.dat +0 -0
  12. data/data/backward/0.7.0/rod_test__dog.dat +0 -0
  13. data/data/backward/0.7.0/rod_test__test_model.dat +0 -0
  14. data/data/portability/_join_element.dat +0 -0
  15. data/data/portability/_polymorphic_join_element.dat +0 -0
  16. data/data/portability/char.dat +0 -0
  17. data/data/portability/database.yml +49 -0
  18. data/data/portability/rod_test__automobile.dat +0 -0
  19. data/data/portability/rod_test__caveman.dat +0 -0
  20. data/data/portability/rod_test__dog.dat +0 -0
  21. data/data/portability/rod_test__test_model.dat +0 -0
  22. data/features/backward.feature +33 -0
  23. data/features/basic.feature +3 -0
  24. data/features/collection_proxy.feature +95 -0
  25. data/features/flat_indexing.feature +44 -2
  26. data/features/hash_indexing.feature +63 -9
  27. data/features/portability.feature +72 -0
  28. data/features/segmented_indexing.feature +45 -2
  29. data/features/steps/collection_proxy.rb +1 -1
  30. data/features/steps/model.rb +48 -5
  31. data/features/steps/rod.rb +15 -16
  32. data/lib/rod.rb +11 -1
  33. data/lib/rod/abstract_database.rb +52 -42
  34. data/lib/rod/berkeley/collection_proxy.rb +96 -0
  35. data/lib/rod/berkeley/database.rb +337 -0
  36. data/lib/rod/berkeley/environment.rb +209 -0
  37. data/lib/rod/berkeley/sequence.rb +222 -0
  38. data/lib/rod/berkeley/transaction.rb +233 -0
  39. data/lib/rod/collection_proxy.rb +76 -1
  40. data/lib/rod/constants.rb +3 -2
  41. data/lib/rod/database.rb +127 -14
  42. data/lib/rod/index/base.rb +12 -3
  43. data/lib/rod/index/hash_index.rb +295 -70
  44. data/lib/rod/index/segmented_index.rb +3 -0
  45. data/lib/rod/model.rb +154 -531
  46. data/lib/rod/property/base.rb +190 -0
  47. data/lib/rod/property/field.rb +258 -0
  48. data/lib/rod/property/plural_association.rb +145 -0
  49. data/lib/rod/property/singular_association.rb +139 -0
  50. data/rod.gemspec +6 -4
  51. data/spec/berkeley/database.rb +83 -0
  52. data/spec/berkeley/environment.rb +58 -0
  53. data/spec/berkeley/sequence.rb +101 -0
  54. data/spec/berkeley/transaction.rb +92 -0
  55. data/spec/collection_proxy.rb +38 -0
  56. data/spec/database.rb +36 -0
  57. data/spec/model.rb +26 -0
  58. data/spec/property/base.rb +73 -0
  59. data/spec/property/field.rb +244 -0
  60. data/spec/property/plural_association.rb +67 -0
  61. data/spec/property/singular_association.rb +65 -0
  62. data/tests/class_compatibility_create.rb +2 -2
  63. data/tests/eff1_test.rb +1 -1
  64. data/tests/eff2_test.rb +1 -1
  65. data/tests/full_runs.rb +1 -1
  66. data/tests/generate_classes_create.rb +14 -14
  67. data/tests/migration_create.rb +47 -47
  68. data/tests/migration_verify.rb +1 -1
  69. data/tests/missing_class_create.rb +6 -6
  70. data/tests/properties_order_create.rb +4 -4
  71. data/tests/read_on_create.rb +33 -34
  72. data/tests/save_struct.rb +40 -39
  73. data/tests/unit/database.rb +1 -1
  74. data/tests/unit/model_tests.rb +73 -65
  75. metadata +71 -15
  76. data/tests/unit/model.rb +0 -36
@@ -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 (\w+))?$/ do |ignore,db_name,location,location_name|
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 File.exist?("tmp")
12
- if location
13
- db_location = location_name
14
- else
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("tmp/#{db_location}")
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
- When /^I reopen (?:the )?(\w+)( for reading)?( in (\w+))?$/ do |db_name,reading,location,location_name|
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 = location_name
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("tmp/#{db_location}",readonly)
53
+ get_db(db_name).instance.open_database(db_location,readonly)
55
54
  end
56
55
 
57
- When /^I open (\w+)( for reading)?( in (\w+))?$/ do |db_name,reading,location,location_name|
56
+ When /^I open (?:the )?(\w+)( for reading)?(?: in (\S+))?$/ do |db_name,reading,location|
58
57
  if location
59
- db_location = location_name
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("tmp/#{db_location}",readonly)
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 'active_model/deprecated_error_methods'
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
- raise DatabaseError.new("Database already opened.") if opened?
70
- @readonly = false
71
- @path = canonicalize_path(path)
72
- if File.exist?(@path)
73
- remove_file("#{@path}database.yml")
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
- FileUtils.mkdir_p(@path)
76
- end
77
- self.classes.each do |klass|
78
- klass.send(:build_structure)
79
- remove_file(klass.path_for_data(@path))
80
- klass.indexed_properties.each do |property,options|
81
- klass.index_for(property,options).destroy
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
- next if special_class?(klass)
84
- remove_files_but(klass.inline_library)
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
- remove_files(self.inline_library)
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,options|
227
- klass.index_for(property,options).save
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
- # Options:
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 encoding it to utf-8.
343
- # Options:
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[:options][:class_name])
480
- metadata[property_type][property][:options][:class_name] =
481
- prefix + options[:options][:class_name]
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