mdbx 0.1.1 → 0.3.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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/History.md +62 -0
- data/README.md +0 -7
- data/ext/mdbx_ext/database.c +552 -384
- data/ext/mdbx_ext/mdbx_ext.h +20 -7
- data/ext/mdbx_ext/stats.c +39 -7
- data/lib/mdbx.rb +1 -1
- data/lib/mdbx/database.rb +109 -82
- metadata +2 -2
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 678369c278e65d4faf56eb814e2cb7eba22da57fb5c07c6e4f9dc4e6edd7f09f
|
4
|
+
data.tar.gz: 0fc4b455b4556bb932f867fa40e7c240f555a5a6bbb7d09cb55c68ed7745bfd0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b9844be65fc6e46df8dbaee242ce9bf185c4330559d2a5d8c803eca43f4d09a783276c3d2a20258ef84be099a133031acf0f0b36ff0c2af1f69d0882a0dd4335
|
7
|
+
data.tar.gz: 22fd3c418db16d2a58100ad6bd8f3ae7b7ddce2172075713a798065440e59f6a2fd4cea47bd37d3585781dbeaebc0421f483e20375a5e652fe3a81ee76e0da2f
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/History.md
CHANGED
@@ -1,6 +1,68 @@
|
|
1
1
|
# Release History for MDBX
|
2
2
|
|
3
3
|
---
|
4
|
+
## v0.3.2 [2021-07-13] Mahlon E. Smith <mahlon@martini.nu>
|
5
|
+
|
6
|
+
Bugfixes:
|
7
|
+
|
8
|
+
- Fix double memory allocation during initialization.
|
9
|
+
- Make various ruby->c string allocations safe with the garbage collector.
|
10
|
+
|
11
|
+
|
12
|
+
Minutiae:
|
13
|
+
|
14
|
+
- Raise exception if instantiating with invalid options.
|
15
|
+
|
16
|
+
|
17
|
+
---
|
18
|
+
## v0.3.1 [2021-05-16] Mahlon E. Smith <mahlon@martini.nu>
|
19
|
+
|
20
|
+
Bugfix:
|
21
|
+
|
22
|
+
- #drop could potentially remove unintended data. Yanked version
|
23
|
+
v0.3.0.
|
24
|
+
|
25
|
+
|
26
|
+
---
|
27
|
+
## v0.3.0 [2021-04-09] Mahlon E. Smith <mahlon@martini.nu>
|
28
|
+
|
29
|
+
Enhancements:
|
30
|
+
|
31
|
+
- Alter the behavior of #clear, so it doesn't destroy collection
|
32
|
+
environments, but just empties them.
|
33
|
+
|
34
|
+
- Add #drop, which explictly -does- destroy a collection environment.
|
35
|
+
|
36
|
+
- Switching to a collection now automatically creates its environment.
|
37
|
+
|
38
|
+
- Add include? and has_key?, for presence checks without allocating
|
39
|
+
value memory or requiring deserialization.
|
40
|
+
|
41
|
+
|
42
|
+
Bugfixes:
|
43
|
+
|
44
|
+
- Run all cursor methods through rb_protect, to ensure proper
|
45
|
+
cursor cleanup in the event of an exception mid iteration.
|
46
|
+
|
47
|
+
- Fix the block form of collections to support multiple scopes.
|
48
|
+
|
49
|
+
|
50
|
+
## v0.2.1 [2021-04-06] Mahlon E. Smith <mahlon@martini.nu>
|
51
|
+
|
52
|
+
Enhancement:
|
53
|
+
|
54
|
+
- Automatically stringify any argument to the collection() method.
|
55
|
+
|
56
|
+
|
57
|
+
## v0.2.0 [2021-03-19] Mahlon E. Smith <mahlon@martini.nu>
|
58
|
+
|
59
|
+
Enhancement:
|
60
|
+
|
61
|
+
- Support dup/clone. This has limited use, as there can only
|
62
|
+
be one open handle per process, but implemented in the interests
|
63
|
+
of avoiding unexpected behavior.
|
64
|
+
|
65
|
+
|
4
66
|
## v0.1.1 [2021-03-14] Mahlon E. Smith <mahlon@martini.nu>
|
5
67
|
|
6
68
|
Bugfix:
|
data/README.md
CHANGED
@@ -340,13 +340,6 @@ information about the build environment, the database environment, and
|
|
340
340
|
the currently connected clients.
|
341
341
|
|
342
342
|
|
343
|
-
## TODO
|
344
|
-
|
345
|
-
- Expose more database/collection information to statistics
|
346
|
-
- Support libmdbx multiple values per key DUPSORT via `put`, `get`
|
347
|
-
Enumerators, and a 'value' argument for `delete`.
|
348
|
-
|
349
|
-
|
350
343
|
## Contributing
|
351
344
|
|
352
345
|
You can check out the current development source with Mercurial via its
|
data/ext/mdbx_ext/database.c
CHANGED
@@ -1,18 +1,16 @@
|
|
1
|
-
/* vim: set noet sta sw=4 ts=4 : */
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
/* Shortcut for fetching current DB variables.
|
1
|
+
/* vim: set noet sta sw=4 ts=4 fdm=marker: */
|
2
|
+
/*
|
3
|
+
* Primary database handle functions.
|
4
|
+
*
|
6
5
|
*/
|
7
|
-
#define UNWRAP_DB( val, db ) \
|
8
|
-
rmdbx_db_t *db; \
|
9
|
-
TypedData_Get_Struct( val, rmdbx_db_t, &rmdbx_db_data, db );
|
10
6
|
|
7
|
+
#include "mdbx_ext.h"
|
11
8
|
|
12
9
|
VALUE rmdbx_cDatabase;
|
13
10
|
|
11
|
+
|
14
12
|
/*
|
15
|
-
* Ruby allocation
|
13
|
+
* Ruby data allocation wrapper.
|
16
14
|
*/
|
17
15
|
static const rb_data_type_t rmdbx_db_data = {
|
18
16
|
.wrap_struct_name = "MDBX::Database::Data",
|
@@ -27,11 +25,24 @@ static const rb_data_type_t rmdbx_db_data = {
|
|
27
25
|
VALUE
|
28
26
|
rmdbx_alloc( VALUE klass )
|
29
27
|
{
|
30
|
-
rmdbx_db_t *new
|
28
|
+
rmdbx_db_t *new;
|
31
29
|
return TypedData_Make_Struct( klass, rmdbx_db_t, &rmdbx_db_data, new );
|
32
30
|
}
|
33
31
|
|
34
32
|
|
33
|
+
/*
|
34
|
+
* Cleanup a previously allocated DB environment.
|
35
|
+
*/
|
36
|
+
void
|
37
|
+
rmdbx_free( void *db )
|
38
|
+
{
|
39
|
+
if ( db ) {
|
40
|
+
rmdbx_close_all( db );
|
41
|
+
xfree( db );
|
42
|
+
}
|
43
|
+
}
|
44
|
+
|
45
|
+
|
35
46
|
/*
|
36
47
|
* Ensure all database file descriptors are collected and
|
37
48
|
* removed.
|
@@ -62,19 +73,9 @@ rmdbx_close_dbi( rmdbx_db_t *db )
|
|
62
73
|
|
63
74
|
|
64
75
|
/*
|
65
|
-
*
|
66
|
-
|
67
|
-
|
68
|
-
rmdbx_free( void *db )
|
69
|
-
{
|
70
|
-
if ( db ) {
|
71
|
-
rmdbx_close_all( db );
|
72
|
-
xfree( db );
|
73
|
-
}
|
74
|
-
}
|
75
|
-
|
76
|
-
|
77
|
-
/*
|
76
|
+
* call-seq:
|
77
|
+
* db.close => true
|
78
|
+
*
|
78
79
|
* Cleanly close an opened database.
|
79
80
|
*/
|
80
81
|
VALUE
|
@@ -101,17 +102,63 @@ rmdbx_closed_p( VALUE self )
|
|
101
102
|
|
102
103
|
|
103
104
|
/*
|
104
|
-
*
|
105
|
-
|
105
|
+
* Check if a given +flag+ is enabled for flag +val+.
|
106
|
+
*/
|
107
|
+
int
|
108
|
+
rmdbx_flag_enabled( val, flag )
|
109
|
+
{
|
110
|
+
return ( val & flag ) == flag;
|
111
|
+
}
|
112
|
+
|
113
|
+
|
114
|
+
/*
|
115
|
+
* Given a ruby string +key+ and a pointer to an MDBX_val, prepare the
|
116
|
+
* key for usage within mdbx. All keys are explicitly converted to
|
117
|
+
* strings.
|
106
118
|
*
|
107
|
-
|
108
|
-
|
119
|
+
*/
|
120
|
+
void
|
121
|
+
rmdbx_key_for( VALUE key, MDBX_val *ckey )
|
122
|
+
{
|
123
|
+
VALUE key_str = rb_funcall( key, rb_intern("to_s"), 0 );
|
124
|
+
ckey->iov_len = RSTRING_LEN( key_str );
|
125
|
+
ckey->iov_base = malloc( ckey->iov_len );
|
126
|
+
strlcpy( ckey->iov_base, StringValuePtr(key_str), ckey->iov_len + 1 );
|
127
|
+
}
|
128
|
+
|
129
|
+
|
130
|
+
/*
|
131
|
+
* Given a ruby +value+ and a pointer to an MDBX_val, prepare
|
132
|
+
* the value for usage within mdbx. Values are potentially serialized.
|
133
|
+
*
|
134
|
+
*/
|
135
|
+
void
|
136
|
+
rmdbx_val_for( VALUE self, VALUE val, MDBX_val *data )
|
137
|
+
{
|
138
|
+
VALUE serialize_proc = rb_iv_get( self, "@serializer" );
|
139
|
+
|
140
|
+
if ( ! NIL_P( serialize_proc ) )
|
141
|
+
val = rb_funcall( serialize_proc, rb_intern("call"), 1, val );
|
142
|
+
|
143
|
+
Check_Type( val, T_STRING );
|
144
|
+
|
145
|
+
data->iov_len = RSTRING_LEN( val );
|
146
|
+
data->iov_base = malloc( data->iov_len );
|
147
|
+
strlcpy( data->iov_base, StringValuePtr(val), data->iov_len + 1 );
|
148
|
+
}
|
149
|
+
|
150
|
+
|
151
|
+
/*
|
152
|
+
* Deserialize and return a value.
|
109
153
|
*/
|
110
154
|
VALUE
|
111
|
-
|
155
|
+
rmdbx_deserialize( VALUE self, VALUE val )
|
112
156
|
{
|
113
|
-
|
114
|
-
|
157
|
+
VALUE deserialize_proc = rb_iv_get( self, "@deserializer" );
|
158
|
+
if ( ! NIL_P( deserialize_proc ) )
|
159
|
+
val = rb_funcall( deserialize_proc, rb_intern("call"), 1, val );
|
160
|
+
|
161
|
+
return val;
|
115
162
|
}
|
116
163
|
|
117
164
|
|
@@ -148,138 +195,65 @@ rmdbx_open_env( VALUE self )
|
|
148
195
|
rmdbx_close_all( db );
|
149
196
|
rb_raise( rmdbx_eDatabaseError, "mdbx_env_open: (%d) %s", rc, mdbx_strerror(rc) );
|
150
197
|
}
|
151
|
-
db->state.open = 1;
|
152
198
|
|
199
|
+
db->state.open = 1;
|
153
200
|
return Qtrue;
|
154
201
|
}
|
155
202
|
|
156
203
|
|
157
|
-
/*
|
158
|
-
* Open a cursor for iteration.
|
159
|
-
*/
|
160
|
-
void
|
161
|
-
rmdbx_open_cursor( rmdbx_db_t *db )
|
162
|
-
{
|
163
|
-
if ( ! db->state.open ) rb_raise( rmdbx_eDatabaseError, "Closed database." );
|
164
|
-
if ( ! db->txn ) rb_raise( rmdbx_eDatabaseError, "No snapshot or transaction currently open." );
|
165
|
-
|
166
|
-
int rc = mdbx_cursor_open( db->txn, db->dbi, &db->cursor );
|
167
|
-
if ( rc != MDBX_SUCCESS ) {
|
168
|
-
rmdbx_close_all( db );
|
169
|
-
rb_raise( rmdbx_eDatabaseError, "Unable to open cursor: (%d) %s", rc, mdbx_strerror(rc) );
|
170
|
-
}
|
171
|
-
|
172
|
-
return;
|
173
|
-
}
|
174
|
-
|
175
|
-
|
176
|
-
/*
|
177
|
-
* Open a new database transaction. If a transaction is already
|
178
|
-
* open, this is a no-op.
|
179
|
-
*
|
180
|
-
* +rwflag+ must be either MDBX_TXN_RDONLY or MDBX_TXN_READWRITE.
|
181
|
-
*/
|
182
|
-
void
|
183
|
-
rmdbx_open_txn( rmdbx_db_t *db, int rwflag )
|
184
|
-
{
|
185
|
-
if ( db->txn ) return;
|
186
|
-
|
187
|
-
int rc = mdbx_txn_begin( db->env, NULL, rwflag, &db->txn );
|
188
|
-
if ( rc != MDBX_SUCCESS ) {
|
189
|
-
rmdbx_close_all( db );
|
190
|
-
rb_raise( rmdbx_eDatabaseError, "mdbx_txn_begin: (%d) %s", rc, mdbx_strerror(rc) );
|
191
|
-
}
|
192
|
-
|
193
|
-
if ( db->dbi == 0 ) {
|
194
|
-
// FIXME: dbi_flags
|
195
|
-
rc = mdbx_dbi_open( db->txn, db->subdb, MDBX_CREATE, &db->dbi );
|
196
|
-
if ( rc != MDBX_SUCCESS ) {
|
197
|
-
rmdbx_close_all( db );
|
198
|
-
rb_raise( rmdbx_eDatabaseError, "mdbx_dbi_open: (%d) %s", rc, mdbx_strerror(rc) );
|
199
|
-
}
|
200
|
-
}
|
201
|
-
|
202
|
-
return;
|
203
|
-
}
|
204
|
-
|
205
|
-
|
206
|
-
/*
|
207
|
-
* Close any existing database transaction. If there is no
|
208
|
-
* active transaction, this is a no-op. If there is a long
|
209
|
-
* running transaction open, this is a no-op.
|
210
|
-
*
|
211
|
-
* +txnflag must either be RMDBX_TXN_ROLLBACK or RMDBX_TXN_COMMIT.
|
212
|
-
*/
|
213
|
-
void
|
214
|
-
rmdbx_close_txn( rmdbx_db_t *db, int txnflag )
|
215
|
-
{
|
216
|
-
if ( ! db->txn || db->state.retain_txn > -1 ) return;
|
217
|
-
|
218
|
-
switch ( txnflag ) {
|
219
|
-
case RMDBX_TXN_COMMIT:
|
220
|
-
mdbx_txn_commit( db->txn );
|
221
|
-
default:
|
222
|
-
mdbx_txn_abort( db->txn );
|
223
|
-
}
|
224
|
-
|
225
|
-
db->txn = 0;
|
226
|
-
return;
|
227
|
-
}
|
228
|
-
|
229
|
-
|
230
204
|
/*
|
231
205
|
* call-seq:
|
232
|
-
* db.
|
233
|
-
*
|
234
|
-
* Open a new long-running transaction. If +mode+ is true,
|
235
|
-
* it is opened read/write.
|
206
|
+
* db.clear
|
236
207
|
*
|
208
|
+
* Empty the current collection on disk. If collections are not enabled
|
209
|
+
* or the database handle is set to the top-level (main) db - this
|
210
|
+
* deletes *all records* from the database.
|
237
211
|
*/
|
238
212
|
VALUE
|
239
|
-
|
213
|
+
rmdbx_clear( VALUE self )
|
240
214
|
{
|
241
215
|
UNWRAP_DB( self, db );
|
242
216
|
|
243
|
-
rmdbx_open_txn( db,
|
244
|
-
|
217
|
+
rmdbx_open_txn( db, MDBX_TXN_READWRITE );
|
218
|
+
int rc = mdbx_drop( db->txn, db->dbi, false );
|
245
219
|
|
246
|
-
|
220
|
+
if ( rc != MDBX_SUCCESS )
|
221
|
+
rb_raise( rmdbx_eDatabaseError, "mdbx_drop: (%d) %s", rc, mdbx_strerror(rc) );
|
222
|
+
|
223
|
+
rmdbx_close_txn( db, RMDBX_TXN_COMMIT );
|
224
|
+
|
225
|
+
return Qnil;
|
247
226
|
}
|
248
227
|
|
249
228
|
|
250
229
|
/*
|
251
230
|
* call-seq:
|
252
|
-
* db.
|
253
|
-
*
|
254
|
-
* Close a long-running transaction. If +write+ is true,
|
255
|
-
* the transaction is committed. Otherwise, rolled back.
|
231
|
+
* db.drop( collection ) -> db
|
256
232
|
*
|
233
|
+
* Destroy a collection. You must be in the top level database to call
|
234
|
+
* this method.
|
257
235
|
*/
|
258
236
|
VALUE
|
259
|
-
|
237
|
+
rmdbx_drop( VALUE self, VALUE name )
|
260
238
|
{
|
261
239
|
UNWRAP_DB( self, db );
|
262
240
|
|
263
|
-
|
264
|
-
|
241
|
+
/* Provide a friendlier error message if max_collections is 0. */
|
242
|
+
if ( db->settings.max_collections == 0 )
|
243
|
+
rb_raise( rmdbx_eDatabaseError, "Unable to drop collection: collections are not enabled." );
|
265
244
|
|
266
|
-
|
267
|
-
|
245
|
+
/* All transactions must be closed when dropping a database. */
|
246
|
+
if ( db->txn )
|
247
|
+
rb_raise( rmdbx_eDatabaseError, "Unable to drop collection: transaction open" );
|
268
248
|
|
249
|
+
/* A drop can only be performed from the top-level database. */
|
250
|
+
if ( db->subdb != NULL )
|
251
|
+
rb_raise( rmdbx_eDatabaseError, "Unable to drop collection: switch to top-level db first" );
|
269
252
|
|
270
|
-
|
271
|
-
|
272
|
-
* db.clear
|
273
|
-
*
|
274
|
-
* Empty the current collection on disk. If collections are not enabled
|
275
|
-
* or the database handle is set to the top-level (main) db - this
|
276
|
-
* deletes *all records* from the database. This is not recoverable!
|
277
|
-
*/
|
278
|
-
VALUE
|
279
|
-
rmdbx_clear( VALUE self )
|
280
|
-
{
|
281
|
-
UNWRAP_DB( self, db );
|
253
|
+
name = rb_funcall( name, rb_intern("to_s"), 0 );
|
254
|
+
db->subdb = StringValueCStr( name );
|
282
255
|
|
256
|
+
rmdbx_close_dbi( db ); /* ensure we're reopening within the new subdb */
|
283
257
|
rmdbx_open_txn( db, MDBX_TXN_READWRITE );
|
284
258
|
int rc = mdbx_drop( db->txn, db->dbi, true );
|
285
259
|
|
@@ -288,345 +262,478 @@ rmdbx_clear( VALUE self )
|
|
288
262
|
|
289
263
|
rmdbx_close_txn( db, RMDBX_TXN_COMMIT );
|
290
264
|
|
291
|
-
/*
|
292
|
-
|
265
|
+
/* Reset the current collection to the top level. */
|
266
|
+
db->subdb = NULL;
|
267
|
+
rmdbx_close_dbi( db ); /* ensure next access is not in the defunct subdb */
|
293
268
|
|
294
|
-
return
|
269
|
+
return self;
|
295
270
|
}
|
296
271
|
|
297
272
|
|
298
|
-
/*
|
299
|
-
*
|
300
|
-
*
|
301
|
-
*
|
273
|
+
/* call-seq:
|
274
|
+
* db.length -> Integer
|
275
|
+
*
|
276
|
+
* Returns the count of keys in the currently selected collection.
|
302
277
|
*/
|
303
|
-
|
304
|
-
|
278
|
+
VALUE
|
279
|
+
rmdbx_length( VALUE self )
|
305
280
|
{
|
306
|
-
|
281
|
+
UNWRAP_DB( self, db );
|
282
|
+
MDBX_stat mstat;
|
283
|
+
|
284
|
+
CHECK_HANDLE();
|
285
|
+
rmdbx_open_txn( db, MDBX_TXN_RDONLY );
|
286
|
+
|
287
|
+
int rc = mdbx_dbi_stat( db->txn, db->dbi, &mstat, sizeof(mstat) );
|
288
|
+
if ( rc != MDBX_SUCCESS )
|
289
|
+
rb_raise( rmdbx_eDatabaseError, "mdbx_dbi_stat: (%d) %s", rc, mdbx_strerror(rc) );
|
307
290
|
|
308
|
-
|
309
|
-
|
310
|
-
rv.iov_base = StringValuePtr( arg );
|
291
|
+
VALUE rv = LONG2FIX( mstat.ms_entries );
|
292
|
+
rmdbx_close_txn( db, RMDBX_TXN_ROLLBACK );
|
311
293
|
|
312
294
|
return rv;
|
313
295
|
}
|
314
296
|
|
315
297
|
|
316
|
-
/*
|
317
|
-
*
|
318
|
-
*
|
298
|
+
/* call-seq:
|
299
|
+
* db.include?( 'key' ) => bool
|
300
|
+
*
|
301
|
+
* Returns true if the current collection contains +key+.
|
319
302
|
*/
|
320
|
-
|
321
|
-
|
303
|
+
VALUE
|
304
|
+
rmdbx_include( VALUE self, VALUE key )
|
322
305
|
{
|
323
|
-
|
324
|
-
VALUE serialize_proc;
|
306
|
+
UNWRAP_DB( self, db );
|
325
307
|
|
326
|
-
|
327
|
-
|
328
|
-
arg = rb_funcall( serialize_proc, rb_intern("call"), 1, arg );
|
308
|
+
CHECK_HANDLE();
|
309
|
+
rmdbx_open_txn( db, MDBX_TXN_RDONLY );
|
329
310
|
|
330
|
-
|
331
|
-
|
311
|
+
MDBX_val ckey;
|
312
|
+
MDBX_val data;
|
313
|
+
rmdbx_key_for( key, &ckey );
|
332
314
|
|
333
|
-
|
334
|
-
|
315
|
+
int rc = mdbx_get( db->txn, db->dbi, &ckey, &data );
|
316
|
+
rmdbx_close_txn( db, RMDBX_TXN_ROLLBACK );
|
317
|
+
xfree( ckey.iov_base );
|
335
318
|
|
319
|
+
switch ( rc ) {
|
320
|
+
case MDBX_SUCCESS:
|
321
|
+
return Qtrue;
|
336
322
|
|
337
|
-
|
338
|
-
|
339
|
-
*/
|
340
|
-
VALUE
|
341
|
-
rmdbx_deserialize( VALUE self, VALUE val )
|
342
|
-
{
|
343
|
-
VALUE deserialize_proc = rb_iv_get( self, "@deserializer" );
|
344
|
-
if ( ! NIL_P( deserialize_proc ) )
|
345
|
-
val = rb_funcall( deserialize_proc, rb_intern("call"), 1, val );
|
323
|
+
case MDBX_NOTFOUND:
|
324
|
+
return Qfalse;
|
346
325
|
|
347
|
-
|
326
|
+
default:
|
327
|
+
rmdbx_close( self );
|
328
|
+
rb_raise( rmdbx_eDatabaseError, "Unable to fetch key: (%d) %s", rc, mdbx_strerror(rc) );
|
329
|
+
}
|
348
330
|
}
|
349
331
|
|
350
332
|
|
351
333
|
/* call-seq:
|
352
|
-
* db
|
334
|
+
* db[ 'key' ] => value
|
353
335
|
*
|
354
|
-
*
|
355
|
-
* A transaction must be opened prior to use.
|
336
|
+
* Return a single value for +key+ immediately.
|
356
337
|
*/
|
357
338
|
VALUE
|
358
|
-
|
339
|
+
rmdbx_get_val( VALUE self, VALUE key )
|
359
340
|
{
|
360
341
|
UNWRAP_DB( self, db );
|
361
|
-
MDBX_val key, data;
|
362
|
-
|
363
|
-
rmdbx_open_cursor( db );
|
364
|
-
RETURN_ENUMERATOR( self, 0, 0 );
|
365
342
|
|
366
|
-
|
367
|
-
|
368
|
-
while ( mdbx_cursor_get( db->cursor, &key, &data, MDBX_NEXT ) == MDBX_SUCCESS ) {
|
369
|
-
rb_yield( rb_str_new( key.iov_base, key.iov_len ) );
|
370
|
-
}
|
371
|
-
}
|
343
|
+
CHECK_HANDLE();
|
344
|
+
rmdbx_open_txn( db, MDBX_TXN_RDONLY );
|
372
345
|
|
373
|
-
|
374
|
-
|
375
|
-
return self;
|
376
|
-
}
|
346
|
+
MDBX_val ckey;
|
347
|
+
MDBX_val data;
|
377
348
|
|
349
|
+
rmdbx_key_for( key, &ckey );
|
350
|
+
int rc = mdbx_get( db->txn, db->dbi, &ckey, &data );
|
351
|
+
rmdbx_close_txn( db, RMDBX_TXN_ROLLBACK );
|
352
|
+
xfree( ckey.iov_base );
|
378
353
|
|
379
|
-
|
380
|
-
|
354
|
+
VALUE rv;
|
355
|
+
switch ( rc ) {
|
356
|
+
case MDBX_SUCCESS:
|
357
|
+
rv = rb_str_new( data.iov_base, data.iov_len );
|
358
|
+
return rmdbx_deserialize( self, rv );
|
359
|
+
|
360
|
+
case MDBX_NOTFOUND:
|
361
|
+
return Qnil;
|
362
|
+
|
363
|
+
default:
|
364
|
+
rmdbx_close( self );
|
365
|
+
rb_raise( rmdbx_eDatabaseError, "Unable to fetch value: (%d) %s", rc, mdbx_strerror(rc) );
|
366
|
+
}
|
367
|
+
}
|
368
|
+
|
369
|
+
|
370
|
+
/* call-seq:
|
371
|
+
* db[ 'key' ] = value
|
381
372
|
*
|
382
|
-
*
|
383
|
-
*
|
373
|
+
* Set a single value for +key+. If the value is +nil+, the
|
374
|
+
* key is removed.
|
384
375
|
*/
|
385
376
|
VALUE
|
386
|
-
|
377
|
+
rmdbx_put_val( VALUE self, VALUE key, VALUE val )
|
387
378
|
{
|
379
|
+
int rc;
|
388
380
|
UNWRAP_DB( self, db );
|
389
|
-
MDBX_val key, data;
|
390
381
|
|
391
|
-
|
392
|
-
|
382
|
+
CHECK_HANDLE();
|
383
|
+
rmdbx_open_txn( db, MDBX_TXN_READWRITE );
|
393
384
|
|
394
|
-
|
395
|
-
|
396
|
-
rb_yield( rmdbx_deserialize( self, rv ) );
|
385
|
+
MDBX_val ckey;
|
386
|
+
rmdbx_key_for( key, &ckey );
|
397
387
|
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
388
|
+
if ( NIL_P(val) ) { /* remove if set to nil */
|
389
|
+
rc = mdbx_del( db->txn, db->dbi, &ckey, NULL );
|
390
|
+
}
|
391
|
+
else {
|
392
|
+
MDBX_val old;
|
393
|
+
MDBX_val data;
|
394
|
+
rmdbx_val_for( self, val, &data );
|
395
|
+
rc = mdbx_replace( db->txn, db->dbi, &ckey, &data, &old, 0 );
|
396
|
+
xfree( data.iov_base );
|
402
397
|
}
|
403
398
|
|
404
|
-
|
405
|
-
|
406
|
-
|
399
|
+
rmdbx_close_txn( db, RMDBX_TXN_COMMIT );
|
400
|
+
xfree( ckey.iov_base );
|
401
|
+
|
402
|
+
switch ( rc ) {
|
403
|
+
case MDBX_SUCCESS:
|
404
|
+
return val;
|
405
|
+
case MDBX_NOTFOUND:
|
406
|
+
return Qnil;
|
407
|
+
default:
|
408
|
+
rb_raise( rmdbx_eDatabaseError, "Unable to update value: (%d) %s", rc, mdbx_strerror(rc) );
|
409
|
+
}
|
407
410
|
}
|
408
411
|
|
409
412
|
|
410
|
-
/*
|
411
|
-
*
|
413
|
+
/*
|
414
|
+
* Return the currently selected collection, or +nil+ if at the
|
415
|
+
* top-level.
|
416
|
+
*/
|
417
|
+
VALUE
|
418
|
+
rmdbx_get_subdb( VALUE self )
|
419
|
+
{
|
420
|
+
UNWRAP_DB( self, db );
|
421
|
+
return ( db->subdb == NULL ) ? Qnil : rb_str_new_cstr( db->subdb );
|
422
|
+
}
|
423
|
+
|
424
|
+
|
425
|
+
/*
|
426
|
+
* Sets the current collection name for read/write operations.
|
412
427
|
*
|
413
|
-
* Calls the block once for each key and value, returning self.
|
414
|
-
* A transaction must be opened prior to use.
|
415
428
|
*/
|
416
429
|
VALUE
|
417
|
-
|
430
|
+
rmdbx_set_subdb( VALUE self, VALUE name )
|
418
431
|
{
|
419
432
|
UNWRAP_DB( self, db );
|
420
|
-
MDBX_val key, data;
|
421
433
|
|
422
|
-
|
423
|
-
|
434
|
+
/* Provide a friendlier error message if max_collections is 0. */
|
435
|
+
if ( db->settings.max_collections == 0 )
|
436
|
+
rb_raise( rmdbx_eDatabaseError, "Unable to change collection: collections are not enabled." );
|
424
437
|
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
rb_yield( rb_assoc_new( rkey, rmdbx_deserialize( self, rval ) ) );
|
438
|
+
/* All transactions must be closed when switching database handles. */
|
439
|
+
if ( db->txn )
|
440
|
+
rb_raise( rmdbx_eDatabaseError, "Unable to change collection: transaction open" );
|
429
441
|
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
442
|
+
xfree( db->subdb );
|
443
|
+
db->subdb = NULL;
|
444
|
+
|
445
|
+
if ( ! NIL_P(name) ) {
|
446
|
+
size_t len = RSTRING_LEN( name ) + 1;
|
447
|
+
db->subdb = malloc( len );
|
448
|
+
strlcpy( db->subdb, StringValuePtr(name), len );
|
435
449
|
}
|
436
450
|
|
437
|
-
|
438
|
-
|
451
|
+
/* Reset the db handle and issue a single transaction to reify
|
452
|
+
the collection.
|
453
|
+
*/
|
454
|
+
rmdbx_close_dbi( db );
|
455
|
+
rmdbx_open_txn( db, MDBX_TXN_READWRITE );
|
456
|
+
rmdbx_close_txn( db, RMDBX_TXN_COMMIT );
|
457
|
+
|
439
458
|
return self;
|
440
459
|
}
|
441
460
|
|
442
461
|
|
443
|
-
/*
|
444
|
-
*
|
462
|
+
/*
|
463
|
+
* call-seq:
|
464
|
+
* db.in_transaction? => false
|
445
465
|
*
|
446
|
-
*
|
466
|
+
* Predicate: return true if a transaction (or snapshot)
|
467
|
+
* is currently open.
|
447
468
|
*/
|
448
469
|
VALUE
|
449
|
-
|
470
|
+
rmdbx_in_transaction_p( VALUE self )
|
450
471
|
{
|
451
472
|
UNWRAP_DB( self, db );
|
452
|
-
|
473
|
+
return db->txn ? Qtrue : Qfalse;
|
474
|
+
}
|
453
475
|
|
454
|
-
if ( ! db->state.open ) rb_raise( rmdbx_eDatabaseError, "Closed database." );
|
455
|
-
rmdbx_open_txn( db, MDBX_TXN_RDONLY );
|
456
476
|
|
457
|
-
|
458
|
-
|
459
|
-
|
477
|
+
/*
|
478
|
+
* Open a new database transaction. If a transaction is already
|
479
|
+
* open, this is a no-op.
|
480
|
+
*
|
481
|
+
* +rwflag+ must be either MDBX_TXN_RDONLY or MDBX_TXN_READWRITE.
|
482
|
+
*/
|
483
|
+
void
|
484
|
+
rmdbx_open_txn( rmdbx_db_t *db, int rwflag )
|
485
|
+
{
|
486
|
+
if ( db->txn ) return;
|
460
487
|
|
461
|
-
|
462
|
-
|
488
|
+
int rc = mdbx_txn_begin( db->env, NULL, rwflag, &db->txn );
|
489
|
+
if ( rc != MDBX_SUCCESS ) {
|
490
|
+
rmdbx_close_all( db );
|
491
|
+
rb_raise( rmdbx_eDatabaseError, "mdbx_txn_begin: (%d) %s", rc, mdbx_strerror(rc) );
|
492
|
+
}
|
463
493
|
|
464
|
-
|
494
|
+
if ( db->dbi == 0 ) {
|
495
|
+
rc = mdbx_dbi_open( db->txn, db->subdb, db->settings.db_flags, &db->dbi );
|
496
|
+
if ( rc != MDBX_SUCCESS ) {
|
497
|
+
rmdbx_close_all( db );
|
498
|
+
rb_raise( rmdbx_eDatabaseError, "mdbx_dbi_open: (%d) %s", rc, mdbx_strerror(rc) );
|
499
|
+
}
|
500
|
+
}
|
501
|
+
|
502
|
+
return;
|
465
503
|
}
|
466
504
|
|
467
505
|
|
468
|
-
/*
|
469
|
-
*
|
506
|
+
/*
|
507
|
+
* Close any existing database transaction. If there is no
|
508
|
+
* active transaction, this is a no-op. If there is a long
|
509
|
+
* running transaction open, this is a no-op.
|
510
|
+
*
|
511
|
+
* +txnflag must either be RMDBX_TXN_ROLLBACK or RMDBX_TXN_COMMIT.
|
512
|
+
*/
|
513
|
+
void
|
514
|
+
rmdbx_close_txn( rmdbx_db_t *db, int txnflag )
|
515
|
+
{
|
516
|
+
if ( ! db->txn || db->state.retain_txn > -1 ) return;
|
517
|
+
|
518
|
+
if ( txnflag == RMDBX_TXN_COMMIT ) {
|
519
|
+
mdbx_txn_commit( db->txn );
|
520
|
+
}
|
521
|
+
else {
|
522
|
+
mdbx_txn_abort( db->txn );
|
523
|
+
}
|
524
|
+
|
525
|
+
db->txn = 0;
|
526
|
+
return;
|
527
|
+
}
|
528
|
+
|
529
|
+
|
530
|
+
/*
|
531
|
+
* call-seq:
|
532
|
+
* db.open_transaction( mode )
|
533
|
+
*
|
534
|
+
* Open a new long-running transaction. If +mode+ is true,
|
535
|
+
* it is opened read/write.
|
470
536
|
*
|
471
|
-
* Return a single value for +key+ immediately.
|
472
537
|
*/
|
473
538
|
VALUE
|
474
|
-
|
539
|
+
rmdbx_rb_opentxn( VALUE self, VALUE mode )
|
475
540
|
{
|
476
|
-
int rc;
|
477
541
|
UNWRAP_DB( self, db );
|
542
|
+
CHECK_HANDLE();
|
478
543
|
|
479
|
-
|
480
|
-
|
544
|
+
rmdbx_open_txn( db, RTEST(mode) ? MDBX_TXN_READWRITE : MDBX_TXN_RDONLY );
|
545
|
+
db->state.retain_txn = RTEST(mode) ? 1 : 0;
|
481
546
|
|
482
|
-
|
483
|
-
|
484
|
-
VALUE rv;
|
485
|
-
rc = mdbx_get( db->txn, db->dbi, &ckey, &data );
|
486
|
-
rmdbx_close_txn( db, RMDBX_TXN_ROLLBACK );
|
547
|
+
return Qtrue;
|
548
|
+
}
|
487
549
|
|
488
|
-
switch ( rc ) {
|
489
|
-
case MDBX_SUCCESS:
|
490
|
-
rv = rb_str_new( data.iov_base, data.iov_len );
|
491
|
-
return rmdbx_deserialize( self, rv );
|
492
550
|
|
493
|
-
|
494
|
-
|
551
|
+
/*
|
552
|
+
* call-seq:
|
553
|
+
* db.close_transaction( mode )
|
554
|
+
*
|
555
|
+
* Close a long-running transaction. If +write+ is true,
|
556
|
+
* the transaction is committed. Otherwise, rolled back.
|
557
|
+
*
|
558
|
+
*/
|
559
|
+
VALUE
|
560
|
+
rmdbx_rb_closetxn( VALUE self, VALUE write )
|
561
|
+
{
|
562
|
+
UNWRAP_DB( self, db );
|
495
563
|
|
496
|
-
|
497
|
-
|
498
|
-
|
564
|
+
db->state.retain_txn = -1;
|
565
|
+
rmdbx_close_txn( db, RTEST(write) ? RMDBX_TXN_COMMIT : RMDBX_TXN_ROLLBACK );
|
566
|
+
|
567
|
+
return Qtrue;
|
568
|
+
}
|
569
|
+
|
570
|
+
|
571
|
+
/*
|
572
|
+
* Open a cursor for iteration.
|
573
|
+
*/
|
574
|
+
void
|
575
|
+
rmdbx_open_cursor( rmdbx_db_t *db )
|
576
|
+
{
|
577
|
+
CHECK_HANDLE();
|
578
|
+
if ( ! db->txn ) rb_raise( rmdbx_eDatabaseError, "No snapshot or transaction currently open." );
|
579
|
+
|
580
|
+
int rc = mdbx_cursor_open( db->txn, db->dbi, &db->cursor );
|
581
|
+
if ( rc != MDBX_SUCCESS ) {
|
582
|
+
rmdbx_close_all( db );
|
583
|
+
rb_raise( rmdbx_eDatabaseError, "Unable to open cursor: (%d) %s", rc, mdbx_strerror(rc) );
|
499
584
|
}
|
585
|
+
|
586
|
+
return;
|
587
|
+
}
|
588
|
+
|
589
|
+
|
590
|
+
/*
|
591
|
+
* Enumerate over keys for the current collection.
|
592
|
+
*/
|
593
|
+
VALUE
|
594
|
+
rmdbx_each_key_i( VALUE self )
|
595
|
+
{
|
596
|
+
UNWRAP_DB( self, db );
|
597
|
+
MDBX_val key, data;
|
598
|
+
|
599
|
+
if ( mdbx_cursor_get( db->cursor, &key, &data, MDBX_FIRST ) == MDBX_SUCCESS ) {
|
600
|
+
rb_yield( rb_str_new( key.iov_base, key.iov_len ) );
|
601
|
+
while ( mdbx_cursor_get( db->cursor, &key, &data, MDBX_NEXT ) == MDBX_SUCCESS ) {
|
602
|
+
rb_yield( rb_str_new( key.iov_base, key.iov_len ) );
|
603
|
+
}
|
604
|
+
}
|
605
|
+
|
606
|
+
return self;
|
500
607
|
}
|
501
608
|
|
502
609
|
|
503
610
|
/* call-seq:
|
504
|
-
* db
|
611
|
+
* db.each_key {|key| block } => self
|
505
612
|
*
|
506
|
-
*
|
613
|
+
* Calls the block once for each key, returning self.
|
614
|
+
* A transaction must be opened prior to use.
|
507
615
|
*/
|
508
616
|
VALUE
|
509
|
-
|
617
|
+
rmdbx_each_key( VALUE self )
|
510
618
|
{
|
511
|
-
int rc;
|
512
619
|
UNWRAP_DB( self, db );
|
620
|
+
int state;
|
513
621
|
|
514
|
-
|
515
|
-
|
622
|
+
CHECK_HANDLE();
|
623
|
+
rmdbx_open_cursor( db );
|
624
|
+
RETURN_ENUMERATOR( self, 0, 0 );
|
516
625
|
|
517
|
-
|
626
|
+
rb_protect( rmdbx_each_key_i, self, &state );
|
518
627
|
|
519
|
-
|
520
|
-
|
521
|
-
if ( NIL_P(val) ) { /* remove if set to nil */
|
522
|
-
rc = mdbx_del( db->txn, db->dbi, &ckey, NULL );
|
523
|
-
}
|
524
|
-
else {
|
525
|
-
MDBX_val old;
|
526
|
-
MDBX_val data = rmdbx_val_for( self, val );
|
527
|
-
rc = mdbx_replace( db->txn, db->dbi, &ckey, &data, &old, 0 );
|
528
|
-
}
|
628
|
+
mdbx_cursor_close( db->cursor );
|
629
|
+
db->cursor = NULL;
|
529
630
|
|
530
|
-
|
631
|
+
if ( state ) rb_jump_tag( state );
|
531
632
|
|
532
|
-
|
533
|
-
case MDBX_SUCCESS:
|
534
|
-
return val;
|
535
|
-
case MDBX_NOTFOUND:
|
536
|
-
return Qnil;
|
537
|
-
default:
|
538
|
-
rb_raise( rmdbx_eDatabaseError, "Unable to store value: (%d) %s", rc, mdbx_strerror(rc) );
|
539
|
-
}
|
633
|
+
return self;
|
540
634
|
}
|
541
635
|
|
542
636
|
|
543
|
-
/*
|
544
|
-
* call-seq:
|
545
|
-
* db.statistics => (hash of stats)
|
546
|
-
*
|
547
|
-
* Returns a hash populated with various metadata for the opened
|
548
|
-
* database.
|
549
|
-
*
|
637
|
+
/* Enumerate over values for the current collection.
|
550
638
|
*/
|
551
639
|
VALUE
|
552
|
-
|
640
|
+
rmdbx_each_value_i( VALUE self )
|
553
641
|
{
|
554
642
|
UNWRAP_DB( self, db );
|
555
|
-
|
643
|
+
MDBX_val key, data;
|
556
644
|
|
557
|
-
|
645
|
+
if ( mdbx_cursor_get( db->cursor, &key, &data, MDBX_FIRST ) == MDBX_SUCCESS ) {
|
646
|
+
VALUE rv = rb_str_new( data.iov_base, data.iov_len );
|
647
|
+
rb_yield( rmdbx_deserialize( self, rv ) );
|
648
|
+
|
649
|
+
while ( mdbx_cursor_get( db->cursor, &key, &data, MDBX_NEXT ) == MDBX_SUCCESS ) {
|
650
|
+
rv = rb_str_new( data.iov_base, data.iov_len );
|
651
|
+
rb_yield( rmdbx_deserialize( self, rv ) );
|
652
|
+
}
|
653
|
+
}
|
654
|
+
|
655
|
+
return self;
|
558
656
|
}
|
559
657
|
|
560
658
|
|
561
|
-
/*
|
562
|
-
*
|
563
|
-
* db.collection -> (collection name, or nil if in main)
|
564
|
-
* db.collection( 'collection_name' ) -> db
|
565
|
-
* db.collection( nil ) -> db (main)
|
566
|
-
*
|
567
|
-
* Gets or sets the sub-database "collection" that read/write
|
568
|
-
* operations apply to.
|
569
|
-
* Passing +nil+ sets the database to the main, top-level namespace.
|
570
|
-
* If a block is passed, the collection automatically reverts to the
|
571
|
-
* prior collection when it exits.
|
572
|
-
*
|
573
|
-
* db.collection( 'collection_name' ) do
|
574
|
-
* [ ... ]
|
575
|
-
* end # reverts to the previous collection name
|
659
|
+
/* call-seq:
|
660
|
+
* db.each_value {|value| block } => self
|
576
661
|
*
|
662
|
+
* Calls the block once for each value, returning self.
|
663
|
+
* A transaction must be opened prior to use.
|
577
664
|
*/
|
578
665
|
VALUE
|
579
|
-
|
666
|
+
rmdbx_each_value( VALUE self )
|
580
667
|
{
|
581
668
|
UNWRAP_DB( self, db );
|
582
|
-
|
583
|
-
char *prev_db = NULL;
|
669
|
+
int state;
|
584
670
|
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
return rb_str_new_cstr( db->subdb );
|
589
|
-
}
|
671
|
+
CHECK_HANDLE();
|
672
|
+
rmdbx_open_cursor( db );
|
673
|
+
RETURN_ENUMERATOR( self, 0, 0 );
|
590
674
|
|
591
|
-
|
592
|
-
if ( db->settings.max_collections == 0 )
|
593
|
-
rb_raise( rmdbx_eDatabaseError, "Unable to change collection: collections are not enabled." );
|
675
|
+
rb_protect( rmdbx_each_value_i, self, &state );
|
594
676
|
|
595
|
-
|
596
|
-
|
597
|
-
rb_raise( rmdbx_eDatabaseError, "Unable to change collection: transaction open" );
|
677
|
+
mdbx_cursor_close( db->cursor );
|
678
|
+
db->cursor = NULL;
|
598
679
|
|
599
|
-
|
600
|
-
*/
|
601
|
-
if ( rb_block_given_p() && db->subdb != NULL ) {
|
602
|
-
prev_db = (char *) malloc( strlen(db->subdb) + 1 );
|
603
|
-
strcpy( prev_db, db->subdb );
|
604
|
-
}
|
680
|
+
if ( state ) rb_jump_tag( state );
|
605
681
|
|
606
|
-
|
607
|
-
|
682
|
+
return self;
|
683
|
+
}
|
608
684
|
|
609
|
-
/*
|
610
|
-
FIXME: Immediate transaction write to auto-create new env?
|
611
|
-
Fetching from here at the moment causes an error if you
|
612
|
-
haven't written anything to the new collection yet.
|
613
|
-
*/
|
614
685
|
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
686
|
+
/* Enumerate over key and value pairs for the current collection.
|
687
|
+
*/
|
688
|
+
VALUE
|
689
|
+
rmdbx_each_pair_i( VALUE self )
|
690
|
+
{
|
691
|
+
UNWRAP_DB( self, db );
|
692
|
+
MDBX_val key, data;
|
693
|
+
|
694
|
+
if ( mdbx_cursor_get( db->cursor, &key, &data, MDBX_FIRST ) == MDBX_SUCCESS ) {
|
695
|
+
VALUE rkey = rb_str_new( key.iov_base, key.iov_len );
|
696
|
+
VALUE rval = rb_str_new( data.iov_base, data.iov_len );
|
697
|
+
rb_yield( rb_assoc_new( rkey, rmdbx_deserialize( self, rval ) ) );
|
698
|
+
|
699
|
+
while ( mdbx_cursor_get( db->cursor, &key, &data, MDBX_NEXT ) == MDBX_SUCCESS ) {
|
700
|
+
rkey = rb_str_new( key.iov_base, key.iov_len );
|
701
|
+
rval = rb_str_new( data.iov_base, data.iov_len );
|
702
|
+
rb_yield( rb_assoc_new( rkey, rmdbx_deserialize( self, rval ) ) );
|
622
703
|
}
|
623
|
-
xfree( prev_db );
|
624
704
|
}
|
625
705
|
|
626
706
|
return self;
|
627
707
|
}
|
628
708
|
|
629
709
|
|
710
|
+
/* call-seq:
|
711
|
+
* db.each_pair {|key, value| block } => self
|
712
|
+
*
|
713
|
+
* Calls the block once for each key and value, returning self.
|
714
|
+
* A transaction must be opened prior to use.
|
715
|
+
*/
|
716
|
+
VALUE
|
717
|
+
rmdbx_each_pair( VALUE self )
|
718
|
+
{
|
719
|
+
UNWRAP_DB( self, db );
|
720
|
+
int state;
|
721
|
+
|
722
|
+
CHECK_HANDLE();
|
723
|
+
rmdbx_open_cursor( db );
|
724
|
+
RETURN_ENUMERATOR( self, 0, 0 );
|
725
|
+
|
726
|
+
rb_protect( rmdbx_each_pair_i, self, &state );
|
727
|
+
|
728
|
+
mdbx_cursor_close( db->cursor );
|
729
|
+
db->cursor = NULL;
|
730
|
+
|
731
|
+
if ( state ) rb_jump_tag( state );
|
732
|
+
|
733
|
+
return self;
|
734
|
+
}
|
735
|
+
|
736
|
+
|
630
737
|
/*
|
631
738
|
* Open an existing (or create a new) mdbx database at filesystem
|
632
739
|
* +path+. In block form, the database is automatically closed.
|
@@ -666,57 +773,108 @@ rmdbx_database_initialize( int argc, VALUE *argv, VALUE self )
|
|
666
773
|
db->state.open = 0;
|
667
774
|
db->state.retain_txn = -1;
|
668
775
|
db->settings.env_flags = MDBX_ENV_DEFAULTS;
|
776
|
+
db->settings.db_flags = MDBX_DB_DEFAULTS | MDBX_CREATE;
|
669
777
|
db->settings.mode = 0644;
|
670
778
|
db->settings.max_collections = 0;
|
671
779
|
db->settings.max_readers = 0;
|
672
780
|
db->settings.max_size = 0;
|
673
781
|
|
674
|
-
/*
|
782
|
+
/* Set instance variables.
|
675
783
|
*/
|
676
|
-
|
677
|
-
|
678
|
-
|
784
|
+
rb_iv_set( self, "@path", path );
|
785
|
+
rb_iv_set( self, "@options", rb_hash_dup( opts ) );
|
786
|
+
|
787
|
+
/* Environment and database options setup, overrides.
|
788
|
+
*/
|
789
|
+
opt = rb_hash_delete( opts, ID2SYM( rb_intern("coalesce") ) );
|
790
|
+
if ( RTEST(opt) ) db->settings.env_flags = db->settings.env_flags | MDBX_COALESCE;
|
791
|
+
opt = rb_hash_delete( opts, ID2SYM( rb_intern("compatible") ) );
|
792
|
+
if ( RTEST(opt) ) {
|
793
|
+
db->settings.db_flags = db->settings.db_flags | MDBX_DB_ACCEDE;
|
794
|
+
db->settings.env_flags = db->settings.env_flags | MDBX_ACCEDE;
|
795
|
+
}
|
796
|
+
opt = rb_hash_delete( opts, ID2SYM( rb_intern("exclusive") ) );
|
797
|
+
if ( RTEST(opt) ) db->settings.env_flags = db->settings.env_flags | MDBX_EXCLUSIVE;
|
798
|
+
opt = rb_hash_delete( opts, ID2SYM( rb_intern("lifo_reclaim") ) );
|
799
|
+
if ( RTEST(opt) ) db->settings.env_flags = db->settings.env_flags | MDBX_LIFORECLAIM;
|
800
|
+
opt = rb_hash_delete( opts, ID2SYM( rb_intern("max_collections") ) );
|
679
801
|
if ( ! NIL_P(opt) ) db->settings.max_collections = FIX2INT( opt );
|
680
|
-
opt =
|
802
|
+
opt = rb_hash_delete( opts, ID2SYM( rb_intern("max_readers") ) );
|
681
803
|
if ( ! NIL_P(opt) ) db->settings.max_readers = FIX2INT( opt );
|
682
|
-
opt =
|
804
|
+
opt = rb_hash_delete( opts, ID2SYM( rb_intern("max_size") ) );
|
683
805
|
if ( ! NIL_P(opt) ) db->settings.max_size = NUM2LONG( opt );
|
684
|
-
opt =
|
806
|
+
opt = rb_hash_delete( opts, ID2SYM( rb_intern("mode") ) );
|
807
|
+
if ( ! NIL_P(opt) ) db->settings.mode = FIX2INT( opt );
|
808
|
+
opt = rb_hash_delete( opts, ID2SYM( rb_intern("no_memory_init") ) );
|
809
|
+
if ( RTEST(opt) ) db->settings.env_flags = db->settings.env_flags | MDBX_NOMEMINIT;
|
810
|
+
opt = rb_hash_delete( opts, ID2SYM( rb_intern("no_metasync") ) );
|
811
|
+
if ( RTEST(opt) ) db->settings.env_flags = db->settings.env_flags | MDBX_NOMETASYNC;
|
812
|
+
opt = rb_hash_delete( opts, ID2SYM( rb_intern("no_subdir") ) );
|
685
813
|
if ( RTEST(opt) ) db->settings.env_flags = db->settings.env_flags | MDBX_NOSUBDIR;
|
686
|
-
opt =
|
814
|
+
opt = rb_hash_delete( opts, ID2SYM( rb_intern("no_readahead") ) );
|
815
|
+
if ( RTEST(opt) ) db->settings.env_flags = db->settings.env_flags | MDBX_NORDAHEAD;
|
816
|
+
opt = rb_hash_delete( opts, ID2SYM( rb_intern("no_threadlocal") ) );
|
817
|
+
if ( RTEST(opt) ) db->settings.env_flags = db->settings.env_flags | MDBX_NOTLS;
|
818
|
+
opt = rb_hash_delete( opts, ID2SYM( rb_intern("readonly") ) );
|
687
819
|
if ( RTEST(opt) ) db->settings.env_flags = db->settings.env_flags | MDBX_RDONLY;
|
688
|
-
opt =
|
689
|
-
if ( RTEST(opt) ) db->settings.env_flags = db->settings.env_flags | MDBX_EXCLUSIVE;
|
690
|
-
opt = rb_hash_aref( opts, ID2SYM( rb_intern("compat") ) );
|
691
|
-
if ( RTEST(opt) ) db->settings.env_flags = db->settings.env_flags | MDBX_ACCEDE;
|
692
|
-
opt = rb_hash_aref( opts, ID2SYM( rb_intern("writemap") ) );
|
820
|
+
opt = rb_hash_delete( opts, ID2SYM( rb_intern("writemap") ) );
|
693
821
|
if ( RTEST(opt) ) db->settings.env_flags = db->settings.env_flags | MDBX_WRITEMAP;
|
694
|
-
opt = rb_hash_aref( opts, ID2SYM( rb_intern("no_threadlocal") ) );
|
695
|
-
if ( RTEST(opt) ) db->settings.env_flags = db->settings.env_flags | MDBX_NOTLS;
|
696
|
-
opt = rb_hash_aref( opts, ID2SYM( rb_intern("no_readahead") ) );
|
697
|
-
if ( RTEST(opt) ) db->settings.env_flags = db->settings.env_flags | MDBX_NORDAHEAD;
|
698
|
-
opt = rb_hash_aref( opts, ID2SYM( rb_intern("no_memory_init") ) );
|
699
|
-
if ( RTEST(opt) ) db->settings.env_flags = db->settings.env_flags | MDBX_NOMEMINIT;
|
700
|
-
opt = rb_hash_aref( opts, ID2SYM( rb_intern("coalesce") ) );
|
701
|
-
if ( RTEST(opt) ) db->settings.env_flags = db->settings.env_flags | MDBX_COALESCE;
|
702
|
-
opt = rb_hash_aref( opts, ID2SYM( rb_intern("lifo_reclaim") ) );
|
703
|
-
if ( RTEST(opt) ) db->settings.env_flags = db->settings.env_flags | MDBX_LIFORECLAIM;
|
704
|
-
opt = rb_hash_aref( opts, ID2SYM( rb_intern("no_metasync") ) );
|
705
|
-
if ( RTEST(opt) ) db->settings.env_flags = db->settings.env_flags | MDBX_NOMETASYNC;
|
706
822
|
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
/* Set instance variables.
|
711
|
-
*/
|
712
|
-
rb_iv_set( self, "@path", path );
|
713
|
-
rb_iv_set( self, "@options", opts );
|
823
|
+
if ( rb_hash_size_num(opts) > 0 ) {
|
824
|
+
rb_raise( rb_eArgError, "Unknown option(s): %"PRIsVALUE, opts );
|
825
|
+
}
|
714
826
|
|
715
827
|
rmdbx_open_env( self );
|
716
828
|
return self;
|
717
829
|
}
|
718
830
|
|
719
831
|
|
832
|
+
/*
|
833
|
+
* call-seq:
|
834
|
+
* db.statistics => (hash of stats)
|
835
|
+
*
|
836
|
+
* Returns a hash populated with various metadata for the opened
|
837
|
+
* database.
|
838
|
+
*
|
839
|
+
*/
|
840
|
+
VALUE
|
841
|
+
rmdbx_stats( VALUE self )
|
842
|
+
{
|
843
|
+
UNWRAP_DB( self, db );
|
844
|
+
CHECK_HANDLE();
|
845
|
+
|
846
|
+
return rmdbx_gather_stats( db );
|
847
|
+
}
|
848
|
+
|
849
|
+
|
850
|
+
/*
|
851
|
+
* call-seq:
|
852
|
+
* db.clone => [copy of db]
|
853
|
+
*
|
854
|
+
* Copy the object (clone/dup). The returned copy is closed and needs
|
855
|
+
* to be reopened before use. This function likely has limited use,
|
856
|
+
* considering you can't open two handles within the same process.
|
857
|
+
*/
|
858
|
+
static VALUE rmdbx_init_copy( VALUE copy, VALUE orig )
|
859
|
+
{
|
860
|
+
rmdbx_db_t *orig_db;
|
861
|
+
rmdbx_db_t *copy_db;
|
862
|
+
|
863
|
+
if ( copy == orig ) return copy;
|
864
|
+
|
865
|
+
TypedData_Get_Struct( orig, rmdbx_db_t, &rmdbx_db_data, orig_db );
|
866
|
+
TypedData_Get_Struct( copy, rmdbx_db_t, &rmdbx_db_data, copy_db );
|
867
|
+
|
868
|
+
/* Copy all fields from the original to the copy, and force-close
|
869
|
+
the copy.
|
870
|
+
*/
|
871
|
+
MEMCPY( copy_db, orig_db, rmdbx_db_t, 1 );
|
872
|
+
rmdbx_close_all( copy_db );
|
873
|
+
|
874
|
+
return copy;
|
875
|
+
}
|
876
|
+
|
877
|
+
|
720
878
|
/*
|
721
879
|
* Initialization for the MDBX::Database class.
|
722
880
|
*/
|
@@ -732,25 +890,35 @@ rmdbx_init_database()
|
|
732
890
|
rb_define_alloc_func( rmdbx_cDatabase, rmdbx_alloc );
|
733
891
|
|
734
892
|
rb_define_protected_method( rmdbx_cDatabase, "initialize", rmdbx_database_initialize, -1 );
|
735
|
-
|
893
|
+
rb_define_protected_method( rmdbx_cDatabase, "initialize_copy", rmdbx_init_copy, 1 );
|
894
|
+
|
895
|
+
rb_define_method( rmdbx_cDatabase, "clear", rmdbx_clear, 0 );
|
736
896
|
rb_define_method( rmdbx_cDatabase, "close", rmdbx_close, 0 );
|
737
|
-
rb_define_method( rmdbx_cDatabase, "reopen", rmdbx_open_env, 0 );
|
738
897
|
rb_define_method( rmdbx_cDatabase, "closed?", rmdbx_closed_p, 0 );
|
739
|
-
rb_define_method( rmdbx_cDatabase, "
|
740
|
-
rb_define_method( rmdbx_cDatabase, "
|
741
|
-
rb_define_method( rmdbx_cDatabase, "each_key", rmdbx_each_key, 0 );
|
742
|
-
rb_define_method( rmdbx_cDatabase, "each_value", rmdbx_each_value, 0 );
|
743
|
-
rb_define_method( rmdbx_cDatabase, "each_pair", rmdbx_each_pair, 0 );
|
898
|
+
rb_define_method( rmdbx_cDatabase, "drop", rmdbx_drop, 1 );
|
899
|
+
rb_define_method( rmdbx_cDatabase, "include?", rmdbx_include, 1 );
|
744
900
|
rb_define_method( rmdbx_cDatabase, "length", rmdbx_length, 0 );
|
901
|
+
rb_define_method( rmdbx_cDatabase, "reopen", rmdbx_open_env, 0 );
|
745
902
|
rb_define_method( rmdbx_cDatabase, "[]", rmdbx_get_val, 1 );
|
746
903
|
rb_define_method( rmdbx_cDatabase, "[]=", rmdbx_put_val, 2 );
|
747
904
|
|
905
|
+
/* Enumerables */
|
906
|
+
rb_define_method( rmdbx_cDatabase, "each_key", rmdbx_each_key, 0 );
|
907
|
+
rb_define_method( rmdbx_cDatabase, "each_pair", rmdbx_each_pair, 0 );
|
908
|
+
rb_define_method( rmdbx_cDatabase, "each_value", rmdbx_each_value, 0 );
|
909
|
+
|
748
910
|
/* Manually open/close transactions from ruby. */
|
911
|
+
rb_define_method( rmdbx_cDatabase, "in_transaction?", rmdbx_in_transaction_p, 0 );
|
749
912
|
rb_define_protected_method( rmdbx_cDatabase, "open_transaction", rmdbx_rb_opentxn, 1 );
|
750
913
|
rb_define_protected_method( rmdbx_cDatabase, "close_transaction", rmdbx_rb_closetxn, 1 );
|
751
914
|
|
915
|
+
/* Collection functions */
|
916
|
+
rb_define_protected_method( rmdbx_cDatabase, "get_subdb", rmdbx_get_subdb, 0 );
|
917
|
+
rb_define_protected_method( rmdbx_cDatabase, "set_subdb", rmdbx_set_subdb, 1 );
|
918
|
+
|
752
919
|
rb_define_protected_method( rmdbx_cDatabase, "raw_stats", rmdbx_stats, 0 );
|
753
920
|
|
754
921
|
rb_require( "mdbx/database" );
|
755
922
|
}
|
756
923
|
|
924
|
+
|