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
@@ -0,0 +1,209 @@
1
+ require 'inline'
2
+ require 'fileutils'
3
+
4
+ module Rod
5
+ module Berkeley
6
+ class Environment
7
+ # Initialization of the environment.
8
+ def initialize
9
+ @opened = false
10
+ end
11
+
12
+ # Opens the Berkeley DB environment at given +path+.
13
+ # The following options are supported (see the Berkeley DB documentation
14
+ # for full description of the flags):
15
+ # * +:create+ - creates the environment if it not exist, DBD: , BDB: +DB_CREATE+
16
+ # * +:transactions+ - initializes the transaction subsystem, BDB: +DB_INIT_TXN+
17
+ # * +:locking+ - initializes the locking subsystem, BDB: +DB_INIT_LOCK+
18
+ # * +:logging+ - initializes the logging subsystem, BDB: +DB_INIT_LOG+
19
+ # * +:cache+ - initializes the environment's cache, BDB: +DB_INIT_MPOOL+
20
+ # * +:data_store+ - initializes the concurrent data store, BDB: +DB_INIT_CDB+
21
+ # * +:replication+ - initializes the replication subsystem, BDB: +DB_INIT_REP+
22
+ # * +:recovery+ - runs normal recovery on the environment, BDB: +DB_RECOVER+
23
+ # * +:fatal_recovery+ - runs fatal recovery on the environment, BDB: +DB_RECOVER_FATAL+
24
+ # * +:lock_in_memory+ - forces the environemnt's shared data to be locked in the memory,
25
+ # BDB: +DB_LOCKDOWN+
26
+ # * +:fail_check+ - checks for threads that exited leaving invalid locks or
27
+ # transactions, BDB: +DB_FAILCHK+
28
+ # * +:recovery_check+ - checks if recovery has to be performed beform opening
29
+ # the environment, BDB: +DB_REGISTER+
30
+ # * +:private+ - the environemnt's data might be shared only witin one process,
31
+ # so concurrency is allowed only between threads, BDB: +DB_PRIVATE+
32
+ # * +:system_memory+ - use system shared memory for environment's shared data,
33
+ # BDB: +DB_SYSTEM_MEM+
34
+ # * +:threads+ allow multiple threads within one process to access the
35
+ # environment and/or its databases, BDB: +DB_THREAD+
36
+ def open(path,options={})
37
+ raise DatabaseError.new("The environment at #{path} is already opened.") if opened?
38
+ FileUtils.mkdir_p(path)
39
+ # TODO check for conflicting options
40
+ # TODO check for validity of options
41
+ _open(path,options)
42
+ @opened = true
43
+ end
44
+
45
+ # Closes the environment.
46
+ def close
47
+ _close
48
+ @opened = false
49
+ end
50
+
51
+ # Returns true if the environment is opened.
52
+ def opened?
53
+ @opened
54
+ end
55
+
56
+ class << self
57
+ # You can set arbitrary Berkeley DB link flags via
58
+ # ROD_BDB_LINK_FLAGS env. variable.
59
+ def rod_link_flags
60
+ ENV['ROD_BDB_LINK_FLAGS'] || '-ldb'
61
+ end
62
+
63
+ # Calls methods on the C +builder+ needed to properly configure the
64
+ # C compiler for Berkeley DB.
65
+ def init_builder(builder)
66
+ builder.include '<db.h>'
67
+ builder.include '<stdio.h>'
68
+ builder.add_link_flags self.rod_link_flags
69
+ end
70
+
71
+ # C definition of the DatabaseError.
72
+ def database_error
73
+ str =<<-END
74
+ |VALUE databaseError(){
75
+ | VALUE klass;
76
+ |
77
+ | klass = rb_const_get(rb_cObject, rb_intern("Rod"));
78
+ | klass = rb_const_get(klass, rb_intern("DatabaseError"));
79
+ | return klass;
80
+ |}
81
+ END
82
+ str.margin
83
+ end
84
+ end
85
+
86
+ inline(:C) do |builder|
87
+ init_builder(builder)
88
+ builder.prefix(self.database_error)
89
+
90
+ str =<<-END
91
+ |/*
92
+ |* Closes the environemt causing the resources to be freed.
93
+ |*/
94
+ |void env_free(DB_ENV * env_pointer){
95
+ | int return_value;
96
+ |
97
+ | if(env_pointer != NULL){
98
+ | return_value = env_pointer->close(env_pointer,0);
99
+ | if(return_value != 0){
100
+ | rb_raise(databaseError(),"%s",db_strerror(return_value));
101
+ | }
102
+ | }
103
+ |}
104
+ END
105
+ builder.prefix(str.margin)
106
+
107
+ str =<<-END
108
+ |/*
109
+ |* Replaces default allocate with function returning wrapper for the
110
+ |* environment struct.
111
+ |*/
112
+ |VALUE allocate(){
113
+ | // db_mark == NULL - no internal elements have to be marked
114
+ | // struct == NULL - there is no struct to wrap at the moment
115
+ | return Data_Wrap_Struct(self,NULL,env_free,NULL);
116
+ |}
117
+ END
118
+ builder.c_singleton(str.margin)
119
+
120
+ str =<<-END
121
+ |/*
122
+ |* Opens the database environemnt on the path given.
123
+ |* See +open+ for a list of options.
124
+ |*/
125
+ |void _open(const char * path, VALUE options){
126
+ | DB_ENV * env_pointer;
127
+ | u_int32_t flags;
128
+ | int return_value;
129
+ |
130
+ | // the flags has to be set to 0 - cf. db_env_create in documentation
131
+ | return_value = db_env_create(&env_pointer, 0);
132
+ | if(return_value != 0){
133
+ | rb_raise(databaseError(),"%s",db_strerror(return_value));
134
+ | }
135
+ | flags = 0;
136
+ | if(rb_hash_aref(options,ID2SYM(rb_intern("create"))) == Qtrue){
137
+ | flags |= DB_CREATE;
138
+ | }
139
+ | if(rb_hash_aref(options,ID2SYM(rb_intern("transactions"))) == Qtrue){
140
+ | flags |= DB_INIT_TXN;
141
+ | }
142
+ | if(rb_hash_aref(options,ID2SYM(rb_intern("locking"))) == Qtrue){
143
+ | flags |= DB_INIT_LOCK;
144
+ | }
145
+ | if(rb_hash_aref(options,ID2SYM(rb_intern("logging"))) == Qtrue){
146
+ | flags |= DB_INIT_LOG;
147
+ | }
148
+ | if(rb_hash_aref(options,ID2SYM(rb_intern("cache"))) == Qtrue){
149
+ | flags |= DB_INIT_MPOOL;
150
+ | }
151
+ | if(rb_hash_aref(options,ID2SYM(rb_intern("data_store"))) == Qtrue){
152
+ | flags |= DB_INIT_CDB;
153
+ | }
154
+ | if(rb_hash_aref(options,ID2SYM(rb_intern("replication"))) == Qtrue){
155
+ | flags |= DB_INIT_REP;
156
+ | }
157
+ | if(rb_hash_aref(options,ID2SYM(rb_intern("recovery"))) == Qtrue){
158
+ | flags |= DB_RECOVER;
159
+ | }
160
+ | if(rb_hash_aref(options,ID2SYM(rb_intern("fatal_recovery"))) == Qtrue){
161
+ | flags |= DB_RECOVER_FATAL;
162
+ | }
163
+ | if(rb_hash_aref(options,ID2SYM(rb_intern("lock_in_memory"))) == Qtrue){
164
+ | flags |= DB_LOCKDOWN;
165
+ | }
166
+ | if(rb_hash_aref(options,ID2SYM(rb_intern("fail_check"))) == Qtrue){
167
+ | flags |= DB_FAILCHK;
168
+ | }
169
+ | if(rb_hash_aref(options,ID2SYM(rb_intern("recovery_check"))) == Qtrue){
170
+ | flags |= DB_REGISTER;
171
+ | }
172
+ | if(rb_hash_aref(options,ID2SYM(rb_intern("private"))) == Qtrue){
173
+ | flags |= DB_PRIVATE;
174
+ | }
175
+ | if(rb_hash_aref(options,ID2SYM(rb_intern("system_memory"))) == Qtrue){
176
+ | flags |= DB_SYSTEM_MEM;
177
+ | }
178
+ | if(rb_hash_aref(options,ID2SYM(rb_intern("threads"))) == Qtrue){
179
+ | flags |= DB_THREAD;
180
+ | }
181
+ |
182
+ | // use the default file access mode (last param)
183
+ | return_value = env_pointer->open(env_pointer,path,flags,0);
184
+ | if(return_value != 0){
185
+ | rb_raise(databaseError(),"%s",db_strerror(return_value));
186
+ | }
187
+ | DATA_PTR(self) = env_pointer;
188
+ |}
189
+ END
190
+ builder.c(str.margin)
191
+
192
+ str =<<-END
193
+ |/*
194
+ |* Closes the environment if it is opened.
195
+ |*/
196
+ |void _close(){
197
+ | DB_ENV * env_pointer;
198
+ | int return_value;
199
+ |
200
+ | Data_Get_Struct(self,DB_ENV,env_pointer);
201
+ | env_free(env_pointer);
202
+ | DATA_PTR(self) = NULL;
203
+ |}
204
+ END
205
+ builder.c(str.margin)
206
+ end
207
+ end
208
+ end
209
+ end
@@ -0,0 +1,222 @@
1
+ module Rod
2
+ module Berkeley
3
+ class Sequence
4
+ # The database of the sequence.
5
+ attr_reader :database
6
+
7
+ # Initializes the sequence as stored within the given
8
+ # +database+.
9
+ def initialize(database)
10
+ @database = database
11
+ @opened = false
12
+ end
13
+
14
+ # Opens the Berkeley DB sequence for the given +key+ with given +transaction+.
15
+ #
16
+ # The following options are supported (see the Berkeley DB documentation
17
+ # for full description of the flags):
18
+ # * +:cache_size - the number of cached values (default to 1)
19
+ # * +:create - create the sequence if it doesn't exist, BDB: +DB_CREATE+
20
+ # * +:create_exclusive - check if the sequence exists when creating it,
21
+ # if it exists - raise an error, BDB: +DB_EXCL+
22
+ # * +:threads+ allow multiple threads within one process to use the sequence,
23
+ # BDB: +DB_THREAD+
24
+ def open(key,transaction=nil,options={})
25
+ if opened?
26
+ raise DatabaseError.new("The seqence associated with the #{@database} is already opened.")
27
+ end
28
+ if String === key
29
+ _open_string(key,transaction,options)
30
+ else
31
+ raise DatabaseError.new("#{key.class} type not supported for sequence keys.")
32
+ end
33
+ end
34
+
35
+ # Closes the sequence.
36
+ def close
37
+ _close
38
+ @opened = false
39
+ end
40
+
41
+ # Returns true if the database is opened.
42
+ def opened?
43
+ @opened
44
+ end
45
+
46
+ # Get the next value of the sequence.
47
+ # The operation might be protected by the +transaction+ given.
48
+ #
49
+ # The following options are supported (see the Berkeley DB documentation
50
+ # for full description of the flags):
51
+ # * +:delta+ - the delta used to compute the next value of the sequence
52
+ # * +:no_sync - do not flush synchronously to the log (ACI without D),
53
+ # BDB: +DB_TXN_NOSYNC+
54
+ def next(transaction=nil,options={})
55
+ delta = options.delete(:delta) || 1
56
+ _next(transaction,delta,options)
57
+ end
58
+
59
+ inline(:C) do |builder|
60
+ Rod::Berkeley::Environment.init_builder(builder)
61
+ builder.prefix(Rod::Berkeley::Environment.database_error)
62
+
63
+ str =<<-END
64
+ |/*
65
+ |* Closes the sequence causing the resources to be freed.
66
+ |*/
67
+ |void seq_free(DB_SEQUENCE * seq_pointer){
68
+ | int return_value;
69
+ |
70
+ | if(seq_pointer != NULL){
71
+ | return_value = seq_pointer->close(seq_pointer,0);
72
+ | if(return_value != 0){
73
+ | rb_raise(databaseError(),"%s",db_strerror(return_value));
74
+ | }
75
+ | }
76
+ |}
77
+ END
78
+ builder.prefix(str.margin)
79
+
80
+ str =<<-END
81
+ |/*
82
+ |* Replaces default allocate with function returning wrapper for the
83
+ |* sequence struct.
84
+ |*/
85
+ |VALUE allocate(){
86
+ | // db_mark == NULL - no internal elements have to be marked
87
+ | // struct == NULL - there is no struct to wrap at the moment
88
+ | return Data_Wrap_Struct(self,NULL,seq_free,NULL);
89
+ |}
90
+ END
91
+ builder.c_singleton(str.margin)
92
+
93
+ str =<<-END
94
+ |/*
95
+ |* Opens the sequence with +key+ in the associated database.
96
+ |* The +key+ is the pointer to the key, and +key_size+ is its size.
97
+ |* The operation might be protected by the +transaction+ given.
98
+ |* See +open+ for a list of options.
99
+ |*/
100
+ |void _open_raw(VALUE self, void * key, unsigned int key_size, VALUE transaction, VALUE options){
101
+ | DB * db_pointer;
102
+ | DB_SEQUENCE * seq_pointer;
103
+ | DB_TXN *txn_pointer;
104
+ | int return_value;
105
+ | VALUE database;
106
+ | DBT db_key;
107
+ | u_int32_t flags;
108
+ | int32_t cache_size;
109
+ |
110
+ | database = rb_iv_get(self,"@database");
111
+ | if(NIL_P(database)){
112
+ | rb_raise(databaseError(),"The database of the sequence is nil!");
113
+ | }
114
+ | Data_Get_Struct(database,DB,db_pointer);
115
+ | if(db_pointer == NULL){
116
+ | rb_raise(databaseError(),"The database of the sequence is NULL!");
117
+ | }
118
+ | // Only 0 is a valid flag.
119
+ | return_value = db_sequence_create(&seq_pointer, db_pointer,0);
120
+ | if(return_value != 0){
121
+ | rb_raise(databaseError(),"%s",db_strerror(return_value));
122
+ | }
123
+ | // By default the sequence starts with 1.
124
+ | return_value = seq_pointer->initial_value(seq_pointer, 1);
125
+ |
126
+ | memset(&db_key, 0, sizeof(DBT));
127
+ | db_key.data = key;
128
+ | db_key.size = key_size;
129
+ |
130
+ | flags = 0;
131
+ | if(rb_hash_aref(options,ID2SYM(rb_intern("create"))) == Qtrue){
132
+ | flags |= DB_CREATE;
133
+ | }
134
+ | if(rb_hash_aref(options,ID2SYM(rb_intern("create_exclusive"))) == Qtrue){
135
+ | flags |= DB_EXCL;
136
+ | }
137
+ | if(rb_hash_aref(options,ID2SYM(rb_intern("threads"))) == Qtrue){
138
+ | flags |= DB_THREAD;
139
+ | }
140
+ | cache_size = 1;
141
+ | if(rb_hash_aref(options,ID2SYM(rb_intern("cache_size"))) != Qnil){
142
+ | cache_size = NUM2INT(rb_hash_aref(options,ID2SYM(rb_intern("cache_size"))));
143
+ | }
144
+ | seq_pointer->set_cachesize(seq_pointer,cache_size);
145
+ |
146
+ | if(NIL_P(transaction)){
147
+ | return_value = seq_pointer->open(seq_pointer, NULL, &db_key, flags);
148
+ | } else {
149
+ | Data_Get_Struct(transaction,DB_TXN,txn_pointer);
150
+ | return_value = seq_pointer->open(seq_pointer, txn_pointer, &db_key, flags);
151
+ | }
152
+ | if(return_value != 0){
153
+ | rb_raise(databaseError(),"%s",db_strerror(return_value));
154
+ | }
155
+ | DATA_PTR(self) = seq_pointer;
156
+ |}
157
+ END
158
+ builder.c_raw(str.margin)
159
+
160
+ str =<<-END
161
+ |/*
162
+ |* Opens the sequence with a string key.
163
+ |* See _open for the arguments.
164
+ |*/
165
+ |void _open_string(VALUE key, VALUE transaction, VALUE options){
166
+ | _open_raw(self,RSTRING_PTR(key),RSTRING_LEN(key),transaction,options);
167
+ |}
168
+ END
169
+ builder.c(str.margin)
170
+
171
+ str =<<-END
172
+ |/*
173
+ |* Returns the next value of the seqence. The operation
174
+ |* might be secured by the +transaction+. The sequence step
175
+ |* is defined by +delta+.
176
+ |* See +next+ for a list of options.
177
+ |*/
178
+ |unsigned long _next(VALUE transaction,int delta,VALUE options){
179
+ | DB_SEQUENCE *seq_pointer;
180
+ | DB_TXN *txn_pointer;
181
+ | int return_value;
182
+ | db_seq_t next_value;
183
+ | u_int32_t flags;
184
+ |
185
+ | Data_Get_Struct(self,DB_SEQUENCE,seq_pointer);
186
+ | if(seq_pointer == NULL){
187
+ | rb_raise(databaseError(),"The handle for the sequence is NULL!");
188
+ | }
189
+ |
190
+ | flags = 0;
191
+ | if(rb_hash_aref(options,ID2SYM(rb_intern("no_sync"))) == Qtrue){
192
+ | flags |= DB_TXN_NOSYNC;
193
+ | }
194
+ | if(NIL_P(transaction)){
195
+ | return_value = seq_pointer->get(seq_pointer,NULL,(int32_t)delta,&next_value,flags);
196
+ | } else {
197
+ | Data_Get_Struct(transaction,DB_TXN,txn_pointer);
198
+ | return_value = seq_pointer->get(seq_pointer,txn_pointer,(int32_t)delta,&next_value,flags);
199
+ | }
200
+ | return (unsigned long)next_value;
201
+ |}
202
+ END
203
+ builder.c(str.margin)
204
+
205
+ str =<<-END
206
+ |/*
207
+ |* Closes the sequence if it is opened.
208
+ |*/
209
+ |void _close(){
210
+ | DB_SEQUENCE * seq_pointer;
211
+ | int return_value;
212
+ |
213
+ | Data_Get_Struct(self,DB_SEQUENCE,seq_pointer);
214
+ | seq_free(seq_pointer);
215
+ | DATA_PTR(self) = NULL;
216
+ |}
217
+ END
218
+ builder.c(str.margin)
219
+ end
220
+ end
221
+ end
222
+ end
@@ -0,0 +1,233 @@
1
+ module Rod
2
+ module Berkeley
3
+ class Transaction
4
+ # The environment of the transaction.
5
+ attr_reader :environment
6
+
7
+ # Initializes the transaction within the given +environment+.
8
+ def initialize(environment)
9
+ @environment = environment
10
+ @started = false
11
+ @finished = false
12
+ end
13
+
14
+ # Begins the transaction.
15
+ #
16
+ # The following options are supported (see the Berkeley DB documentation
17
+ # for full description of the flags):
18
+ # * +:read_committed: - degree 2 isolation of the transaction,
19
+ # BDB: +DB_READ_COMMITTED+
20
+ # * +:read_uncommitted: - degree 1 isolation of the transaction,
21
+ # BDB: +DB_READ_UNCOMMITTED+
22
+ # * +:bulk: - enable transactional bulk insert, NOT SUPPORTED!
23
+ # BDB: +DB_TXN_BULK+
24
+ # * +:no_sync: - do not flush synchronously to the log (ACI without D),
25
+ # BDB: +DB_TXN_NOSYNC+
26
+ # * +:no_wait: - raises exception if can't obtain a lock immediately,
27
+ # BDB: +DB_TXN_NOWAIT+
28
+ # * +:snapshot: - snapshot isolation of the transaction,
29
+ # BDB: +DB_TXN_SNAPSHOT+
30
+ # * +:sync: - (default) flush synchronously to the log (full ACID),
31
+ # BDB: +DB_TXN_SYNC+
32
+ # * +:wait: - (default) wait for locks if they are not available,
33
+ # BDB: +DB_TXN_WAIT+
34
+ # * +:write_no_sync: - write to the log but don't flush it, similar to
35
+ # +:no_sync+, BDB: +DB_TXN_WRITE_NOSYNC+
36
+ def begin(options={})
37
+ raise DatabaseError.new("The transaction has already started.") if started?
38
+ _begin(options)
39
+ @started = true
40
+ end
41
+
42
+ # Returns true if the transaction was started.
43
+ def started?
44
+ @started
45
+ end
46
+
47
+ # Returns true if the transaction was finished.
48
+ def finished?
49
+ @finished
50
+ end
51
+
52
+ # Restarts the transaction if it was finished.
53
+ # This allows for preservation of resources.
54
+ def reset
55
+ raise DatabaseError.new("Transaction not finished!") if started? && !finished?
56
+ @started = false
57
+ @finished = false
58
+ end
59
+
60
+ # Aborts the transaction if it was started and not finished otherwise
61
+ # leaves it as it is.
62
+ def finish
63
+ return unless started?
64
+ return if finished?
65
+ self.abort
66
+ end
67
+
68
+ # Commit the transaction.
69
+ #
70
+ # The following options are supported (see the Berkeley DB documentation
71
+ # for full description of the flags):
72
+ # * +:no_sync: - do not flush synchronously to the log (ACI without D),
73
+ # BDB: +DB_TXN_NOSYNC+
74
+ # * +:sync: - (default) flush synchronously to the log (full ACID),
75
+ # BDB: +DB_TXN_SYNC+
76
+ # * +:write_no_sync: - write to the log but don't flush it, similar to
77
+ # +:no_sync+, BDB: +DB_TXN_WRITE_NOSYNC+
78
+ def commit(options={})
79
+ raise DatabaseError.new("The transaction has not been started!") unless started?
80
+ _commit(options)
81
+ @finished = true
82
+ end
83
+
84
+ # Abort the transaction.
85
+ def abort
86
+ raise DatabaseError.new("The transaction has not been started!") unless started?
87
+ _abort
88
+ @finished = true
89
+ end
90
+
91
+ inline(:C) do |builder|
92
+ Rod::Berkeley::Environment.init_builder(builder)
93
+ builder.prefix(Rod::Berkeley::Environment.database_error)
94
+
95
+ str =<<-END
96
+ |/*
97
+ |* Aborts the transaction causing the resources to be freed.
98
+ |*/
99
+ |void txn_free(DB_TXN * txn_pointer){
100
+ | int return_value;
101
+ |
102
+ | if(txn_pointer != NULL){
103
+ | return_value = txn_pointer->abort(txn_pointer);
104
+ | if(return_value != 0){
105
+ | rb_raise(databaseError(),"%s",db_strerror(return_value));
106
+ | }
107
+ | }
108
+ |}
109
+ END
110
+ builder.prefix(str.margin)
111
+
112
+ str =<<-END
113
+ |/*
114
+ |* Replaces default allocate with function returning wrapper for the
115
+ |* transaction struct.
116
+ |*/
117
+ |VALUE allocate(){
118
+ | // db_mark == NULL - no internal elements have to be marked
119
+ | // struct == NULL - there is no struct to wrap at the moment
120
+ | return Data_Wrap_Struct(self,NULL,txn_free,NULL);
121
+ |}
122
+ END
123
+ builder.c_singleton(str.margin)
124
+
125
+ str =<<-END
126
+ |/*
127
+ |* Starts the transaction. See +start+ for a list of options.
128
+ |*/
129
+ |void _begin(VALUE options){
130
+ | DB_ENV * env_pointer;
131
+ | DB_TXN * txn_pointer;
132
+ | u_int32_t flags;
133
+ | int return_value;
134
+ | VALUE environment;
135
+ |
136
+ | environment = rb_iv_get(self,"@environment");
137
+ | if(NIL_P(environment)){
138
+ | rb_raise(databaseError(),"The environment of the transaction is nil!");
139
+ | }
140
+ | Data_Get_Struct(environment,DB_ENV,env_pointer);
141
+ | if(env_pointer == NULL){
142
+ | rb_raise(databaseError(),"The environment of the transaction is NULL!");
143
+ | }
144
+ | flags = 0;
145
+ | if(rb_hash_aref(options,ID2SYM(rb_intern("read_committed"))) == Qtrue){
146
+ | flags |= DB_READ_COMMITTED;
147
+ | }
148
+ | if(rb_hash_aref(options,ID2SYM(rb_intern("read_uncommitted"))) == Qtrue){
149
+ | flags |= DB_READ_UNCOMMITTED;
150
+ | }
151
+ | if(rb_hash_aref(options,ID2SYM(rb_intern("bulk"))) == Qtrue){
152
+ | // Not supported yet.
153
+ | //flags |= DB_TXN_BULK;
154
+ | }
155
+ | if(rb_hash_aref(options,ID2SYM(rb_intern("no_sync"))) == Qtrue){
156
+ | flags |= DB_TXN_NOSYNC;
157
+ | }
158
+ | if(rb_hash_aref(options,ID2SYM(rb_intern("no_wait"))) == Qtrue){
159
+ | flags |= DB_TXN_NOWAIT;
160
+ | }
161
+ | if(rb_hash_aref(options,ID2SYM(rb_intern("snapshot"))) == Qtrue){
162
+ | flags |= DB_TXN_SNAPSHOT;
163
+ | }
164
+ | if(rb_hash_aref(options,ID2SYM(rb_intern("sync"))) == Qtrue){
165
+ | flags |= DB_TXN_SYNC;
166
+ | }
167
+ | if(rb_hash_aref(options,ID2SYM(rb_intern("wait"))) == Qtrue){
168
+ | flags |= DB_TXN_WAIT;
169
+ | }
170
+ | if(rb_hash_aref(options,ID2SYM(rb_intern("write_no_sync"))) == Qtrue){
171
+ | flags |= DB_TXN_WRITE_NOSYNC;
172
+ | }
173
+ | // 1 NULL - parent transaction (not implemented yet)
174
+ | return_value = env_pointer->txn_begin(env_pointer,NULL,&txn_pointer,flags);
175
+ | if(return_value != 0){
176
+ | rb_raise(databaseError(),"%s",db_strerror(return_value));
177
+ | }
178
+ | DATA_PTR(self) = txn_pointer;
179
+ |}
180
+ END
181
+ builder.c(str.margin)
182
+
183
+ str =<<-END
184
+ |/*
185
+ |* Commit the transaction.
186
+ |*/
187
+ |void _commit(VALUE options){
188
+ | DB_TXN * txn_pointer;
189
+ | int return_value;
190
+ | u_int32_t flags;
191
+ |
192
+ | Data_Get_Struct(self,DB_TXN,txn_pointer);
193
+ |
194
+ | flags = 0;
195
+ | if(rb_hash_aref(options,ID2SYM(rb_intern("no_sync"))) == Qtrue){
196
+ | flags |= DB_TXN_NOSYNC;
197
+ | }
198
+ | if(rb_hash_aref(options,ID2SYM(rb_intern("sync"))) == Qtrue){
199
+ | flags |= DB_TXN_SYNC;
200
+ | }
201
+ | if(rb_hash_aref(options,ID2SYM(rb_intern("write_no_sync"))) == Qtrue){
202
+ | flags |= DB_TXN_WRITE_NOSYNC;
203
+ | }
204
+ |
205
+ | if(txn_pointer != NULL){
206
+ | return_value = txn_pointer->commit(txn_pointer,flags);
207
+ | if(return_value != 0){
208
+ | rb_raise(databaseError(),"%s",db_strerror(return_value));
209
+ | }
210
+ | }
211
+ | DATA_PTR(self) = NULL;
212
+ |}
213
+ END
214
+ builder.c(str.margin)
215
+
216
+ str =<<-END
217
+ |/*
218
+ |* Abort the transaction.
219
+ |*/
220
+ |void _abort(){
221
+ | DB_TXN * txn_pointer;
222
+ | int return_value;
223
+ |
224
+ | Data_Get_Struct(self,DB_TXN,txn_pointer);
225
+ | txn_free(txn_pointer);
226
+ | DATA_PTR(self) = NULL;
227
+ |}
228
+ END
229
+ builder.c(str.margin)
230
+ end
231
+ end
232
+ end
233
+ end