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
@@ -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
|