mattbauer-bdb 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2008 Dan Janowski <danj@3skel.com>, Matt Bauer <bauer@pedalbrain.com>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
@@ -0,0 +1,83 @@
1
+ h1. Bdb
2
+
3
+ Ruby bindings for Berkeley DB versions 4.2-4.6.
4
+
5
+ h2. Download
6
+
7
+ Currently this library is available via git at:
8
+
9
+ git://github.com/mattbauer/bdb.git
10
+
11
+ h2. Installation
12
+
13
+ h3. From Git
14
+
15
+ You can check out the latest source from git:
16
+
17
+ > git clone git://github.com/mattbauer/bdb.git
18
+
19
+ h3. As a Gem
20
+
21
+ At the moment this library is not available on Rubyforge. To install it as a
22
+ gem, do the following:
23
+
24
+ > sudo env ARCHFLAGS="-arch i386" gem install mattbauer-bdb --source http://gems.github.com -- --with-db-dir=/usr/local/BerkeleyDB.4.7
25
+
26
+ This assumes you're on OS X and BerkeleyDB wasn't compiled as a universal binary.
27
+
28
+ h2. Sample Usage
29
+
30
+ <pre>
31
+ env = Bdb::Env.new(0)
32
+ env_flags = Bdb::DB_CREATE | # Create the environment if it does not already exist.
33
+ Bdb::DB_INIT_TXN | # Initialize transactions
34
+ Bdb::DB_INIT_LOCK | # Initialize locking.
35
+ Bdb::DB_INIT_LOG | # Initialize logging
36
+ Bdb::DB_INIT_MPOOL # Initialize the in-memory cache.
37
+ env.open(File.join(File.dirname(__FILE__), 'tmp'), env_flags, 0);
38
+
39
+ db = env.db
40
+ db.open(nil, 'db1.db', nil, Bdb::Db::BTREE, Bdb::DB_CREATE | Bdb::DB_AUTO_COMMIT, 0)
41
+
42
+ txn = env.txn_begin(nil, 0)
43
+ db.put(txn, 'key', 'value', 0)
44
+ txn.commit(0)
45
+
46
+ value = db.get(nil, 'key', nil, 0)
47
+
48
+ db.close(0)
49
+ env.close
50
+ </pre>
51
+
52
+ h2. API
53
+
54
+ This interface is most closely based on the DB4 C api and tries to maintain close
55
+ interface proximity. That API is published by Oracle at "http://www.oracle.com/technology/documentation/berkeley-db/db/api_c/frame.html":http://www.oracle.com/technology/documentation/berkeley-db/db/api_c/frame.html.
56
+
57
+ All function arguments systematically omit the leading DB handles and TXN handles.
58
+ A few calls omit the flags parameter when the documentation indicates that no
59
+ flag values are used - cursor.close is one.
60
+
61
+ h2. Notes
62
+
63
+ The defines generator is imperfect and includes some defines that are not
64
+ flags. While it could be improved, it is easier to delete the incorrect ones.
65
+ Thus, if you decide to rebuild the defines, you will need to edit the resulting
66
+ file. This may be necessary if using a different release of DB4 than the ones
67
+ the authors developed against. In nearly every case the defines generator works
68
+ flawlessly.
69
+
70
+ The authors have put all possible caution into ensuring that DB and Ruby cooperate.
71
+ The memory access was one aspect carefully considered. Since Ruby copies
72
+ when doing String#new, all key/data retrieval from DB is done with a 0 flag,
73
+ meaning that DB will be responsible. See "this":http://groups.google.com/group/comp.databases.berkeley-db/browse_frm/thread/4f70a9999b64ce6a/c06b94692e3cbc41?tvc=1&q=dbt+malloc#c06b94692e3cbc41
74
+ news group posting about the effect of that.
75
+
76
+ The only other design consideration of consequence was associate. The prior
77
+ version used a Ruby thread local variable and kept track of the current
78
+ database in use. The authors decided to take a simpler approach since Ruby is green
79
+ threads. A global array stores the VALUE of the Proc for a given association
80
+ by the file descriptor number of the underlying database. This is looked
81
+ up when the first layer callback is made. It would have been better considered
82
+ if DB allowed the passing of a (void *) user data into the alloc that would
83
+ be supplied during callback. So far this design has not produced any problems.
@@ -0,0 +1,41 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+ require 'rake/rdoctask'
4
+ require 'rake/testtask'
5
+
6
+ load 'bdb.gemspec'
7
+
8
+ Rake::GemPackageTask.new(BDB_SPEC) do |pkg|
9
+ pkg.need_tar = true
10
+ end
11
+
12
+ task :default => "test"
13
+
14
+ desc "Clean"
15
+ task :clean do
16
+ include FileUtils
17
+ Dir.chdir('ext') do
18
+ rm(Dir.glob('*') - ['bdb.c', 'bdb.h', 'extconf.rb'])
19
+ end
20
+ rm_rf 'pkg'
21
+ end
22
+
23
+ desc "Run tests"
24
+ Rake::TestTask.new("test") do |t|
25
+ t.libs << ["test", "ext"]
26
+ t.pattern = 'test/*_test.rb'
27
+ t.verbose = true
28
+ t.warning = true
29
+ end
30
+
31
+ task :doc => [:rdoc]
32
+ namespace :doc do
33
+ Rake::RDocTask.new do |rdoc|
34
+ files = ["README", "lib/**/*.rb"]
35
+ rdoc.rdoc_files.add(files)
36
+ rdoc.main = "README.textile"
37
+ rdoc.title = "Bdb Docs"
38
+ rdoc.rdoc_dir = "doc"
39
+ rdoc.options << "--line-numbers" << "--inline-source"
40
+ end
41
+ end
@@ -0,0 +1,28 @@
1
+ BDB_SPEC = Gem::Specification.new do |s|
2
+ s.platform = Gem::Platform::RUBY
3
+ s.required_ruby_version = '>=1.8.4'
4
+ s.name = "bdb"
5
+ s.version = "0.0.1"
6
+ s.authors = ["Matt Bauer", "Dan Janowski"]
7
+ s.email = "bauer@pedalbrain.com"
8
+ s.summary = "A Ruby interface to BerkeleyDB"
9
+ s.files = ['bdb.gemspec',
10
+ 'ext/bdb.c',
11
+ 'ext/bdb.h',
12
+ 'ext/extconf.rb',
13
+ 'LICENSE',
14
+ 'README.textile',
15
+ 'Rakefile']
16
+ s.test_files = ['test/cursor_test.rb',
17
+ 'test/db_test.rb',
18
+ 'test/env_test.rb',
19
+ 'test/stat_test.rb',
20
+ 'test/test_helper.rb',
21
+ 'test/txn_test.rb']
22
+ s.extensions = ["ext/extconf.rb"]
23
+
24
+ s.homepage = "http://github.com/mattbauer/bdb"
25
+
26
+ s.require_paths = ["lib", "ext"]
27
+ s.has_rdoc = false
28
+ end
@@ -0,0 +1,2855 @@
1
+ /*
2
+ * Ruby library that wraps the Sleepycat Berkeley DB.
3
+ *
4
+ * Developed against 4.3/4.4. No support for prior versions.
5
+ *
6
+ */
7
+
8
+ #include <bdb.h>
9
+ #include <stdio.h>
10
+
11
+ #define LMEMFLAG 0
12
+ #define NOFLAGS 0
13
+ #undef DEBUG_DB
14
+
15
+ #ifdef HAVE_STDARG_PROTOTYPES
16
+ #include <stdarg.h>
17
+ #define va_init_list(a,b) va_start(a,b)
18
+ #else
19
+ #include <varargs.h>
20
+ #define va_init_list(a,b) va_start(a)
21
+ #endif
22
+
23
+ VALUE mBdb; /* Top level module */
24
+ VALUE cDb; /* DBT class */
25
+ VALUE cDbStat; /* db status class, not specialized for DBTYPE */
26
+ VALUE cEnv; /* Environment class */
27
+ VALUE cTxn; /* Transaction class */
28
+ VALUE cCursor; /* Cursors */
29
+ VALUE cTxnStat; /* Transaction Status class */
30
+ VALUE cTxnStatActive; /* Active Transaction Status class */
31
+ VALUE eDbError;
32
+
33
+ static ID fv_call, fv_err_new,fv_err_code,fv_err_msg;
34
+
35
+ /*
36
+ * Document-class: Bdb::DbError
37
+ *
38
+ * Errors generated by methods under the Bdb hierarchy will be
39
+ * of this class unless Ruby itself raises the error.
40
+ *
41
+ */
42
+
43
+ static void
44
+ #ifdef HAVE_STDARG_PROTOTYPES
45
+ raise_error(int code, const char *fmt, ...)
46
+ #else
47
+ raise_error(code,fmt,va_alist)
48
+ int code;
49
+ const char *fmt;
50
+ va_dcl
51
+ #endif
52
+ {
53
+ va_list args;
54
+ char buf[1024];
55
+ VALUE exc;
56
+ VALUE argv[2];
57
+
58
+ va_init_list(args,fmt);
59
+ vsnprintf(buf,1024,fmt,args);
60
+ va_end(args);
61
+
62
+ argv[0]=rb_str_new2(buf);
63
+ argv[1]=INT2NUM(code);
64
+
65
+ exc=rb_class_new_instance(2,argv,eDbError);
66
+ rb_exc_raise(exc);
67
+ }
68
+
69
+ /*
70
+ * An error can only be generated internally
71
+ */
72
+ VALUE err_initialize(VALUE obj, VALUE message, VALUE code)
73
+ {
74
+ VALUE args[1];
75
+ args[0]=message;
76
+ rb_call_super(1,args);
77
+ return rb_ivar_set(obj,fv_err_code,code);
78
+ }
79
+
80
+ /*
81
+ * call-seq:
82
+ * err.code() -> Bdb error code integer
83
+ *
84
+ */
85
+ VALUE err_code(VALUE obj)
86
+ {
87
+ return rb_ivar_get(obj,fv_err_code);
88
+ }
89
+
90
+ static void db_free(t_dbh *dbh)
91
+ {
92
+ #ifdef DEBUG_DB
93
+ if ( RTEST(ruby_debug) )
94
+ fprintf(stderr,"%s/%d %s 0x%x\n",__FILE__,__LINE__,"db_free cleanup!",dbh);
95
+ #endif
96
+
97
+ if (dbh) {
98
+ if (dbh->db) {
99
+ if (dbh->db_opened == 1)
100
+ dbh->db->close(dbh->db,NOFLAGS);
101
+ if ( RTEST(ruby_debug) && dbh->filename[0] != '\0')
102
+ fprintf(stderr,"%s/%d %s %p %s\n",__FILE__,__LINE__,
103
+ "db_free database was still open!",dbh->db,dbh->filename);
104
+ dbh->db=NULL;
105
+ }
106
+ free(dbh);
107
+ }
108
+ }
109
+
110
+ static void db_mark(t_dbh *dbh)
111
+ {
112
+ if ( dbh == NULL ) return;
113
+ if ( ! NIL_P(dbh->aproc) )
114
+ rb_gc_mark(dbh->aproc);
115
+ if ( dbh->env )
116
+ rb_gc_mark(dbh->env->self);
117
+ if ( ! NIL_P(dbh->adbc) )
118
+ rb_gc_mark(dbh->adbc);
119
+ }
120
+
121
+ static void dbc_mark(t_dbch *dbch)
122
+ {
123
+ if (dbch->db)
124
+ rb_gc_mark(dbch->db->self);
125
+ }
126
+ static void dbc_free(void *p)
127
+ {
128
+ t_dbch *dbch;
129
+ dbch=(t_dbch *)p;
130
+
131
+ #ifdef DEBUG_DB
132
+ if ( RTEST(ruby_debug) )
133
+ fprintf(stderr,"%s/%d %s 0x%x\n",__FILE__,__LINE__,
134
+ "dbc_free cleanup!",p);
135
+ #endif
136
+
137
+ if ( dbch ) {
138
+ if ( dbch->dbc ) {
139
+ dbch->dbc->c_close(dbch->dbc);
140
+ if ( RTEST(ruby_debug) )
141
+ fprintf(stderr,"%s/%d %s %p %s\n",__FILE__,__LINE__,
142
+ "dbc_free cursor was still open!",p,dbch->filename);
143
+ }
144
+ free(p);
145
+ }
146
+ }
147
+
148
+ VALUE
149
+ db_alloc(VALUE klass)
150
+ {
151
+ return Data_Wrap_Struct(klass,db_mark,db_free,0);
152
+ }
153
+
154
+ VALUE db_init_aux(VALUE obj,t_envh * eh)
155
+ {
156
+ DB *db;
157
+ t_dbh *dbh;
158
+ int rv;
159
+
160
+ /* This excludes possible use of X/Open Transaction Mgr */
161
+ rv = db_create(&db,(eh)?eh->env:NULL,NOFLAGS);
162
+ if (rv != 0) {
163
+ raise_error(rv, "db_new failure: %s",db_strerror(rv));
164
+ }
165
+
166
+ #ifdef DEBUG_DB
167
+ db->set_errfile(db,stderr);
168
+ #endif
169
+
170
+ dbh=ALLOC(t_dbh);
171
+ if (DATA_PTR(obj)) {
172
+ /* if called from env_db, the data ptr has not been allocated,
173
+ * was freeing 0x0 */
174
+ db_free(DATA_PTR(obj));
175
+ }
176
+ DATA_PTR(obj)=dbh;
177
+ dbh->db=db;
178
+ dbh->self=obj;
179
+ dbh->env=eh;
180
+ dbh->aproc=Qnil;
181
+ memset(&(dbh->filename),0,FNLEN+1);
182
+
183
+ dbh->adbc=Qnil;
184
+
185
+ if (dbh->env) {
186
+ #ifdef DEBUG_DB
187
+ fprintf(stderr,"Adding db to env 0x%x 0x%x\n",obj,dbh);
188
+ #endif
189
+ rb_ary_push(dbh->env->adb,obj);
190
+ }
191
+
192
+ return obj;
193
+ }
194
+
195
+ /*
196
+ * Document-class: Bdb::Db
197
+ *
198
+ */
199
+
200
+ VALUE db_initialize(VALUE obj)
201
+ {
202
+ return db_init_aux(obj,NULL);
203
+ }
204
+
205
+ /*
206
+ * call-seq:
207
+ * db.open(txn_object,disk_file,logical_db,db_type,flags,mode) -> value
208
+ *
209
+ * open a database. disk file is file path. logical_db is
210
+ * a named database within that file, which will be created under
211
+ * conditions noted by DB.
212
+ *
213
+ * db_type is one of the constants:
214
+ * Bdb::DB::BTREE
215
+ * Bdb::DB::HASH
216
+ * Bdb::DB::RECNO
217
+ * Bdb::DB::QUEUE
218
+ * Bdb::DB::UNKNOWN
219
+ *
220
+ * unknown will open an already existing db in the mode created
221
+ */
222
+ VALUE db_open(VALUE obj, VALUE vtxn, VALUE vdisk_file,
223
+ VALUE vlogical_db,
224
+ VALUE vdbtype, VALUE vflags, VALUE vmode)
225
+ {
226
+ t_dbh *dbh;
227
+ int rv;
228
+ t_txnh *txn=NOTXN;
229
+ u_int32_t flags=0;
230
+ DBTYPE dbtype=DB_UNKNOWN;
231
+ char *logical_db=NULL;
232
+ long len;
233
+ int mode=0;
234
+
235
+ if ( ! NIL_P(vflags) )
236
+ flags=NUM2UINT(vflags);
237
+
238
+ if ( ! NIL_P(vtxn) ) {
239
+ Data_Get_Struct(vtxn,t_txnh,txn);
240
+ if (!txn->txn)
241
+ raise(0, "txn is closed");
242
+ }
243
+
244
+ if ( TYPE(vlogical_db)==T_STRING && RSTRING_LEN(vlogical_db) > 0 )
245
+ logical_db=StringValueCStr(vlogical_db);
246
+
247
+ if ( FIXNUM_P(vdbtype) ) {
248
+ dbtype=NUM2INT(vdbtype);
249
+ if ( dbtype < DB_BTREE || dbtype > DB_UNKNOWN ) {
250
+ raise_error(0,"db_open Bad access type: %d",dbtype);
251
+ return Qnil;
252
+ }
253
+ }
254
+
255
+ if ( TYPE(vdisk_file)!=T_STRING || RSTRING_LEN(vdisk_file) < 1 ) {
256
+ raise_error(0,"db_open Bad disk file name");
257
+ return Qnil;
258
+ }
259
+
260
+ if ( ! NIL_P(vmode) )
261
+ mode=NUM2INT(vmode);
262
+
263
+ Data_Get_Struct(obj,t_dbh,dbh);
264
+ if ( ! NIL_P(dbh->adbc) )
265
+ raise_error(0,"db handle already opened");
266
+
267
+ rv = dbh->db->open(dbh->db,txn?txn->txn:NULL,
268
+ StringValueCStr(vdisk_file),
269
+ logical_db,
270
+ dbtype,flags,mode);
271
+ if (rv != 0) {
272
+ raise_error(rv,"db_open failure: %s(%d)",db_strerror(rv),rv);
273
+ }
274
+ filename_copy(dbh->filename,vdisk_file)
275
+ dbh->adbc=rb_ary_new();
276
+ dbh->db_opened = 1;
277
+ return obj;
278
+ }
279
+
280
+ /*
281
+ * call-seq:
282
+ * db.flags=value
283
+ *
284
+ * set database flags based on DB constants.
285
+ * see http://www.sleepycat.com/docs/api_c/db_set_flags.html
286
+ *
287
+ */
288
+ VALUE db_flags_set(VALUE obj, VALUE vflags)
289
+ {
290
+ t_dbh *dbh;
291
+ int rv;
292
+ u_int32_t flags;
293
+
294
+ flags=NUM2UINT(vflags);
295
+ Data_Get_Struct(obj,t_dbh,dbh);
296
+ if (!dbh->db)
297
+ raise_error(0,"db is closed");
298
+
299
+ rv = dbh->db->set_flags(dbh->db,flags);
300
+ if ( rv != 0 ) {
301
+ raise_error(rv, "db_flag_set failure: %s",db_strerror(rv));
302
+ }
303
+ return vflags;
304
+ }
305
+
306
+ /*
307
+ * call-seq:
308
+ * db.flags -> value
309
+ *
310
+ * get database flags.
311
+ * see http://www.sleepycat.com/docs/api_c/db_get_flags.html
312
+ *
313
+ */
314
+ VALUE db_flags_get(VALUE obj)
315
+ {
316
+ t_dbh *dbh;
317
+ int rv;
318
+ u_int32_t flags;
319
+
320
+ Data_Get_Struct(obj,t_dbh,dbh);
321
+ if (!dbh->db)
322
+ raise_error(0,"db is closed");
323
+
324
+ rv = dbh->db->get_flags(dbh->db,&flags);
325
+ if ( rv != 0 ) {
326
+ raise_error(rv, "db_flag_get failure: %s",db_strerror(rv));
327
+ }
328
+ return INT2NUM(flags);
329
+ }
330
+
331
+ /*
332
+ * call-seq:
333
+ * db.pagesize=value
334
+ *
335
+ * set database flags based on DB constants.
336
+ * see http://www.sleepycat.com/docs/api_c/db_set_flags.html
337
+ *
338
+ */
339
+ VALUE db_pagesize_set(VALUE obj, VALUE vpagesize)
340
+ {
341
+ t_dbh *dbh;
342
+ int rv;
343
+ u_int32_t pagesize;
344
+
345
+ pagesize=NUM2INT(vpagesize);
346
+ Data_Get_Struct(obj,t_dbh,dbh);
347
+ if (!dbh->db)
348
+ raise_error(0,"db is closed");
349
+ rv = dbh->db->set_pagesize(dbh->db,pagesize);
350
+ if ( rv != 0 ) {
351
+ raise_error(rv, "db_pagesize_set failure: %s",db_strerror(rv));
352
+ }
353
+ return vpagesize;
354
+ }
355
+
356
+ /*
357
+ * call-seq:
358
+ * db.pagesize
359
+ *
360
+ * set database flags based on DB constants.
361
+ * see http://www.sleepycat.com/docs/api_c/db_set_flags.html
362
+ *
363
+ */
364
+ VALUE db_pagesize(VALUE obj)
365
+ {
366
+ t_dbh *dbh;
367
+ int rv;
368
+ u_int32_t pagesize;
369
+
370
+ Data_Get_Struct(obj,t_dbh,dbh);
371
+ if (!dbh->db)
372
+ raise_error(0,"db is closed");
373
+ rv = dbh->db->get_pagesize(dbh->db,&pagesize);
374
+ if ( rv != 0 ) {
375
+ raise_error(rv, "db_pagesize_get failure: %s",db_strerror(rv));
376
+ }
377
+ return INT2NUM(pagesize);
378
+ }
379
+
380
+ /*
381
+ * call-seq:
382
+ * db.h_ffactor=value
383
+ *
384
+ * get hash db fill factor
385
+ * formula:
386
+ * (pagesize - 32) / (average_key_size + average_data_size + 8)
387
+ *
388
+ */
389
+ VALUE db_h_ffactor_set(VALUE obj, VALUE vint)
390
+ {
391
+ t_dbh *dbh;
392
+ int rv;
393
+ u_int32_t cint;
394
+
395
+ cint=NUM2INT(vint);
396
+ Data_Get_Struct(obj,t_dbh,dbh);
397
+ if (!dbh->db)
398
+ raise_error(0,"db is closed");
399
+ rv = dbh->db->set_h_ffactor(dbh->db,cint);
400
+ if ( rv != 0 ) {
401
+ raise_error(rv, "db_h_ffactor_set failure: %s",db_strerror(rv));
402
+ }
403
+ return vint;
404
+ }
405
+
406
+ /*
407
+ * call-seq:
408
+ * db.h_ffactor
409
+ *
410
+ * get hash db fill factor
411
+ * formula:
412
+ * (pagesize - 32) / (average_key_size + average_data_size + 8)
413
+ * see http://www.sleepycat.com/docs/api_c/db_set_flags.html
414
+ *
415
+ */
416
+ VALUE db_h_ffactor(VALUE obj)
417
+ {
418
+ t_dbh *dbh;
419
+ int rv;
420
+ u_int32_t cint;
421
+
422
+ Data_Get_Struct(obj,t_dbh,dbh);
423
+ if (!dbh->db)
424
+ raise_error(0,"db is closed");
425
+ rv = dbh->db->get_h_ffactor(dbh->db,&cint);
426
+ if ( rv != 0 ) {
427
+ raise_error(rv, "db_h_ffactor failure: %s",db_strerror(rv));
428
+ }
429
+ return INT2NUM(cint);
430
+ }
431
+ /*
432
+ * call-seq:
433
+ * db.h_nelem=value
434
+ *
435
+ * set estimate number of elements in hash table
436
+ * see http://www.sleepycat.com/docs/api_c/db_set_flags.html
437
+ *
438
+ */
439
+ VALUE db_h_nelem_set(VALUE obj, VALUE vint)
440
+ {
441
+ t_dbh *dbh;
442
+ int rv;
443
+ u_int32_t cint;
444
+
445
+ cint=NUM2INT(vint);
446
+ Data_Get_Struct(obj,t_dbh,dbh);
447
+ if (!dbh->db)
448
+ raise_error(0,"db is closed");
449
+ rv = dbh->db->set_h_nelem(dbh->db,cint);
450
+ if ( rv != 0 ) {
451
+ raise_error(rv, "db_h_nelem_set failure: %s",db_strerror(rv));
452
+ }
453
+ return vint;
454
+ }
455
+
456
+ /*
457
+ * call-seq:
458
+ * db.h_nelem
459
+ *
460
+ * get estimate number of element in the hash table
461
+ * see http://www.sleepycat.com/docs/api_c/db_set_flags.html
462
+ *
463
+ */
464
+ VALUE db_h_nelem(VALUE obj)
465
+ {
466
+ t_dbh *dbh;
467
+ int rv;
468
+ u_int32_t nelem;
469
+
470
+ Data_Get_Struct(obj,t_dbh,dbh);
471
+ if (!dbh->db)
472
+ raise_error(0,"db is closed");
473
+ rv = dbh->db->get_h_nelem(dbh->db,&nelem);
474
+ if ( rv != 0 ) {
475
+ raise_error(rv, "db_h_nelem failure: %s",db_strerror(rv));
476
+ }
477
+ return INT2NUM(nelem);
478
+ }
479
+
480
+ VALUE dbc_close(VALUE);
481
+
482
+ /* call-seq:
483
+ * db.close(flags) -> value
484
+ *
485
+ * close a database handle. Will close open cursors.
486
+ */
487
+ VALUE db_close(VALUE obj, VALUE vflags)
488
+ {
489
+ t_dbh *dbh;
490
+ int rv;
491
+ u_int32_t flags;
492
+ VALUE cur;
493
+
494
+ flags=NUM2UINT(vflags);
495
+ Data_Get_Struct(obj,t_dbh,dbh);
496
+ if ( dbh->db==NULL )
497
+ return Qnil;
498
+
499
+ if (! NIL_P(dbh->adbc) && RARRAY(dbh->adbc)->len > 0 ) {
500
+ rb_warning("%s/%d %s",__FILE__,__LINE__,
501
+ "cursor handles still open");
502
+ while ( (cur=rb_ary_pop(dbh->adbc)) != Qnil ) {
503
+ dbc_close(cur);
504
+ }
505
+ }
506
+
507
+ if ( RTEST(ruby_debug) )
508
+ rb_warning("%s/%d %s 0x%x %s",__FILE__,__LINE__,"db_close!",dbh,
509
+ (dbh->filename==NULL||*(dbh->filename)=='0') ? "unknown" : dbh->filename);
510
+
511
+ rv = dbh->db->close(dbh->db,flags);
512
+ dbh->db=NULL;
513
+ dbh->aproc=Qnil;
514
+ if ( dbh->env ) {
515
+ if ( RTEST(ruby_debug) )
516
+ rb_warning("%s/%d %s 0x%x",__FILE__,__LINE__,"db_close! removing",obj);
517
+ rb_ary_delete(dbh->env->adb,obj);
518
+ dbh->env = NULL;
519
+ }
520
+ if ( rv != 0 ) {
521
+ raise_error(rv, "db_close failure: %s",db_strerror(rv));
522
+ }
523
+ dbh->db_opened = 0;
524
+ return obj;
525
+ }
526
+
527
+ /*
528
+ * call-seq:
529
+ * db.put(txn,key,data,flags) -> self
530
+ *
531
+ * put a key/data pair into the database. returns db. Will
532
+ * raise an error on DB_KEYEXIST but error.code will indicate
533
+ * so it can be easily caught.
534
+ */
535
+ VALUE db_put(VALUE obj, VALUE vtxn, VALUE vkey, VALUE vdata, VALUE vflags)
536
+ {
537
+ t_dbh *dbh;
538
+ int rv;
539
+ u_int32_t flags=0;
540
+ DBT key,data;
541
+ t_txnh *txn=NULL;
542
+
543
+ memset(&key,0,sizeof(DBT));
544
+ memset(&data,0,sizeof(DBT));
545
+
546
+ if ( ! NIL_P(vtxn) ) {
547
+ Data_Get_Struct(vtxn,t_txnh,txn);
548
+ if (!txn->txn)
549
+ raise(0, "txn is closed");
550
+ }
551
+
552
+ if ( ! NIL_P(vflags) )
553
+ flags=NUM2UINT(vflags);
554
+
555
+ Data_Get_Struct(obj,t_dbh,dbh);
556
+ if (!dbh->db)
557
+ raise_error(0,"db is closed");
558
+
559
+ StringValue(vkey);
560
+ key.data = RSTRING_PTR(vkey);
561
+ key.size = RSTRING_LEN(vkey);
562
+ key.flags = LMEMFLAG;
563
+
564
+ StringValue(vdata);
565
+ data.data = RSTRING_PTR(vdata);
566
+ data.size = RSTRING_LEN(vdata);
567
+ data.flags = LMEMFLAG;
568
+
569
+ rv = dbh->db->put(dbh->db,txn?txn->txn:NULL,&key,&data,flags);
570
+ /*
571
+ if (rv == DB_KEYEXIST)
572
+ return Qnil;
573
+ */
574
+ if (rv != 0) {
575
+ raise_error(rv, "db_put fails: %s",db_strerror(rv));
576
+ }
577
+ return obj;
578
+ }
579
+
580
+ /*
581
+ * call-seq:
582
+ * db.get(txn,key,data,flags) -> String(data)
583
+ *
584
+ * get a key/data pair from database. data as a string.
585
+ *
586
+ */
587
+
588
+ VALUE db_get(VALUE obj, VALUE vtxn, VALUE vkey, VALUE vdata, VALUE vflags)
589
+ {
590
+ t_dbh *dbh;
591
+ int rv;
592
+ u_int32_t flags=0;
593
+ DBT key,data;
594
+ VALUE str;
595
+ t_txnh *txn=NULL;
596
+
597
+ memset(&key,0,sizeof(DBT));
598
+ memset(&data,0,sizeof(DBT));
599
+
600
+ if ( ! NIL_P(vtxn) ) {
601
+ Data_Get_Struct(vtxn,t_txnh,txn);
602
+ if (!txn->txn)
603
+ raise(0, "txn is closed");
604
+ }
605
+
606
+ if ( ! NIL_P(vflags) ) {
607
+ flags=NUM2UINT(vflags);
608
+ }
609
+
610
+ Data_Get_Struct(obj,t_dbh,dbh);
611
+ if (!dbh->db)
612
+ raise_error(0,"db is closed");
613
+
614
+ StringValue(vkey);
615
+
616
+ key.data = RSTRING_PTR(vkey);
617
+ key.size = RSTRING_LEN(vkey);
618
+ key.flags = LMEMFLAG;
619
+
620
+ if ( ! NIL_P(vdata) ) {
621
+ StringValue(vdata);
622
+ data.data = RSTRING_PTR(vdata);
623
+ data.size = RSTRING_LEN(vdata);
624
+ data.flags = LMEMFLAG;
625
+ }
626
+
627
+ rv = dbh->db->get(dbh->db,txn?txn->txn:NULL,&key,&data,flags);
628
+ if ( rv == 0 ) {
629
+ return rb_str_new(data.data,data.size);
630
+ } else if (rv == DB_NOTFOUND) {
631
+ return Qnil;
632
+ } else {
633
+ raise_error(rv, "db_get failure: %s",db_strerror(rv));
634
+ }
635
+ return Qnil;
636
+ }
637
+
638
+ /*
639
+ * call-seq:
640
+ * db.pget(txn,key,data,flags) -> [pkey,data]
641
+ *
642
+ * get a key/data pair from database using a secondary index.
643
+ * returns an array with a primary key and the data element.
644
+ *
645
+ */
646
+ VALUE db_pget(VALUE obj, VALUE vtxn, VALUE vkey, VALUE vdata, VALUE vflags)
647
+ {
648
+ t_dbh *dbh;
649
+ int rv;
650
+ u_int32_t flags=0;
651
+ DBT key,data,pkey;
652
+ VALUE str;
653
+ t_txnh *txn=NULL;
654
+
655
+ memset(&key,0,sizeof(DBT));
656
+ memset(&data,0,sizeof(DBT));
657
+ memset(&pkey,0,sizeof(DBT));
658
+
659
+ if ( ! NIL_P(vtxn) ) {
660
+ Data_Get_Struct(vtxn,t_txnh,txn);
661
+ if (!txn->txn)
662
+ raise(0, "txn is closed");
663
+ }
664
+
665
+ if ( ! NIL_P(vflags) ) {
666
+ flags=NUM2UINT(vflags);
667
+ }
668
+
669
+ Data_Get_Struct(obj,t_dbh,dbh);
670
+ if (!dbh->db)
671
+ raise(0, "db is closed");
672
+
673
+ StringValue(vkey);
674
+
675
+ key.data = RSTRING_PTR(vkey);
676
+ key.size = RSTRING_LEN(vkey);
677
+ key.flags = LMEMFLAG;
678
+
679
+ if ( ! NIL_P(vdata) ) {
680
+ StringValue(vdata);
681
+ data.data = RSTRING_PTR(vdata);
682
+ data.size = RSTRING_LEN(vdata);
683
+ data.flags = LMEMFLAG;
684
+ }
685
+
686
+ rv = dbh->db->pget(dbh->db,txn?txn->txn:NULL,&key,&pkey,&data,flags);
687
+ if ( rv == 0 ) {
688
+ return
689
+ rb_ary_new3(2,
690
+ rb_str_new(pkey.data,pkey.size),
691
+ rb_str_new(data.data,data.size));
692
+
693
+ } else if (rv == DB_NOTFOUND) {
694
+ return Qnil;
695
+ } else {
696
+ raise_error(rv, "db_pget failure: %s",db_strerror(rv));
697
+ }
698
+ return Qnil;
699
+ }
700
+
701
+ /*
702
+ * call-seq:
703
+ * db[key] -> data
704
+ *
705
+ * array ref style data retrieval
706
+ *
707
+ */
708
+ VALUE db_aget(VALUE obj, VALUE vkey)
709
+ {
710
+ return db_get(obj,Qnil,vkey,Qnil,Qnil);
711
+ }
712
+
713
+ /*
714
+ * call-seq:
715
+ * db[key]=data
716
+ *
717
+ * array ref style data storage
718
+ */
719
+ VALUE db_aset(VALUE obj, VALUE vkey, VALUE vdata)
720
+ {
721
+ return db_put(obj,Qnil,vkey,vdata,Qnil);
722
+ }
723
+
724
+ /*
725
+ * call-seq:
726
+ * db.join([cursors],flags) -> join_cursor
727
+ *
728
+ * create a join cursor from an array of cursors.
729
+ * The input cursors will usually be set_range and, if duplicate
730
+ * data items are allowed the should be DUP_SORT or the performance
731
+ * will be abysmal.
732
+ */
733
+ VALUE db_join(VALUE obj, VALUE vacurs, VALUE vflags)
734
+ {
735
+ t_dbh *dbh;
736
+ t_dbch *dbch;
737
+ u_int32_t flags;
738
+ DBC **curs;
739
+ int i,rv;
740
+ VALUE jcurs;
741
+
742
+ flags=NUM2UINT(vflags);
743
+ Data_Get_Struct(obj,t_dbh,dbh);
744
+ if (!dbh->db)
745
+ raise(0, "db is closed");
746
+
747
+ curs = ALLOCA_N(DBC *,RARRAY(vacurs)->len);
748
+ for (i=0; i<RARRAY(vacurs)->len; i++) {
749
+ Data_Get_Struct(RARRAY(vacurs)->ptr[i],t_dbch,dbch);
750
+ /* cursor is closed? */
751
+ curs[i]=dbch->dbc;
752
+ }
753
+ curs[i]=NULL;
754
+ jcurs=Data_Make_Struct(cCursor,t_dbch,dbc_mark,dbc_free,dbch);
755
+ rv = dbh->db->join(dbh->db,curs,&(dbch->dbc),flags);
756
+ if (rv) {
757
+ raise_error(rv, "db_join: %s",db_strerror(rv));
758
+ }
759
+ dbch->db=dbh;
760
+ rb_ary_push(dbch->db->adbc,jcurs);
761
+ rb_obj_call_init(jcurs,0,NULL);
762
+ return jcurs;
763
+ }
764
+
765
+ #if DB_VERSION_MINOR > 3
766
+ /*
767
+ * call-seq:
768
+ * db.compact(txn,start_key,stop_key,compact_opts,flags) -> end_key
769
+ *
770
+ * compact the database (4.4 an up). start and stop keys limit the
771
+ * range of compaction in BTREE types. compact_opts is currently
772
+ * ignored. Call returns the last key compacted (could be fed into
773
+ * a subsequent call as the start_key).
774
+ *
775
+ */
776
+ VALUE db_compact(VALUE obj, VALUE vtxn, VALUE vstart_key,
777
+ VALUE vstop_key, VALUE db_compact,
778
+ VALUE vflags)
779
+ {
780
+ t_dbh *dbh;
781
+ u_int32_t flags;
782
+ t_txnh *txn=NULL;
783
+ DBT start_key, stop_key, end_key;
784
+ int rv;
785
+
786
+ flags=NUM2UINT(vflags);
787
+ Data_Get_Struct(obj,t_dbh,dbh);
788
+ if (!dbh->db)
789
+ raise(0, "db is closed");
790
+
791
+ memset(&start_key,0,sizeof(DBT));
792
+ memset(&stop_key,0,sizeof(DBT));
793
+ memset(&end_key,0,sizeof(DBT));
794
+
795
+ if ( ! NIL_P(vstart_key) ) {
796
+ StringValue(vstart_key);
797
+ start_key.data=RSTRING_PTR(vstart_key);
798
+ start_key.size=RSTRING_LEN(vstart_key);
799
+ start_key.flags= LMEMFLAG;
800
+ }
801
+ if ( ! NIL_P(vstop_key) ) {
802
+ StringValue(vstop_key);
803
+ stop_key.data=RSTRING_PTR(vstop_key);
804
+ stop_key.size=RSTRING_LEN(vstop_key);
805
+ stop_key.flags= LMEMFLAG;
806
+ }
807
+ if ( ! NIL_P(vtxn) ) {
808
+ Data_Get_Struct(vtxn,t_txnh,txn);
809
+ if (!txn->txn)
810
+ raise(0, "txn is closed");
811
+ }
812
+
813
+ rv=dbh->db->compact(dbh->db,txn?txn->txn:NULL,
814
+ &start_key,
815
+ &stop_key,
816
+ NULL,
817
+ flags,
818
+ &end_key);
819
+ if (rv)
820
+ raise_error(rv,"db_compact failure: %s",db_strerror(rv));
821
+
822
+ return rb_str_new(end_key.data,end_key.size);
823
+
824
+ }
825
+ #endif
826
+
827
+ /*
828
+ * call-seq:
829
+ * db.get_byteswapped -> true/false
830
+ *
831
+ * true if database is running in swapped mode. This happens when
832
+ * the db is created on a machine with a different endian.
833
+ */
834
+ VALUE db_get_byteswapped(VALUE obj)
835
+ {
836
+ t_dbh *dbh;
837
+ int rv;
838
+ int is_swapped;
839
+
840
+ Data_Get_Struct(obj,t_dbh,dbh);
841
+ if (!dbh->db)
842
+ raise(0, "db is closed");
843
+ rv=dbh->db->get_byteswapped(dbh->db,&is_swapped);
844
+ if (rv)
845
+ raise_error(rv,"db_get_byteswapped failed: %s",db_strerror(rv));
846
+ if (is_swapped)
847
+ return Qtrue;
848
+ else
849
+ return Qfalse;
850
+ }
851
+
852
+ /*
853
+ * call-seq:
854
+ * db.get_type -> Fixnum(type)
855
+ *
856
+ * an integer indicating the type of db (BTREE, HASH, etc).
857
+ */
858
+ VALUE db_get_type(VALUE obj)
859
+ {
860
+ t_dbh *dbh;
861
+ int rv;
862
+ DBTYPE dbtype;
863
+
864
+ Data_Get_Struct(obj,t_dbh,dbh);
865
+ if (!dbh->db)
866
+ raise(0, "db is closed");
867
+ rv=dbh->db->get_type(dbh->db,&dbtype);
868
+ if (rv)
869
+ raise_error(rv,"db_get_type failed: %s",db_strerror(rv));
870
+ return INT2FIX(dbtype);
871
+ }
872
+
873
+ /*
874
+ * call-seq:
875
+ * db.remove(disk_file,logical_db,flags) -> true
876
+ *
877
+ * removes a whole database file, or just a logical_db within
878
+ * that file, i.e. if file and logical are both specified, only
879
+ * the logical will be removed. the Bdb::Db instance cannot have
880
+ * been previously used for anything and cannot be used after.
881
+ */
882
+ VALUE db_remove(VALUE obj, VALUE vdisk_file,
883
+ VALUE vlogical_db, VALUE vflags)
884
+ {
885
+ t_dbh *dbh;
886
+ int rv;
887
+ u_int32_t flags=0;
888
+ char *logical_db=NULL;
889
+
890
+ if ( ! NIL_P(vflags) )
891
+ flags=NUM2UINT(vflags);
892
+
893
+ Data_Get_Struct(obj,t_dbh,dbh);
894
+ if (!dbh->db)
895
+ raise(0, "db is closed");
896
+ rv=dbh->db->remove(dbh->db,
897
+ NIL_P(vdisk_file)?NULL:StringValueCStr(vdisk_file),
898
+ NIL_P(vlogical_db)?NULL:StringValueCStr(vlogical_db),
899
+ flags);
900
+ /* handle cannot be accessed again per docs */
901
+ dbh->db=NULL;
902
+ if (rv)
903
+ raise_error(rv,"db_remove failed: %s",db_strerror(rv));
904
+ return Qtrue;
905
+ }
906
+
907
+ /*
908
+ * call-seq:
909
+ * db.rename(file,logical,newname,flags) -> true
910
+ *
911
+ * rename a file or logical db to newname.
912
+ */
913
+ VALUE db_rename(VALUE obj, VALUE vdisk_file,
914
+ VALUE vlogical_db, VALUE newname, VALUE vflags)
915
+ {
916
+ t_dbh *dbh;
917
+ int rv;
918
+ u_int32_t flags=0;
919
+ char *disk_file=NULL;
920
+ char *logical_db=NULL;
921
+
922
+ if ( ! NIL_P(vflags) )
923
+ flags=NUM2UINT(vflags);
924
+
925
+ if ( NIL_P(newname) )
926
+ raise_error(0,"db_rename newname must be specified");
927
+
928
+ Data_Get_Struct(obj,t_dbh,dbh);
929
+ if (!dbh->db)
930
+ raise(0, "db is closed");
931
+ rv=dbh->db->rename(dbh->db,
932
+ NIL_P(vdisk_file)?NULL:StringValueCStr(vdisk_file),
933
+ NIL_P(vlogical_db)?NULL:StringValueCStr(vlogical_db),
934
+ StringValueCStr(newname),
935
+ flags);
936
+
937
+ if (rv) {
938
+ raise_error(rv,"db_rename failed: %s",db_strerror(rv));
939
+ }
940
+ return Qtrue;
941
+ }
942
+
943
+ /*
944
+ * call-seq:
945
+ * db.sync -> true
946
+ *
947
+ * sync the database out to storage.
948
+ */
949
+ VALUE db_sync(VALUE obj)
950
+ {
951
+ t_dbh *dbh;
952
+ int rv;
953
+
954
+ Data_Get_Struct(obj,t_dbh,dbh);
955
+ if (!dbh->db)
956
+ raise(0, "db is closed");
957
+ rv=dbh->db->sync(dbh->db,NOFLAGS);
958
+
959
+ if (rv)
960
+ raise_error(rv,"db_sync failed: %s",db_strerror(rv));
961
+ return Qtrue;
962
+ }
963
+
964
+ /*
965
+ * call-seq:
966
+ * db.truncate(txn) -> Fixnum(record_count)
967
+ *
968
+ * truncate, i.e. remove all records, purge, trash.
969
+ *
970
+ */
971
+ VALUE db_truncate(VALUE obj, VALUE vtxn)
972
+ {
973
+ t_dbh *dbh;
974
+ t_txnh *txn=NULL;
975
+ int rv;
976
+ VALUE result;
977
+ u_int32_t count;
978
+
979
+ if ( ! NIL_P(vtxn) ) {
980
+ Data_Get_Struct(vtxn,t_txnh,txn);
981
+ if (!txn->txn)
982
+ raise(0, "txn is closed");
983
+ }
984
+
985
+ Data_Get_Struct(obj,t_dbh,dbh);
986
+ if (!dbh->db)
987
+ raise(0, "db is closed");
988
+
989
+ rv=dbh->db->truncate(dbh->db,txn?txn->txn:NULL,&count,NOFLAGS);
990
+ if (rv)
991
+ raise_error(rv,"db_truncate: %s",db_strerror(rv));
992
+
993
+ return INT2FIX(count);
994
+ }
995
+
996
+ /*
997
+ * call-seq:
998
+ * db.key_range(txn,vkey,flags) -> [#less,#same,#greater]
999
+ *
1000
+ * calculate position of key within database. returns the counts
1001
+ * of keys less, same and greater than the given key as an
1002
+ * array of Fixnum-s
1003
+ */
1004
+ VALUE db_key_range(VALUE obj, VALUE vtxn, VALUE vkey, VALUE vflags)
1005
+ {
1006
+ t_dbh *dbh;
1007
+ t_txnh *txn=NULL;
1008
+ DBT key;
1009
+ u_int32_t flags;
1010
+ int rv;
1011
+ DB_KEY_RANGE key_range;
1012
+ VALUE result;
1013
+
1014
+ if ( ! NIL_P(vtxn) ) {
1015
+ Data_Get_Struct(vtxn,t_txnh,txn);
1016
+ if (!txn->txn)
1017
+ raise(0, "txn is closed");
1018
+ }
1019
+
1020
+ if ( ! NIL_P(vflags) )
1021
+ flags=NUM2UINT(vflags);
1022
+
1023
+ Data_Get_Struct(obj,t_dbh,dbh);
1024
+ if (!dbh->db)
1025
+ raise(0, "db is closed");
1026
+
1027
+ memset(&key,0,sizeof(DBT));
1028
+ StringValue(vkey);
1029
+ key.data = RSTRING_PTR(vkey);
1030
+ key.size = RSTRING_LEN(vkey);
1031
+ key.flags = LMEMFLAG;
1032
+
1033
+ rv=dbh->db->key_range(dbh->db,txn?txn->txn:NULL,&key,
1034
+ &key_range,flags);
1035
+ if (rv)
1036
+ raise_error(rv,"db_key_range: %s",db_strerror(rv));
1037
+
1038
+ result=rb_ary_new3(3,
1039
+ rb_float_new(key_range.less),
1040
+ rb_float_new(key_range.equal),
1041
+ rb_float_new(key_range.greater));
1042
+ return result;
1043
+ }
1044
+
1045
+ /*
1046
+ * call-seq:
1047
+ * db.del(txn,key,flags) -> true/nil
1048
+ *
1049
+ * delete records with the key given. DUP values will removed
1050
+ * as a group. true if deleted extant records. NIL if the
1051
+ * operation resulted in DB indicating DB_NOTFOUND.
1052
+ *
1053
+ */
1054
+ VALUE db_del(VALUE obj, VALUE vtxn, VALUE vkey, VALUE vflags)
1055
+ {
1056
+ t_dbh *dbh;
1057
+ int rv;
1058
+ u_int32_t flags;
1059
+ DBT key;
1060
+ VALUE str;
1061
+ t_txnh *txn=NULL;
1062
+
1063
+ memset(&key,0,sizeof(DBT));
1064
+
1065
+ if ( ! NIL_P(vtxn) ) {
1066
+ Data_Get_Struct(vtxn,t_txnh,txn);
1067
+ if (!txn->txn)
1068
+ raise(0, "txn is closed");
1069
+ }
1070
+
1071
+ flags=NUM2UINT(vflags);
1072
+ Data_Get_Struct(obj,t_dbh,dbh);
1073
+ if (!dbh->db)
1074
+ raise(0, "db is closed");
1075
+
1076
+ StringValue(vkey);
1077
+ key.data = RSTRING_PTR(vkey);
1078
+ key.size = RSTRING_LEN(vkey);
1079
+ key.flags = LMEMFLAG;
1080
+
1081
+ rv = dbh->db->del(dbh->db,txn?txn->txn:NULL,&key,flags);
1082
+ if ( rv == DB_NOTFOUND ) {
1083
+ return Qnil;
1084
+ } else if (rv != 0) {
1085
+ raise_error(rv, "db_del failure: %s",db_strerror(rv));
1086
+ }
1087
+ return Qtrue;
1088
+ }
1089
+
1090
+ t_dbh *dbassoc[LMAXFD];
1091
+ VALUE
1092
+ assoc_callback2(VALUE *args)
1093
+ {
1094
+ return rb_funcall(args[0],fv_call,3,args[1],args[2],args[3]);
1095
+ }
1096
+
1097
+ int assoc_callback(DB *secdb,const DBT* key, const DBT* data, DBT* result)
1098
+ {
1099
+ t_dbh *dbh;
1100
+ VALUE proc;
1101
+ int fdp,status;
1102
+ VALUE retv;
1103
+ VALUE args[4];
1104
+
1105
+ memset(result,0,sizeof(DBT));
1106
+ secdb->fd(secdb,&fdp);
1107
+ dbh=dbassoc[fdp];
1108
+
1109
+ args[0]=dbh->aproc;
1110
+ args[1]=dbh->self;
1111
+ args[2]=rb_str_new(key->data,key->size);
1112
+ args[3]=rb_str_new(data->data,data->size);
1113
+
1114
+ retv=rb_protect((VALUE(*)_((VALUE)))assoc_callback2,(VALUE)args,&status);
1115
+
1116
+ if (status) return 99999;
1117
+ if ( NIL_P(retv) )
1118
+ return DB_DONOTINDEX;
1119
+
1120
+ if ( TYPE(retv) != T_STRING )
1121
+ rb_warning("return from assoc callback not a string!");
1122
+
1123
+ StringValue(retv);
1124
+ #ifdef DEBUG_DB
1125
+ fprintf(stderr,"assoc_key %*s for %*s\n",
1126
+ RSTRING_LEN(retv),RSTRING_PTR(retv),
1127
+ data->size,data->data);
1128
+ #endif
1129
+ result->data=RSTRING_PTR(retv);
1130
+ result->size=RSTRING_LEN(retv);
1131
+ result->flags=LMEMFLAG;
1132
+ return 0;
1133
+ }
1134
+
1135
+ /*
1136
+ * call-seq:
1137
+ * db.associate(txn,sec_db,flags,proc)
1138
+ *
1139
+ * associate a secondary index(database) with this (primary)
1140
+ * database. The proc can be nil if the database is only opened
1141
+ * DB_RDONLY. Only +one+ proc can be assigned to a given database
1142
+ * according to the file-descriptor number of the secondary within
1143
+ * a single ruby process. This is typcially not an issue since
1144
+ * the proc should always be the same (it would corrupt the
1145
+ * secondary otherwise). But this does limit use of nil if the
1146
+ * db is openend DB_RDONLY and writeable in the same process.
1147
+ * (although, opening the same db more than once has not been
1148
+ * tested)
1149
+ *
1150
+ * call back proc has signature:
1151
+ * proc(secdb,key,value)
1152
+ */
1153
+ VALUE db_associate(VALUE obj, VALUE vtxn, VALUE osecdb,
1154
+ VALUE vflags, VALUE cb_proc)
1155
+ {
1156
+ t_dbh *sdbh,*pdbh;
1157
+ int rv;
1158
+ u_int32_t flags,flagsp,flagss;
1159
+ int fdp;
1160
+ t_txnh *txn=NOTXN;
1161
+
1162
+ flags=NUM2UINT(vflags);
1163
+ Data_Get_Struct(obj,t_dbh,pdbh);
1164
+ if (!pdbh->db)
1165
+ raise(0, "db is closed");
1166
+ Data_Get_Struct(osecdb,t_dbh,sdbh);
1167
+ if (!sdbh->db)
1168
+ raise(0, "sdb is closed");
1169
+
1170
+ if ( ! NIL_P(vtxn) ) {
1171
+ Data_Get_Struct(vtxn,t_txnh,txn);
1172
+ if (!txn->txn)
1173
+ raise(0, "txn is closed");
1174
+ }
1175
+
1176
+ if ( cb_proc == Qnil ) {
1177
+ rb_warning("db_associate: no association may be applied");
1178
+ pdbh->db->get_open_flags(pdbh->db,&flagsp);
1179
+ sdbh->db->get_open_flags(sdbh->db,&flagss);
1180
+ if ( flagsp & DB_RDONLY & flagss ) {
1181
+ rv=pdbh->db->associate(pdbh->db,txn?txn->txn:NULL,
1182
+ sdbh->db,NULL,flags);
1183
+ if (rv)
1184
+ raise_error(rv,"db_associate: %s",db_strerror(rv));
1185
+ return Qtrue;
1186
+ } else {
1187
+ raise_error(0,"db_associate empty associate only available when both DBs opened with DB_RDONLY");
1188
+ }
1189
+ } else if ( rb_obj_is_instance_of(cb_proc,rb_cProc) != Qtrue ) {
1190
+ raise_error(0, "db_associate proc required");
1191
+ }
1192
+
1193
+ sdbh->db->fd(sdbh->db,&fdp);
1194
+ sdbh->aproc=cb_proc;
1195
+
1196
+ /* No register is needed since this is just a way to
1197
+ * get back to a real object
1198
+ */
1199
+ dbassoc[fdp]=sdbh;
1200
+ rv=pdbh->db->associate(pdbh->db,txn?txn->txn:NULL,sdbh->db,assoc_callback,flags);
1201
+ #ifdef DEBUG_DB
1202
+ fprintf(stderr,"file is %d\n",fdp);
1203
+ fprintf(stderr,"assoc done 0x%x 0x%x\n",sdbh,dbassoc[fdp]);
1204
+ #endif
1205
+ if (rv != 0) {
1206
+ raise_error(rv, "db_associate failure: %s",db_strerror(rv));
1207
+ }
1208
+ return Qtrue;
1209
+ }
1210
+
1211
+ /*
1212
+ * call-seq:
1213
+ * db.cursor(txn,flags)
1214
+ *
1215
+ * open a cursor
1216
+ */
1217
+ VALUE db_cursor(VALUE obj, VALUE vtxn, VALUE vflags)
1218
+ {
1219
+ t_dbh *dbh;
1220
+ int rv;
1221
+ u_int32_t flags;
1222
+ DBC *dbc;
1223
+ t_txnh *txn=NOTXN;
1224
+ VALUE c_obj;
1225
+ t_dbch *dbch;
1226
+
1227
+ flags=NUM2UINT(vflags);
1228
+ Data_Get_Struct(obj,t_dbh,dbh);
1229
+ if (!dbh->db)
1230
+ raise(0, "db is closed");
1231
+
1232
+ c_obj=Data_Make_Struct(cCursor, t_dbch, dbc_mark, dbc_free, dbch);
1233
+
1234
+ if ( ! NIL_P(vtxn) ) {
1235
+ Data_Get_Struct(vtxn,t_txnh,txn);
1236
+ if (!txn->txn)
1237
+ raise(0, "txn is closed");
1238
+ }
1239
+
1240
+ rv=dbh->db->cursor(dbh->db,txn?txn->txn:NULL,&(dbch->dbc),flags);
1241
+ if (rv)
1242
+ raise_error(rv,"db_cursor: %s",db_strerror(rv));
1243
+
1244
+ filename_dup(dbch->filename,dbh->filename);
1245
+ dbch->db=dbh;
1246
+ rb_ary_push(dbch->db->adbc,c_obj);
1247
+ rb_obj_call_init(c_obj,0,NULL);
1248
+ return c_obj;
1249
+ }
1250
+
1251
+ /*
1252
+ * call-seq:
1253
+ * dbc.close -> nil
1254
+ *
1255
+ * close an open cursor
1256
+ */
1257
+ VALUE dbc_close(VALUE obj)
1258
+ {
1259
+ t_dbch *dbch;
1260
+ int rv;
1261
+ Data_Get_Struct(obj,t_dbch,dbch);
1262
+ if ( dbch->dbc ) {
1263
+ rv=dbch->dbc->c_close(dbch->dbc);
1264
+ rb_ary_delete(dbch->db->adbc,obj);
1265
+ dbch->db=NULL;
1266
+ dbch->dbc=NULL;
1267
+ if (rv)
1268
+ raise_error(rv,"dbc_close: %s",db_strerror(rv));
1269
+ }
1270
+ return Qnil;
1271
+ }
1272
+
1273
+ /*
1274
+ * call-seq:
1275
+ * dbc.get(key,data,flags) -> [key,data]
1276
+ *
1277
+ * get data by key or key and data. returns array of key,data
1278
+ */
1279
+ VALUE dbc_get(VALUE obj, VALUE vkey, VALUE vdata, VALUE vflags)
1280
+ {
1281
+ t_dbch *dbch;
1282
+ u_int32_t flags;
1283
+ DBT key,data;
1284
+ VALUE rar;
1285
+ int rv;
1286
+
1287
+ flags=NUM2UINT(vflags);
1288
+ Data_Get_Struct(obj,t_dbch,dbch);
1289
+ if (!dbch->dbc)
1290
+ raise(0, "dbc is closed");
1291
+
1292
+ memset(&key,0,sizeof(DBT));
1293
+ memset(&data,0,sizeof(DBT));
1294
+
1295
+ if ( ! NIL_P(vkey) ) {
1296
+ StringValue(vkey);
1297
+ key.data = RSTRING_PTR(vkey);
1298
+ key.size = RSTRING_LEN(vkey);
1299
+ key.flags = LMEMFLAG;
1300
+ }
1301
+ if ( ! NIL_P(vdata) ) {
1302
+ StringValue(vdata);
1303
+ data.data = RSTRING_PTR(vdata);
1304
+ data.size = RSTRING_LEN(vdata);
1305
+ data.flags = LMEMFLAG;
1306
+ }
1307
+
1308
+ rv = dbch->dbc->c_get(dbch->dbc,&key,&data,flags);
1309
+ if ( rv == 0 ) {
1310
+ rar = rb_ary_new3(2,rb_str_new(key.data,key.size),
1311
+ rb_str_new(data.data,data.size));
1312
+ return rar;
1313
+ } else if (rv == DB_NOTFOUND) {
1314
+ return Qnil;
1315
+ } else {
1316
+ raise_error(rv, "dbc_get %s",db_strerror(rv));
1317
+ }
1318
+ return Qnil;
1319
+ }
1320
+ /*
1321
+ * call-seq:
1322
+ * dbc.pget(key,data,flags) -> [key,pkey,data]
1323
+ *
1324
+ * cursor pget, returns array(key, primary key, data)
1325
+ */
1326
+ VALUE dbc_pget(VALUE obj, VALUE vkey, VALUE vdata, VALUE vflags)
1327
+ {
1328
+ t_dbch *dbch;
1329
+ u_int32_t flags;
1330
+ DBT key,data,pkey;
1331
+ VALUE rar;
1332
+ int rv;
1333
+
1334
+ flags=NUM2UINT(vflags);
1335
+ Data_Get_Struct(obj,t_dbch,dbch);
1336
+ if (!dbch->dbc)
1337
+ raise(0, "dbc is closed");
1338
+
1339
+ memset(&key,0,sizeof(DBT));
1340
+ memset(&data,0,sizeof(DBT));
1341
+ memset(&pkey,0,sizeof(DBT));
1342
+
1343
+ if ( ! NIL_P(vkey) ) {
1344
+ StringValue(vkey);
1345
+ key.data = RSTRING_PTR(vkey);
1346
+ key.size = RSTRING_LEN(vkey);
1347
+ key.flags = LMEMFLAG;
1348
+ }
1349
+ if ( ! NIL_P(vdata) ) {
1350
+ StringValue(vdata);
1351
+ data.data = RSTRING_PTR(vdata);
1352
+ data.size = RSTRING_LEN(vdata);
1353
+ data.flags = LMEMFLAG;
1354
+ }
1355
+
1356
+ rv = dbch->dbc->c_pget(dbch->dbc,&key,&pkey,&data,flags);
1357
+ if ( rv == 0 ) {
1358
+ rar = rb_ary_new3(3,
1359
+ rb_str_new(key.data,key.size),
1360
+ rb_str_new(pkey.data,pkey.size),
1361
+ rb_str_new(data.data,data.size));
1362
+ return rar;
1363
+ } else if (rv == DB_NOTFOUND) {
1364
+ return Qnil;
1365
+ } else {
1366
+ raise_error(rv, "dbc_pget %s",db_strerror(rv));
1367
+ }
1368
+ return Qnil;
1369
+ }
1370
+
1371
+ /*
1372
+ * call-seq:
1373
+ * dbc.put(key,data,flags) -> data
1374
+ *
1375
+ * cursor put key/data
1376
+ */
1377
+ VALUE dbc_put(VALUE obj, VALUE vkey, VALUE vdata, VALUE vflags)
1378
+ {
1379
+ t_dbch *dbch;
1380
+ u_int32_t flags;
1381
+ DBT key,data;
1382
+ int rv;
1383
+
1384
+ if ( NIL_P(vdata) )
1385
+ raise_error(0,"data element is required for put");
1386
+
1387
+ flags=NUM2UINT(vflags);
1388
+ Data_Get_Struct(obj,t_dbch,dbch);
1389
+ if (!dbch->dbc)
1390
+ raise(0, "dbc is closed");
1391
+
1392
+ memset(&key,0,sizeof(DBT));
1393
+ memset(&data,0,sizeof(DBT));
1394
+
1395
+ if ( ! NIL_P(vkey) ) {
1396
+ StringValue(vkey);
1397
+ key.data = RSTRING_PTR(vkey);
1398
+ key.size = RSTRING_LEN(vkey);
1399
+ key.flags = LMEMFLAG;
1400
+ }
1401
+
1402
+ StringValue(vdata);
1403
+ data.data = RSTRING_PTR(vdata);
1404
+ data.size = RSTRING_LEN(vdata);
1405
+ data.flags = LMEMFLAG;
1406
+
1407
+ rv = dbch->dbc->c_put(dbch->dbc,&key,&data,flags);
1408
+ if (rv != 0)
1409
+ raise_error(rv,"dbc_put failure: %s",db_strerror(rv));
1410
+
1411
+ return vdata;
1412
+ }
1413
+
1414
+ /*
1415
+ * call-seq:
1416
+ * dbc.del -> true|nil
1417
+ *
1418
+ * delete tuple at current cursor position. returns true if
1419
+ * something was deleted, nil otherwise (DB_KEYEMPTY)
1420
+ *
1421
+ */
1422
+ VALUE dbc_del(VALUE obj)
1423
+ {
1424
+ t_dbch *dbch;
1425
+ int rv;
1426
+
1427
+ Data_Get_Struct(obj,t_dbch,dbch);
1428
+ if (!dbch->dbc)
1429
+ raise(0, "dbc is closed");
1430
+ rv = dbch->dbc->c_del(dbch->dbc,NOFLAGS);
1431
+ if (rv == DB_KEYEMPTY)
1432
+ return Qnil;
1433
+ else if (rv != 0) {
1434
+ raise_error(rv, "dbc_del failure: %s",db_strerror(rv));
1435
+ }
1436
+ return Qtrue;
1437
+ }
1438
+
1439
+ /*
1440
+ * call-seq:
1441
+ * dbc.count -> Fixnum(count)
1442
+ *
1443
+ * returns cursor count as per DB.
1444
+ */
1445
+ VALUE dbc_count(VALUE obj)
1446
+ {
1447
+ t_dbch *dbch;
1448
+ int rv;
1449
+ db_recno_t count;
1450
+
1451
+ Data_Get_Struct(obj,t_dbch,dbch);
1452
+ if (!dbch->dbc)
1453
+ raise(0, "dbc is closed");
1454
+ rv = dbch->dbc->c_count(dbch->dbc,&count,NOFLAGS);
1455
+ if (rv != 0)
1456
+ raise_error(rv, "db_count failure: %s",db_strerror(rv));
1457
+
1458
+ return INT2FIX(count);
1459
+ }
1460
+
1461
+ static void env_free(void *p)
1462
+ {
1463
+ t_envh *eh;
1464
+ eh=(t_envh *)p;
1465
+
1466
+ #ifdef DEBUG_DB
1467
+ if ( RTEST(ruby_debug) )
1468
+ fprintf(stderr,"%s/%d %s 0x%x\n",__FILE__,__LINE__,"env_free cleanup!",p);
1469
+ #endif
1470
+
1471
+ if ( eh ) {
1472
+ if ( eh->env ) {
1473
+ eh->env->close(eh->env,NOFLAGS);
1474
+ eh->env=NULL;
1475
+ }
1476
+ free(p);
1477
+ }
1478
+ }
1479
+ static void env_mark(t_envh *eh)
1480
+ {
1481
+ rb_gc_mark(eh->adb);
1482
+ rb_gc_mark(eh->atxn);
1483
+ }
1484
+
1485
+ /*
1486
+ * Document-class: Bdb::Env
1487
+ *
1488
+ * Encapsulated DB environment. Create by simple new (with
1489
+ * flags as neede), then open. Database handles created with
1490
+ * env.db -> Bdb::Db object.
1491
+ */
1492
+
1493
+ /*
1494
+ * call-seq:
1495
+ * new(flags) -> object
1496
+ *
1497
+ *
1498
+ */
1499
+ VALUE env_new(VALUE class, VALUE vflags)
1500
+ {
1501
+ t_envh *eh;
1502
+ int rv;
1503
+ u_int32_t flags=0;
1504
+ VALUE obj;
1505
+
1506
+ if ( ! NIL_P(vflags) )
1507
+ flags=NUM2UINT(vflags);
1508
+
1509
+ obj=Data_Make_Struct(class,t_envh,env_mark,env_free,eh);
1510
+ rv=db_env_create(&(eh->env),flags);
1511
+ if ( rv != 0 ) {
1512
+ raise_error(rv,"env_new: %s",db_strerror(rv));
1513
+ return Qnil;
1514
+ }
1515
+ eh->self=obj;
1516
+ eh->adb = rb_ary_new();
1517
+ eh->atxn = rb_ary_new();
1518
+ rb_obj_call_init(obj,0,NULL);
1519
+ return obj;
1520
+ }
1521
+
1522
+ /*
1523
+ * call-seq:
1524
+ * env.open(homedir,flags,mode) -> self
1525
+ *
1526
+ * open an environment
1527
+ */
1528
+ VALUE env_open(VALUE obj, VALUE vhome, VALUE vflags, VALUE vmode)
1529
+ {
1530
+ t_envh *eh;
1531
+ int rv;
1532
+ u_int32_t flags=0;
1533
+ int mode=0;
1534
+
1535
+ if ( ! NIL_P(vflags) )
1536
+ flags=NUM2UINT(vflags);
1537
+ if ( ! NIL_P(vmode) )
1538
+ mode=NUM2INT(vmode);
1539
+ Data_Get_Struct(obj,t_envh,eh);
1540
+ if (!eh->env)
1541
+ raise(0, "env is closed");
1542
+ if ( NIL_P(eh->adb) )
1543
+ raise_error(0,"env handle already used and closed");
1544
+
1545
+ rv = eh->env->open(eh->env,StringValueCStr(vhome),flags,mode);
1546
+ if (rv != 0) {
1547
+ raise_error(rv, "env_open failure: %s",db_strerror(rv));
1548
+ }
1549
+ return obj;
1550
+ }
1551
+
1552
+ VALUE txn_abort(VALUE);
1553
+
1554
+ /*
1555
+ * call-seq:
1556
+ * env.close -> self
1557
+ *
1558
+ * close an environment. Do not use it after you close it.
1559
+ * The close process will automatically abort any open transactions
1560
+ * and close open databases (which also closes open cursors).
1561
+ * But this is just an effort to keep your dbs and envs from
1562
+ * becoming corrupted due to ruby errors that exit
1563
+ * unintentionally. However, to make this at all worth anything
1564
+ * use ObjectSpace.define_finalizer which calls env.close to
1565
+ * approach some assurance of it happening.
1566
+ *
1567
+ */
1568
+ VALUE env_close(VALUE obj)
1569
+ {
1570
+ t_envh *eh;
1571
+ VALUE db;
1572
+ int rv;
1573
+
1574
+ Data_Get_Struct(obj,t_envh,eh);
1575
+ if ( eh->env==NULL )
1576
+ return Qnil;
1577
+
1578
+ if (RARRAY(eh->adb)->len > 0) {
1579
+ rb_warning("%s/%d %s %d",__FILE__,__LINE__,
1580
+ "database handles still open",RARRAY(eh->adb)->len);
1581
+ while (RARRAY(eh->adb)->len > 0)
1582
+ if ((db=rb_ary_pop(eh->adb)) != Qnil ) {
1583
+ rb_warning("%s/%d %s 0x%x",__FILE__,__LINE__,
1584
+ "closing",db);
1585
+ /* this could raise! needs rb_protect */
1586
+ db_close(db,INT2FIX(0));
1587
+ }
1588
+ }
1589
+ if (RARRAY(eh->atxn)->len > 0) {
1590
+ rb_warning("%s/%d %s",__FILE__,__LINE__,
1591
+ "database transactions still open");
1592
+ while ( (db=rb_ary_pop(eh->atxn)) != Qnil ) {
1593
+ /* this could raise! needs rb_protect */
1594
+ txn_abort(db);
1595
+ }
1596
+ }
1597
+
1598
+ if ( RTEST(ruby_debug) )
1599
+ rb_warning("%s/%d %s 0x%x",__FILE__,__LINE__,"env_close!",eh);
1600
+
1601
+ rv = eh->env->close(eh->env,NOFLAGS);
1602
+ eh->env=NULL;
1603
+ eh->adb=Qnil;
1604
+ eh->atxn=Qnil;
1605
+ if ( rv != 0 ) {
1606
+ raise_error(rv, "env_close failure: %s",db_strerror(rv));
1607
+ return Qnil;
1608
+ }
1609
+ return obj;
1610
+ }
1611
+
1612
+ /*
1613
+ * call-seq:
1614
+ * env.db -> Bdb::Db instance
1615
+ *
1616
+ * create a db associated with an environment
1617
+ */
1618
+ VALUE env_db(VALUE obj)
1619
+ {
1620
+ t_envh *eh;
1621
+ VALUE dbo;
1622
+
1623
+ Data_Get_Struct(obj,t_envh,eh);
1624
+ if (!eh->env)
1625
+ raise(0, "env is closed");
1626
+ dbo = Data_Wrap_Struct(cDb,db_mark,db_free,0);
1627
+
1628
+ return db_init_aux(dbo,eh);
1629
+ }
1630
+
1631
+ /*
1632
+ * call-seq:
1633
+ * env.cachesize=Fixnum|Bignum
1634
+ *
1635
+ * set the environment cache size. If it is a Bignum then it
1636
+ * will populate the bytes and gbytes part of the DB struct.
1637
+ * Fixnums will only populate bytes (which is still pretty big).
1638
+ */
1639
+ VALUE env_set_cachesize(VALUE obj, VALUE size)
1640
+ {
1641
+ t_envh *eh;
1642
+ unsigned long long ln;
1643
+ u_int32_t bytes=0,gbytes=0;
1644
+ int rv;
1645
+ Data_Get_Struct(obj,t_envh,eh);
1646
+ if (!eh->env)
1647
+ raise(0, "env is closed");
1648
+
1649
+ if ( TYPE(size) == T_BIGNUM ) {
1650
+ ln = rb_big2ull(size);
1651
+ gbytes = ln / (1024*1024*1024);
1652
+ bytes = ln - (gbytes*1024*1024*1024);
1653
+ } else if (FIXNUM_P(size) ) {
1654
+ bytes=NUM2UINT(size);
1655
+ } else {
1656
+ raise_error(0,"set_cachesize requires number");
1657
+ return Qnil;
1658
+ }
1659
+
1660
+ rv=eh->env->set_cachesize(eh->env,gbytes,bytes,1);
1661
+ if ( rv != 0 ) {
1662
+ raise_error(rv, "set_cachesize failure: %s",db_strerror(rv));
1663
+ return Qnil;
1664
+ }
1665
+
1666
+ return Qtrue;
1667
+ }
1668
+
1669
+ /*
1670
+ * call-seq:
1671
+ * env.cachesize -> Fixnum|Bignum
1672
+ *
1673
+ * return the environment cache size. If it is a Bignum then it
1674
+ * will populate the bytes and gbytes part of the DB struct.
1675
+ * Fixnums will only populate bytes (which is still pretty big).
1676
+ */
1677
+ VALUE env_get_cachesize(VALUE obj)
1678
+ {
1679
+ t_envh *eh;
1680
+ unsigned long long ln;
1681
+ u_int32_t bytes=0,gbytes=0;
1682
+ int ncache;
1683
+ int rv;
1684
+ Data_Get_Struct(obj,t_envh,eh);
1685
+ if (!eh->env)
1686
+ raise(0, "env is closed");
1687
+
1688
+ rv=eh->env->get_cachesize(eh->env,&gbytes,&bytes,&ncache);
1689
+ if ( rv != 0 ) {
1690
+ raise_error(rv, "get_cachesize failure: %s",db_strerror(rv));
1691
+ return Qnil;
1692
+ }
1693
+
1694
+ if (gbytes != 0)
1695
+ return ULL2NUM(gbytes*1024*1024*1024+bytes);
1696
+ else
1697
+ return UINT2NUM(bytes);
1698
+
1699
+ return Qtrue;
1700
+ }
1701
+
1702
+ VALUE env_set_flags(VALUE obj, VALUE vflags, int onoff)
1703
+ {
1704
+ t_envh *eh;
1705
+ int rv;
1706
+ u_int32_t flags;
1707
+
1708
+ Data_Get_Struct(obj,t_envh,eh);
1709
+ if (!eh->env)
1710
+ raise(0, "env is closed");
1711
+ if ( ! NIL_P(vflags) ) {
1712
+ flags=NUM2UINT(vflags);
1713
+
1714
+ rv=eh->env->set_flags(eh->env,flags,onoff);
1715
+
1716
+ if ( rv != 0 ) {
1717
+ raise_error(rv, "set_flags failure: %s",db_strerror(rv));
1718
+ return Qnil;
1719
+ }
1720
+ }
1721
+ return Qtrue;
1722
+ }
1723
+
1724
+ /*
1725
+ * call-seq:
1726
+ * env.flags -> flags
1727
+ *
1728
+ * get what flags are on.
1729
+ */
1730
+ VALUE env_get_flags(VALUE obj)
1731
+ {
1732
+ t_envh *eh;
1733
+ int rv;
1734
+ u_int32_t flags;
1735
+
1736
+ Data_Get_Struct(obj,t_envh,eh);
1737
+ if (!eh->env)
1738
+ raise(0, "env is closed");
1739
+
1740
+ rv=eh->env->get_flags(eh->env,&flags);
1741
+
1742
+ if ( rv != 0 ) {
1743
+ raise_error(rv, "set_flags failure: %s",db_strerror(rv));
1744
+ return Qnil;
1745
+ }
1746
+
1747
+ return UINT2NUM(flags);
1748
+ }
1749
+
1750
+ /*
1751
+ * call-seq:
1752
+ * env.flags_on=flags
1753
+ *
1754
+ * set the 'flags' on. An or'ed set of flags will be set on.
1755
+ * Only included flags will be affected. Serialized calls
1756
+ * will only affect flags indicated (leaving others, default or
1757
+ * set as they were).
1758
+ */
1759
+ VALUE env_set_flags_on(VALUE obj, VALUE vflags)
1760
+ {
1761
+ return env_set_flags(obj,vflags,1);
1762
+ }
1763
+
1764
+ /*
1765
+ * call-seq:
1766
+ * env.flags_off=flags
1767
+ *
1768
+ * set the 'flags' off. An or'ed set of flags will be set off.
1769
+ * Only included flags will be affected. Serialized calls
1770
+ * will only affect flags indicated (leaving others, default or
1771
+ * set as they were).
1772
+ */
1773
+ VALUE env_set_flags_off(VALUE obj, VALUE vflags)
1774
+ {
1775
+ return env_set_flags(obj,vflags,0);
1776
+ }
1777
+
1778
+ /*
1779
+ * call-seq:
1780
+ * env.list_dbs -> [Bdb::Db array]
1781
+ *
1782
+ * return 0 or more open databases within the receiver environment.
1783
+ * If 0, will return [], not nil.
1784
+ */
1785
+ VALUE env_list_dbs(VALUE obj)
1786
+ {
1787
+ t_envh *eh;
1788
+ Data_Get_Struct(obj,t_envh,eh);
1789
+ if (!eh->env)
1790
+ raise(0, "env is closed");
1791
+ return eh->adb;
1792
+ }
1793
+ static void txn_mark(t_txnh *txn)
1794
+ {
1795
+ if (txn->env)
1796
+ rb_gc_mark(txn->env->self);
1797
+ }
1798
+ static void txn_free(t_txnh *txn)
1799
+ {
1800
+ #ifdef DEBUG_DB
1801
+ if ( RTEST(ruby_debug) )
1802
+ fprintf(stderr,"%s/%d %s %p\n",__FILE__,__LINE__,"txn_free",txn);
1803
+ #endif
1804
+
1805
+ if (txn) {
1806
+ if (txn->txn)
1807
+ txn->txn->abort(txn->txn);
1808
+ txn->txn=NULL;
1809
+ if (txn->env) {
1810
+ rb_ary_delete(txn->env->atxn,txn->self);
1811
+ }
1812
+ txn->env=NULL;
1813
+
1814
+ free(txn);
1815
+ }
1816
+ }
1817
+
1818
+ /*
1819
+ * call-seq:
1820
+ * env.txn_begin(txn_parent,flags) -> Bdb::Txn
1821
+ *
1822
+ * start a root transaction or embedded (via txn_parent).
1823
+ */
1824
+ VALUE env_txn_begin(VALUE obj, VALUE vtxn_parent, VALUE vflags)
1825
+ {
1826
+ t_txnh *parent=NULL, *txn=NULL;
1827
+ u_int32_t flags=0;
1828
+ int rv;
1829
+ t_envh *eh;
1830
+ VALUE t_obj;
1831
+
1832
+ if ( ! NIL_P(vflags))
1833
+ flags=NUM2UINT(vflags);
1834
+ if ( ! NIL_P(vtxn_parent) ) {
1835
+ Data_Get_Struct(vtxn_parent,t_txnh,parent);
1836
+ if (!parent->txn)
1837
+ raise(0, "parent txn is closed");
1838
+ }
1839
+
1840
+ Data_Get_Struct(obj,t_envh,eh);
1841
+ if (!eh->env)
1842
+ raise(0, "env is closed");
1843
+ t_obj=Data_Make_Struct(cTxn,t_txnh,txn_mark,txn_free,txn);
1844
+
1845
+ rv=eh->env->txn_begin(eh->env,parent?parent->txn:NULL,
1846
+ &(txn->txn),flags);
1847
+
1848
+ if ( rv != 0 ) {
1849
+ raise_error(rv, "env_txn_begin: %s",db_strerror(rv));
1850
+ return Qnil;
1851
+ }
1852
+ txn->env=eh;
1853
+ txn->self=t_obj;
1854
+ rb_ary_push(eh->atxn,t_obj);
1855
+
1856
+ /* Once we get this working, we'll have to track transactions */
1857
+ rb_obj_call_init(t_obj,0,NULL);
1858
+ return t_obj;
1859
+ }
1860
+
1861
+ /*
1862
+ * call-seq:
1863
+ * env.txn_checkpoint -> true
1864
+ *
1865
+ * Cause env transaction state to be checkpointed.
1866
+ */
1867
+ VALUE env_txn_checkpoint(VALUE obj, VALUE vkbyte, VALUE vmin,
1868
+ VALUE vflags)
1869
+ {
1870
+ u_int32_t flags=0;
1871
+ int rv;
1872
+ t_envh *eh;
1873
+ u_int32_t kbyte=0, min=0;
1874
+
1875
+ if ( ! NIL_P(vflags))
1876
+ flags=NUM2UINT(vflags);
1877
+
1878
+ if ( FIXNUM_P(vkbyte) )
1879
+ kbyte=FIX2UINT(vkbyte);
1880
+
1881
+ if ( FIXNUM_P(vmin) )
1882
+ min=FIX2UINT(vmin);
1883
+
1884
+ Data_Get_Struct(obj,t_envh,eh);
1885
+ if (!eh->env)
1886
+ raise(0, "env is closed");
1887
+ rv=eh->env->txn_checkpoint(eh->env,kbyte,min,flags);
1888
+ if ( rv != 0 ) {
1889
+ raise_error(rv, "env_txn_checkpoint: %s",db_strerror(rv));
1890
+ return Qnil;
1891
+ }
1892
+ return Qtrue;
1893
+ }
1894
+
1895
+ VALUE env_txn_stat_active(DB_TXN_ACTIVE *t)
1896
+ {
1897
+ VALUE ao;
1898
+
1899
+ ao=rb_class_new_instance(0,NULL,cTxnStatActive);
1900
+
1901
+ rb_iv_set(ao,"@txnid",INT2FIX(t->txnid));
1902
+ rb_iv_set(ao,"@parentid",INT2FIX(t->parentid));
1903
+ /* rb_iv_set(ao,"@thread_id",INT2FIX(t->thread_id)); */
1904
+ rb_iv_set(ao,"@lsn",rb_ary_new3(2,
1905
+ INT2FIX(t->lsn.file),
1906
+ INT2FIX(t->lsn.offset)));
1907
+ /* XA status is currently excluded */
1908
+ return ao;
1909
+ }
1910
+
1911
+ /*
1912
+ * call-seq:
1913
+ * db.stat(txn,flags) -> Bdb::Db::Stat
1914
+ *
1915
+ * get database status. Returns a Bdb::Db::Stat object that
1916
+ * is specialized to the db_type and only responds to [] to
1917
+ * retrieve status values. All values are stored in instance
1918
+ * variables, so singleton classes can be created and instance_eval
1919
+ * will work.
1920
+ */
1921
+ VALUE db_stat(VALUE obj, VALUE vtxn, VALUE vflags)
1922
+ {
1923
+ u_int32_t flags=0;
1924
+ int rv;
1925
+ t_dbh *dbh;
1926
+ t_txnh *txn=NULL;
1927
+ DBTYPE dbtype;
1928
+ union {
1929
+ void *stat;
1930
+ DB_HASH_STAT *hstat;
1931
+ DB_BTREE_STAT *bstat;
1932
+ DB_QUEUE_STAT *qstat;
1933
+ } su;
1934
+ VALUE s_obj;
1935
+
1936
+ if ( ! NIL_P(vflags) )
1937
+ flags=NUM2UINT(vflags);
1938
+ if ( ! NIL_P(vtxn) ) {
1939
+ Data_Get_Struct(vtxn,t_txnh,txn);
1940
+ if (!txn->txn)
1941
+ raise(0, "txn is closed");
1942
+ }
1943
+
1944
+ Data_Get_Struct(obj,t_dbh,dbh);
1945
+ if (!dbh->db)
1946
+ raise(0, "db is closed");
1947
+
1948
+ rv=dbh->db->get_type(dbh->db,&dbtype);
1949
+ if (rv)
1950
+ raise_error(rv,"db_stat %s",db_strerror(rv));
1951
+ #if DB_VERSION_MINOR > 2
1952
+ rv=dbh->db->stat(dbh->db,txn?txn->txn:NULL,&(su.stat),flags);
1953
+ #else
1954
+ rv=dbh->db->stat(dbh->db,&(su.stat),flags);
1955
+ #endif
1956
+ if (rv)
1957
+ raise_error(rv,"db_stat %s",db_strerror(rv));
1958
+
1959
+ s_obj=rb_class_new_instance(0,NULL,cDbStat);
1960
+ rb_iv_set(s_obj,"@dbtype",INT2FIX(dbtype));
1961
+
1962
+ switch(dbtype) {
1963
+
1964
+ #define hs_int(field) \
1965
+ rb_iv_set(s_obj,"@" #field,INT2FIX(su.hstat->field))
1966
+
1967
+ case DB_HASH:
1968
+ hs_int(hash_magic);
1969
+ hs_int(hash_version); /* Version number. */
1970
+ hs_int(hash_metaflags); /* Metadata flags. */
1971
+ hs_int(hash_nkeys); /* Number of unique keys. */
1972
+ hs_int(hash_ndata); /* Number of data items. */
1973
+ hs_int(hash_pagesize); /* Page size. */
1974
+ hs_int(hash_ffactor); /* Fill factor specified at create. */
1975
+ hs_int(hash_buckets); /* Number of hash buckets. */
1976
+ hs_int(hash_free); /* Pages on the free list. */
1977
+ hs_int(hash_bfree); /* Bytes free on bucket pages. */
1978
+ hs_int(hash_bigpages); /* Number of big key/data pages. */
1979
+ hs_int(hash_big_bfree); /* Bytes free on big item pages. */
1980
+ hs_int(hash_overflows); /* Number of overflow pages. */
1981
+ hs_int(hash_ovfl_free); /* Bytes free on ovfl pages. */
1982
+ hs_int(hash_dup); /* Number of dup pages. */
1983
+ hs_int(hash_dup_free); /* Bytes free on duplicate pages. */
1984
+ break;
1985
+
1986
+ #define bs_int(field) \
1987
+ rb_iv_set(s_obj,"@" #field,INT2FIX(su.bstat->field))
1988
+
1989
+ case DB_BTREE:
1990
+ case DB_RECNO:
1991
+ bs_int(bt_magic); /* Magic number. */
1992
+ bs_int(bt_version); /* Version number. */
1993
+ bs_int(bt_metaflags); /* Metadata flags. */
1994
+ bs_int(bt_nkeys); /* Number of unique keys. */
1995
+ bs_int(bt_ndata); /* Number of data items. */
1996
+ bs_int(bt_pagesize); /* Page size. */
1997
+ #if DB_VERSION_MINOR < 4
1998
+ bs_int(bt_maxkey); /* Maxkey value. */
1999
+ #endif
2000
+ bs_int(bt_minkey); /* Minkey value. */
2001
+ bs_int(bt_re_len); /* Fixed-length record length. */
2002
+ bs_int(bt_re_pad); /* Fixed-length record pad. */
2003
+ bs_int(bt_levels); /* Tree levels. */
2004
+ bs_int(bt_int_pg); /* Internal pages. */
2005
+ bs_int(bt_leaf_pg); /* Leaf pages. */
2006
+ bs_int(bt_dup_pg); /* Duplicate pages. */
2007
+ bs_int(bt_over_pg); /* Overflow pages. */
2008
+ #if DB_VERSION_MINOR > 2
2009
+ bs_int(bt_empty_pg); /* Empty pages. */
2010
+ #endif
2011
+ bs_int(bt_free); /* Pages on the free list. */
2012
+ bs_int(bt_int_pgfree); /* Bytes free in internal pages. */
2013
+ bs_int(bt_leaf_pgfree); /* Bytes free in leaf pages. */
2014
+ bs_int(bt_dup_pgfree); /* Bytes free in duplicate pages. */
2015
+ bs_int(bt_over_pgfree); /* Bytes free in overflow pages. */
2016
+
2017
+ break;
2018
+
2019
+ #define qs_int(field) \
2020
+ rb_iv_set(s_obj,"@" #field,INT2FIX(su.qstat->field))
2021
+
2022
+ case DB_QUEUE:
2023
+ qs_int(qs_magic); /* Magic number. */
2024
+ qs_int(qs_version); /* Version number. */
2025
+ qs_int(qs_metaflags); /* Metadata flags. */
2026
+ qs_int(qs_nkeys); /* Number of unique keys. */
2027
+ qs_int(qs_ndata); /* Number of data items. */
2028
+ qs_int(qs_pagesize); /* Page size. */
2029
+ qs_int(qs_extentsize); /* Pages per extent. */
2030
+ qs_int(qs_pages); /* Data pages. */
2031
+ qs_int(qs_re_len); /* Fixed-length record length. */
2032
+ qs_int(qs_re_pad); /* Fixed-length record pad. */
2033
+ qs_int(qs_pgfree); /* Bytes free in data pages. */
2034
+ qs_int(qs_first_recno); /* First not deleted record. */
2035
+ qs_int(qs_cur_recno); /* Next available record number. */
2036
+
2037
+ break;
2038
+ }
2039
+
2040
+ free(su.stat);
2041
+ return s_obj;
2042
+ }
2043
+
2044
+ /*
2045
+ * call-seq:
2046
+ * stat[name] -> value
2047
+ *
2048
+ * return status value
2049
+ */
2050
+ VALUE stat_aref(VALUE obj, VALUE vname)
2051
+ {
2052
+ rb_iv_get(obj,RSTRING_PTR(rb_str_concat(rb_str_new2("@"),vname)));
2053
+ }
2054
+
2055
+ /*
2056
+ * call-seq:
2057
+ * env.txn_stat -> Bdb::TxnStat
2058
+ *
2059
+ * get the environment transaction status. Each active
2060
+ * transaction will be contained within a Bdb::TxnStat::Active
2061
+ * class.
2062
+ */
2063
+ VALUE env_txn_stat(VALUE obj, VALUE vflags)
2064
+ {
2065
+ u_int32_t flags=0;
2066
+ int rv;
2067
+ t_envh *eh;
2068
+ DB_TXN_STAT *statp;
2069
+ VALUE s_obj;
2070
+ VALUE active;
2071
+ int i;
2072
+
2073
+ if ( ! NIL_P(vflags))
2074
+ flags=NUM2UINT(vflags);
2075
+
2076
+ Data_Get_Struct(obj,t_envh,eh);
2077
+ if (!eh->env)
2078
+ raise(0, "env is closed");
2079
+
2080
+ /* statp will need free() */
2081
+ rv=eh->env->txn_stat(eh->env,&statp,flags);
2082
+ if ( rv != 0 ) {
2083
+ raise_error(rv, "txn_stat: %s",db_strerror(rv));
2084
+ }
2085
+
2086
+ s_obj=rb_class_new_instance(0,NULL,cTxnStat);
2087
+ rb_iv_set(s_obj,"@st_last_ckp",
2088
+ rb_ary_new3(2,
2089
+ INT2FIX(statp->st_last_ckp.file),
2090
+ INT2FIX(statp->st_last_ckp.offset)) );
2091
+ rb_iv_set(s_obj,"@st_time_ckp",
2092
+ rb_time_new(statp->st_time_ckp,0));
2093
+ rb_iv_set(s_obj,"@st_last_txnid",
2094
+ INT2FIX(statp->st_last_txnid));
2095
+ rb_iv_set(s_obj,"@st_maxtxns",
2096
+ INT2FIX(statp->st_maxtxns));
2097
+ rb_iv_set(s_obj,"@st_nactive",
2098
+ INT2FIX(statp->st_nactive));
2099
+ rb_iv_set(s_obj,"@st_maxnactive",
2100
+ INT2FIX(statp->st_maxnactive));
2101
+ rb_iv_set(s_obj,"@st_nbegins",
2102
+ INT2FIX(statp->st_nbegins));
2103
+ rb_iv_set(s_obj,"@st_naborts",
2104
+ INT2FIX(statp->st_naborts));
2105
+ rb_iv_set(s_obj,"@st_ncommits",
2106
+ INT2FIX(statp->st_ncommits));
2107
+ rb_iv_set(s_obj,"@st_nrestores",
2108
+ INT2FIX(statp->st_nrestores));
2109
+ rb_iv_set(s_obj,"@st_regsize",
2110
+ INT2FIX(statp->st_regsize));
2111
+ rb_iv_set(s_obj,"@st_region_wait",
2112
+ INT2FIX(statp->st_region_wait));
2113
+ rb_iv_set(s_obj,"@st_region_nowait",
2114
+ INT2FIX(statp->st_region_nowait));
2115
+ rb_iv_set(s_obj,"@st_txnarray",
2116
+ active=rb_ary_new2(statp->st_nactive));
2117
+
2118
+ for (i=0; i<statp->st_nactive; i++) {
2119
+ rb_ary_push(active,env_txn_stat_active(&(statp->st_txnarray[i])));
2120
+ }
2121
+
2122
+ free(statp);
2123
+ return s_obj;
2124
+ }
2125
+
2126
+ /*
2127
+ * call-seq:
2128
+ * env.set_timeout(timeout,flags) -> timeout
2129
+ *
2130
+ * set lock and transaction timeout
2131
+ */
2132
+ VALUE env_set_timeout(VALUE obj, VALUE vtimeout, VALUE vflags)
2133
+ {
2134
+ t_envh *eh;
2135
+ u_int32_t flags=0;
2136
+ db_timeout_t timeout;
2137
+ int rv;
2138
+
2139
+ if ( ! NIL_P(vflags))
2140
+ flags=NUM2UINT(vflags);
2141
+ timeout=FIX2UINT(vtimeout);
2142
+
2143
+ Data_Get_Struct(obj,t_envh,eh);
2144
+ if (!eh->env)
2145
+ raise(0, "env is closed");
2146
+ rv=eh->env->set_timeout(eh->env,timeout,flags);
2147
+ if ( rv != 0 ) {
2148
+ raise_error(rv, "env_set_timeout: %s",db_strerror(rv));
2149
+ }
2150
+
2151
+ return vtimeout;
2152
+ }
2153
+
2154
+ /*
2155
+ * call-seq:
2156
+ * env.get_timeout(flags) -> Fixnum
2157
+ *
2158
+ * Get current transaction and lock timeout value
2159
+ */
2160
+ VALUE env_get_timeout(VALUE obj, VALUE vflags)
2161
+ {
2162
+ t_envh *eh;
2163
+ u_int32_t flags=0;
2164
+ db_timeout_t timeout;
2165
+ int rv;
2166
+
2167
+ if ( ! NIL_P(vflags))
2168
+ flags=NUM2UINT(vflags);
2169
+
2170
+ Data_Get_Struct(obj,t_envh,eh);
2171
+ if (!eh->env)
2172
+ raise(0, "env is closed");
2173
+ rv=eh->env->get_timeout(eh->env,&timeout,flags);
2174
+ if ( rv != 0 ) {
2175
+ raise_error(rv, "env_get_timeout: %s",db_strerror(rv));
2176
+ }
2177
+
2178
+ return INT2FIX(timeout);
2179
+ }
2180
+
2181
+ /*
2182
+ * call-seq:
2183
+ * env.set_tx_max(max) -> max
2184
+ *
2185
+ * Set the maximum number of transactions with the environment
2186
+ */
2187
+ VALUE env_set_tx_max(VALUE obj, VALUE vmax)
2188
+ {
2189
+ t_envh *eh;
2190
+ u_int32_t max;
2191
+ int rv;
2192
+
2193
+ max=FIX2UINT(vmax);
2194
+
2195
+ Data_Get_Struct(obj,t_envh,eh);
2196
+ if (!eh->env)
2197
+ raise(0, "env is closed");
2198
+ rv=eh->env->set_tx_max(eh->env,max);
2199
+ if ( rv != 0 ) {
2200
+ raise_error(rv, "env_set_tx_max: %s",db_strerror(rv));
2201
+ }
2202
+
2203
+ return vmax;
2204
+ }
2205
+
2206
+ /*
2207
+ * call-seq
2208
+ * env.get_tx_max -> Fixnum
2209
+ *
2210
+ * Get current maximum number of transactions
2211
+ */
2212
+ VALUE env_get_tx_max(VALUE obj)
2213
+ {
2214
+ t_envh *eh;
2215
+ u_int32_t max;
2216
+ int rv;
2217
+
2218
+ Data_Get_Struct(obj,t_envh,eh);
2219
+ if (!eh->env)
2220
+ raise(0, "env is closed");
2221
+ rv=eh->env->get_tx_max(eh->env,&max);
2222
+ if ( rv != 0 ) {
2223
+ raise_error(rv, "env_get_tx_max: %s",db_strerror(rv));
2224
+ }
2225
+
2226
+ return INT2FIX(max);
2227
+ }
2228
+
2229
+ /*
2230
+ * call-seq:
2231
+ * env.set_shm_key(key) -> max
2232
+ *
2233
+ * Set the shared memory key base
2234
+ */
2235
+ VALUE env_set_shm_key(VALUE obj, VALUE vkey)
2236
+ {
2237
+ t_envh *eh;
2238
+ long key;
2239
+ int rv;
2240
+
2241
+ key=FIX2UINT(vkey);
2242
+
2243
+ Data_Get_Struct(obj,t_envh,eh);
2244
+ if (!eh->env)
2245
+ raise(0, "env is closed");
2246
+ rv=eh->env->set_shm_key(eh->env,key);
2247
+ if ( rv != 0 ) {
2248
+ raise_error(rv, "env_set_shm_key: %s",db_strerror(rv));
2249
+ }
2250
+
2251
+ return vkey;
2252
+ }
2253
+
2254
+ /*
2255
+ * call-seq
2256
+ * env.get_shm_key -> Fixnum
2257
+ *
2258
+ * Get the current shm key base
2259
+ */
2260
+ VALUE env_get_shm_key(VALUE obj)
2261
+ {
2262
+ t_envh *eh;
2263
+ long key;
2264
+ int rv;
2265
+
2266
+ Data_Get_Struct(obj,t_envh,eh);
2267
+ if (!eh->env)
2268
+ raise(0, "env is closed");
2269
+ rv=eh->env->get_shm_key(eh->env,&key);
2270
+ if ( rv != 0 ) {
2271
+ raise_error(rv, "env_get_shm_key: %s",db_strerror(rv));
2272
+ }
2273
+
2274
+ return INT2FIX(key);
2275
+ }
2276
+
2277
+ /*
2278
+ * call-seq:
2279
+ * env.set_lk_detect(detect) -> detect
2280
+ *
2281
+ * Set when lock detector should be run
2282
+ */
2283
+ VALUE env_set_lk_detect(VALUE obj, VALUE vdetect)
2284
+ {
2285
+ t_envh *eh;
2286
+ u_int32_t detect;
2287
+ int rv;
2288
+
2289
+ detect=NUM2UINT(vdetect);
2290
+
2291
+ Data_Get_Struct(obj,t_envh,eh);
2292
+ if (!eh->env)
2293
+ raise(0, "env is closed");
2294
+ rv=eh->env->set_lk_detect(eh->env,detect);
2295
+ if ( rv != 0 ) {
2296
+ raise_error(rv, "env_set_lk_detect: %s",db_strerror(rv));
2297
+ }
2298
+
2299
+ return vdetect;
2300
+ }
2301
+
2302
+ /*
2303
+ * call-seq:
2304
+ * env.get_lk_detect() -> detect
2305
+ *
2306
+ * Get when lock detector should be run
2307
+ */
2308
+ VALUE env_get_lk_detect(VALUE obj)
2309
+ {
2310
+ t_envh *eh;
2311
+ u_int32_t detect;
2312
+ int rv;
2313
+
2314
+ Data_Get_Struct(obj,t_envh,eh);
2315
+ if (!eh->env)
2316
+ raise(0, "env is closed");
2317
+ rv=eh->env->get_lk_detect(eh->env,&detect);
2318
+ if ( rv != 0 ) {
2319
+ raise_error(rv, "env_set_lk_detect: %s",db_strerror(rv));
2320
+ }
2321
+
2322
+ return UINT2NUM(detect);
2323
+ }
2324
+
2325
+ /*
2326
+ * call-seq:
2327
+ * env.set_lk_max_locks(max) -> max
2328
+ *
2329
+ * Set the maximum number of locks in the environment
2330
+ */
2331
+ VALUE env_set_lk_max_locks(VALUE obj, VALUE vmax)
2332
+ {
2333
+ t_envh *eh;
2334
+ u_int32_t max;
2335
+ int rv;
2336
+
2337
+ max=FIX2UINT(vmax);
2338
+
2339
+ Data_Get_Struct(obj,t_envh,eh);
2340
+ if (!eh->env)
2341
+ raise(0, "env is closed");
2342
+ rv=eh->env->set_lk_max_locks(eh->env,max);
2343
+ if ( rv != 0 ) {
2344
+ raise_error(rv, "env_set_lk_max_locks: %s",db_strerror(rv));
2345
+ }
2346
+
2347
+ return vmax;
2348
+ }
2349
+
2350
+ /*
2351
+ * call-seq:
2352
+ * env.get_lk_max_locks -> max
2353
+ *
2354
+ * Get the maximum number of locks in the environment
2355
+ */
2356
+ VALUE env_get_lk_max_locks(VALUE obj)
2357
+ {
2358
+ t_envh *eh;
2359
+ u_int32_t max;
2360
+ int rv;
2361
+
2362
+ Data_Get_Struct(obj,t_envh,eh);
2363
+ if (!eh->env)
2364
+ raise(0, "env is closed");
2365
+ rv=eh->env->get_lk_max_locks(eh->env,&max);
2366
+ if ( rv != 0 ) {
2367
+ raise_error(rv, "env_get_lk_max_locks: %s",db_strerror(rv));
2368
+ }
2369
+
2370
+ return UINT2NUM(max);
2371
+ }
2372
+
2373
+ /*
2374
+ * call-seq:
2375
+ * env.set_lk_max_objects(max) -> max
2376
+ *
2377
+ * Set the maximum number of locks in the environment
2378
+ */
2379
+ VALUE env_set_lk_max_objects(VALUE obj, VALUE vmax)
2380
+ {
2381
+ t_envh *eh;
2382
+ u_int32_t max;
2383
+ int rv;
2384
+
2385
+ max=FIX2UINT(vmax);
2386
+
2387
+ Data_Get_Struct(obj,t_envh,eh);
2388
+ if (!eh->env)
2389
+ raise(0, "env is closed");
2390
+ rv=eh->env->set_lk_max_objects(eh->env,max);
2391
+ if ( rv != 0 ) {
2392
+ raise_error(rv, "env_set_lk_max_objects: %s",db_strerror(rv));
2393
+ }
2394
+
2395
+ return vmax;
2396
+ }
2397
+
2398
+ /*
2399
+ * call-seq:
2400
+ * env.get_lk_max_objects -> max
2401
+ *
2402
+ * Get the maximum number of locks in the environment
2403
+ */
2404
+ VALUE env_get_lk_max_objects(VALUE obj)
2405
+ {
2406
+ t_envh *eh;
2407
+ u_int32_t max;
2408
+ int rv;
2409
+
2410
+ Data_Get_Struct(obj,t_envh,eh);
2411
+ if (!eh->env)
2412
+ raise(0, "env is closed");
2413
+ rv=eh->env->get_lk_max_objects(eh->env,&max);
2414
+ if ( rv != 0 ) {
2415
+ raise_error(rv, "env_get_lk_max_objects: %s",db_strerror(rv));
2416
+ }
2417
+
2418
+ return UINT2NUM(max);
2419
+ }
2420
+
2421
+ VALUE env_report_stderr(VALUE obj)
2422
+ {
2423
+ t_envh *eh;
2424
+ u_int32_t max;
2425
+ int rv;
2426
+
2427
+ Data_Get_Struct(obj,t_envh,eh);
2428
+ if (!eh->env)
2429
+ raise(0, "env is closed");
2430
+ eh->env->set_errfile(eh->env,stderr);
2431
+
2432
+ return Qtrue;
2433
+ }
2434
+
2435
+ /*
2436
+ * call-seq:
2437
+ * env.set_data_dir(data_dir) -> data_dir
2438
+ *
2439
+ * set data_dir
2440
+ */
2441
+ VALUE env_set_data_dir(VALUE obj, VALUE vdata_dir)
2442
+ {
2443
+ t_envh *eh;
2444
+ const char *data_dir;
2445
+ int rv;
2446
+
2447
+ data_dir=StringValueCStr(vdata_dir);
2448
+
2449
+ Data_Get_Struct(obj,t_envh,eh);
2450
+ if (!eh->env)
2451
+ raise(0, "env is closed");
2452
+ rv=eh->env->set_data_dir(eh->env,data_dir);
2453
+ if ( rv != 0 ) {
2454
+ raise_error(rv, "env_set_data_dir: %s",db_strerror(rv));
2455
+ }
2456
+
2457
+ return vdata_dir;
2458
+ }
2459
+
2460
+ /*
2461
+ * call-seq:
2462
+ * env.get_data_dir -> [data_dir_1, data_dir_2, ...]
2463
+ *
2464
+ * get data_dir
2465
+ */
2466
+ VALUE env_get_data_dirs(VALUE obj)
2467
+ {
2468
+ t_envh *eh;
2469
+ const char **data_dirs;
2470
+ int rv;
2471
+ int ln;
2472
+
2473
+ Data_Get_Struct(obj,t_envh,eh);
2474
+ if (!eh->env)
2475
+ raise(0, "env is closed");
2476
+ rv=eh->env->get_data_dirs(eh->env,&data_dirs);
2477
+ if ( rv != 0 ) {
2478
+ raise_error(rv, "env_get_data_dir: %s",db_strerror(rv));
2479
+ }
2480
+
2481
+ ln = (sizeof (data_dirs))/sizeof(data_dirs[0]);
2482
+ VALUE rb_data_dirs = rb_ary_new2(ln);
2483
+ int i;
2484
+ for (i=0; i<ln; i++) {
2485
+ rb_ary_push(rb_data_dirs, rb_str_new2(data_dirs[i]));
2486
+ }
2487
+
2488
+ return rb_data_dirs;
2489
+ }
2490
+
2491
+ /*
2492
+ * call-seq:
2493
+ * env.set_lg_dir(lg_dir) -> lg_dir
2494
+ *
2495
+ * set lg_dir
2496
+ */
2497
+ VALUE env_set_lg_dir(VALUE obj, VALUE vlg_dir)
2498
+ {
2499
+ t_envh *eh;
2500
+ const char *lg_dir;
2501
+ int rv;
2502
+
2503
+ lg_dir=StringValueCStr(vlg_dir);
2504
+
2505
+ Data_Get_Struct(obj,t_envh,eh);
2506
+ if (!eh->env)
2507
+ raise(0, "env is closed");
2508
+ rv=eh->env->set_lg_dir(eh->env,lg_dir);
2509
+ if ( rv != 0 ) {
2510
+ raise_error(rv, "env_set_lg_dir: %s",db_strerror(rv));
2511
+ }
2512
+
2513
+ return vlg_dir;
2514
+ }
2515
+
2516
+ /*
2517
+ * call-seq:
2518
+ * env.get_lg_dir -> lg_dir
2519
+ *
2520
+ * get lg_dir
2521
+ */
2522
+ VALUE env_get_lg_dir(VALUE obj)
2523
+ {
2524
+ t_envh *eh;
2525
+ const char *lg_dir;
2526
+ int rv;
2527
+
2528
+ Data_Get_Struct(obj,t_envh,eh);
2529
+ if (!eh->env)
2530
+ raise(0, "env is closed");
2531
+ rv=eh->env->get_lg_dir(eh->env,&lg_dir);
2532
+ if ( rv != 0 ) {
2533
+ raise_error(rv, "env_get_lg_dir: %s",db_strerror(rv));
2534
+ }
2535
+
2536
+ return rb_str_new2(lg_dir);
2537
+ }
2538
+
2539
+ /*
2540
+ * call-seq:
2541
+ * env.set_tmp_dir(tmp_dir) -> tmp_dir
2542
+ *
2543
+ * set tmp_dir
2544
+ */
2545
+ VALUE env_set_tmp_dir(VALUE obj, VALUE vtmp_dir)
2546
+ {
2547
+ t_envh *eh;
2548
+ const char *tmp_dir;
2549
+ int rv;
2550
+
2551
+ tmp_dir=StringValueCStr(vtmp_dir);
2552
+
2553
+ Data_Get_Struct(obj,t_envh,eh);
2554
+ if (!eh->env)
2555
+ raise(0, "env is closed");
2556
+ rv=eh->env->set_tmp_dir(eh->env,tmp_dir);
2557
+ if ( rv != 0 ) {
2558
+ raise_error(rv, "env_set_tmp_dir: %s",db_strerror(rv));
2559
+ }
2560
+
2561
+ return vtmp_dir;
2562
+ }
2563
+
2564
+ /*
2565
+ * call-seq:
2566
+ * env.get_tmp_dir -> tmp_dir
2567
+ *
2568
+ * get tmp_dir
2569
+ */
2570
+ VALUE env_get_tmp_dir(VALUE obj)
2571
+ {
2572
+ t_envh *eh;
2573
+ const char *tmp_dir;
2574
+ int rv;
2575
+
2576
+ Data_Get_Struct(obj,t_envh,eh);
2577
+ if (!eh->env)
2578
+ raise(0, "env is closed");
2579
+ rv=eh->env->get_tmp_dir(eh->env,&tmp_dir);
2580
+ if ( rv != 0 ) {
2581
+ raise_error(rv, "env_get_tmp_dir: %s",db_strerror(rv));
2582
+ }
2583
+
2584
+ return rb_str_new2(tmp_dir);
2585
+ }
2586
+
2587
+ static void txn_finish(t_txnh *txn)
2588
+ {
2589
+ if ( RTEST(ruby_debug) )
2590
+ rb_warning("%s/%d %s 0x%x",__FILE__,__LINE__,"txn_finish",txn);
2591
+
2592
+ txn->txn=NULL;
2593
+ if (txn->env) {
2594
+ rb_ary_delete(txn->env->atxn,txn->self);
2595
+ txn->env=NULL;
2596
+ }
2597
+ }
2598
+
2599
+ /*
2600
+ * call-seq:
2601
+ * txn.commit(flags) -> true
2602
+ *
2603
+ * commit a transaction
2604
+ */
2605
+ VALUE txn_commit(VALUE obj, VALUE vflags)
2606
+ {
2607
+ t_txnh *txn=NULL;
2608
+ u_int32_t flags=0;
2609
+ int rv;
2610
+
2611
+ if ( ! NIL_P(vflags))
2612
+ flags=NUM2UINT(vflags);
2613
+
2614
+ Data_Get_Struct(obj,t_txnh,txn);
2615
+
2616
+ if (!txn->txn)
2617
+ return Qfalse;
2618
+
2619
+ rv=txn->txn->commit(txn->txn,flags);
2620
+ txn_finish(txn);
2621
+ if ( rv != 0 ) {
2622
+ raise_error(rv, "txn_commit: %s",db_strerror(rv));
2623
+ return Qnil;
2624
+ }
2625
+ return Qtrue;
2626
+ }
2627
+
2628
+ /*
2629
+ * call-seq:
2630
+ * txn.abort -> true
2631
+ *
2632
+ * abort a transaction
2633
+ */
2634
+ VALUE txn_abort(VALUE obj)
2635
+ {
2636
+ t_txnh *txn=NULL;
2637
+ int rv;
2638
+
2639
+ Data_Get_Struct(obj,t_txnh,txn);
2640
+
2641
+ if (!txn->txn)
2642
+ return Qfalse;
2643
+
2644
+ rv=txn->txn->abort(txn->txn);
2645
+ txn_finish(txn);
2646
+ if ( rv != 0 ) {
2647
+ raise_error(rv, "txn_abort: %s",db_strerror(rv));
2648
+ return Qnil;
2649
+ }
2650
+ return Qtrue;
2651
+ }
2652
+
2653
+ /*
2654
+ * call-seq:
2655
+ * txn.discard -> true
2656
+ *
2657
+ * discard a transaction. Since prepare is not yet supported,
2658
+ * I don't think this has much value.
2659
+ */
2660
+ VALUE txn_discard(VALUE obj)
2661
+ {
2662
+ t_txnh *txn=NULL;
2663
+ int rv;
2664
+
2665
+ Data_Get_Struct(obj,t_txnh,txn);
2666
+
2667
+ if (!txn->txn)
2668
+ raise_error(0,"txn is closed");
2669
+
2670
+ rv=txn->txn->discard(txn->txn,NOFLAGS);
2671
+ txn_finish(txn);
2672
+ if ( rv != 0 ) {
2673
+ raise_error(rv, "txn_abort: %s",db_strerror(rv));
2674
+ return Qnil;
2675
+ }
2676
+ return Qtrue;
2677
+ }
2678
+
2679
+ /*
2680
+ * call-seq:
2681
+ * txn.tid -> Fixnum
2682
+ *
2683
+ * return the transaction id, (named tid to not conflict with
2684
+ * ruby's id method)
2685
+ */
2686
+ VALUE txn_id(VALUE obj)
2687
+ {
2688
+ t_txnh *txn=NULL;
2689
+ int rv;
2690
+
2691
+ Data_Get_Struct(obj,t_txnh,txn);
2692
+ if (!txn->txn)
2693
+ raise_error(0,"txn is closed");
2694
+
2695
+ rv=txn->txn->id(txn->txn);
2696
+ return INT2FIX(rv);
2697
+ }
2698
+
2699
+ /*
2700
+ * call-seq:
2701
+ * tx.set_timeout(timeout,flags) -> true
2702
+ *
2703
+ * set transaction lock timeout
2704
+ */
2705
+ VALUE txn_set_timeout(VALUE obj, VALUE vtimeout, VALUE vflags)
2706
+ {
2707
+ t_txnh *txn=NULL;
2708
+ db_timeout_t timeout;
2709
+ u_int32_t flags=0;
2710
+ int rv;
2711
+
2712
+ if ( ! NIL_P(vflags))
2713
+ flags=NUM2UINT(vflags);
2714
+
2715
+ if ( ! FIXNUM_P(vtimeout) )
2716
+ raise_error(0,"timeout must be a fixed integer");
2717
+ timeout=FIX2UINT(vtimeout);
2718
+
2719
+ Data_Get_Struct(obj,t_txnh,txn);
2720
+
2721
+ if (!txn->txn)
2722
+ raise_error(0,"txn is closed");
2723
+
2724
+ rv=txn->txn->set_timeout(txn->txn,timeout,flags);
2725
+ if ( rv != 0 ) {
2726
+ raise_error(rv, "txn_set_timeout: %s",db_strerror(rv));
2727
+ return Qnil;
2728
+ }
2729
+ return Qtrue;
2730
+ }
2731
+
2732
+ /*
2733
+ * Document-class: Bdb
2734
+ *
2735
+ * Ruby library that wraps the Sleepycat Berkeley DB.
2736
+ *
2737
+ * Developed against 4.3/4.4. No support for prior versions.
2738
+ */
2739
+
2740
+ void Init_bdb() {
2741
+ fv_call=rb_intern("call");
2742
+ fv_err_new=rb_intern("new");
2743
+ fv_err_code=rb_intern("@code");
2744
+ fv_err_msg=rb_intern("@message");
2745
+
2746
+ mBdb = rb_define_module("Bdb");
2747
+
2748
+ #include "bdb_aux._c"
2749
+
2750
+ cDb = rb_define_class_under(mBdb,"Db", rb_cObject);
2751
+ eDbError = rb_define_class_under(mBdb,"DbError",rb_eStandardError);
2752
+ rb_define_method(eDbError,"initialize",err_initialize,2);
2753
+ rb_define_method(eDbError,"code",err_code,0);
2754
+
2755
+ rb_define_const(cDb,"BTREE",INT2FIX((DBTYPE)(DB_BTREE)));
2756
+ rb_define_const(cDb,"HASH",INT2FIX((DBTYPE)(DB_HASH)));
2757
+ rb_define_const(cDb,"RECNO",INT2FIX((DBTYPE)(DB_RECNO)));
2758
+ rb_define_const(cDb,"QUEUE",INT2FIX((DBTYPE)(DB_QUEUE)));
2759
+ rb_define_const(cDb,"UNKNOWN",INT2FIX((DBTYPE)(DB_UNKNOWN)));
2760
+
2761
+ rb_define_alloc_func(cDb,db_alloc);
2762
+ rb_define_method(cDb,"initialize",db_initialize,0);
2763
+
2764
+ rb_define_method(cDb,"put",db_put,4);
2765
+ rb_define_method(cDb,"get",db_get,4);
2766
+ rb_define_method(cDb,"pget",db_pget,4);
2767
+ rb_define_method(cDb,"del",db_del,3);
2768
+ rb_define_method(cDb,"cursor",db_cursor,2);
2769
+ rb_define_method(cDb,"associate",db_associate,4);
2770
+ rb_define_method(cDb,"flags=",db_flags_set,1);
2771
+ rb_define_method(cDb,"flags",db_flags_get,0);
2772
+ rb_define_method(cDb,"open",db_open,6);
2773
+ rb_define_method(cDb,"close",db_close,1);
2774
+ rb_define_method(cDb,"[]",db_aget,1);
2775
+ rb_define_method(cDb,"[]=",db_aset,2);
2776
+ rb_define_method(cDb,"join",db_join,2);
2777
+ rb_define_method(cDb,"get_byteswapped",db_get_byteswapped,0);
2778
+ rb_define_method(cDb,"get_type",db_get_type,0);
2779
+ rb_define_method(cDb,"remove",db_remove,3);
2780
+ rb_define_method(cDb,"key_range",db_key_range,3);
2781
+ rb_define_method(cDb,"rename",db_rename,4);
2782
+ rb_define_method(cDb,"pagesize",db_pagesize,0);
2783
+ rb_define_method(cDb,"pagesize=",db_pagesize_set,1);
2784
+ rb_define_method(cDb,"h_ffactor",db_h_ffactor,0);
2785
+ rb_define_method(cDb,"h_ffactor=",db_h_ffactor_set,1);
2786
+ rb_define_method(cDb,"h_nelem",db_h_nelem,0);
2787
+ rb_define_method(cDb,"h_nelem=",db_h_nelem_set,1);
2788
+ rb_define_method(cDb,"stat",db_stat,2);
2789
+ cDbStat = rb_define_class_under(cDb,"Stat",rb_cObject);
2790
+ rb_define_method(cDbStat,"[]",stat_aref,1);
2791
+
2792
+ rb_define_method(cDb,"sync",db_sync,0);
2793
+ rb_define_method(cDb,"truncate",db_truncate,1);
2794
+
2795
+ #if DB_VERSION_MINOR > 3
2796
+ rb_define_method(cDb,"compact",db_compact,5);
2797
+ #endif
2798
+
2799
+ cCursor = rb_define_class_under(cDb,"Cursor",rb_cObject);
2800
+ rb_define_method(cCursor,"get",dbc_get,3);
2801
+ rb_define_method(cCursor,"pget",dbc_pget,3);
2802
+ rb_define_method(cCursor,"put",dbc_put,3);
2803
+ rb_define_method(cCursor,"close",dbc_close,0);
2804
+ rb_define_method(cCursor,"del",dbc_del,0);
2805
+ rb_define_method(cCursor,"count",dbc_count,0);
2806
+
2807
+ cEnv = rb_define_class_under(mBdb,"Env",rb_cObject);
2808
+ rb_define_singleton_method(cEnv,"new",env_new,1);
2809
+ rb_define_method(cEnv,"open",env_open,3);
2810
+ rb_define_method(cEnv,"close",env_close,0);
2811
+ rb_define_method(cEnv,"db",env_db,0);
2812
+ rb_define_method(cEnv,"cachesize=",env_set_cachesize,1);
2813
+ rb_define_method(cEnv,"cachesize",env_get_cachesize,0);
2814
+ rb_define_method(cEnv,"flags",env_get_flags,0);
2815
+ rb_define_method(cEnv,"flags_on=",env_set_flags_on,1);
2816
+ rb_define_method(cEnv,"flags_off=",env_set_flags_off,1);
2817
+ rb_define_method(cEnv,"list_dbs",env_list_dbs,0);
2818
+ rb_define_method(cEnv,"txn_begin",env_txn_begin,2);
2819
+ rb_define_method(cEnv,"txn_checkpoint",env_txn_checkpoint,3);
2820
+ rb_define_method(cEnv,"txn_stat",env_txn_stat,1);
2821
+ rb_define_method(cEnv,"set_timeout",env_set_timeout,2);
2822
+ rb_define_method(cEnv,"get_timeout",env_get_timeout,1);
2823
+ rb_define_method(cEnv,"set_tx_max",env_set_tx_max,1);
2824
+ rb_define_method(cEnv,"get_tx_max",env_get_tx_max,0);
2825
+ rb_define_method(cEnv,"report_stderr",env_report_stderr,0);
2826
+ rb_define_method(cEnv,"set_lk_detect",env_set_lk_detect,1);
2827
+ rb_define_method(cEnv,"get_lk_detect",env_get_lk_detect,0);
2828
+ rb_define_method(cEnv,"set_lk_max_locks",env_set_lk_max_locks,1);
2829
+ rb_define_method(cEnv,"get_lk_max_locks",env_get_lk_max_locks,0);
2830
+ rb_define_method(cEnv,"set_lk_max_objects",env_set_lk_max_objects,1);
2831
+ rb_define_method(cEnv,"get_lk_max_objects",env_get_lk_max_objects,0);
2832
+ rb_define_method(cEnv,"set_shm_key",env_set_shm_key,1);
2833
+ rb_define_method(cEnv,"get_shm_key",env_get_shm_key,0);
2834
+
2835
+ rb_define_method(cEnv,"set_data_dir",env_set_data_dir,1);
2836
+ rb_define_method(cEnv,"get_data_dirs",env_get_data_dirs,0);
2837
+ rb_define_method(cEnv,"set_lg_dir",env_set_lg_dir,1);
2838
+ rb_define_method(cEnv,"get_lg_dir",env_get_lg_dir,0);
2839
+ rb_define_method(cEnv,"set_tmp_dir",env_set_tmp_dir,1);
2840
+ rb_define_method(cEnv,"get_tmp_dir",env_get_tmp_dir,0);
2841
+
2842
+ cTxnStat = rb_define_class_under(mBdb,"TxnStat",rb_cObject);
2843
+ rb_define_method(cTxnStat,"[]",stat_aref,1);
2844
+
2845
+ cTxnStatActive =
2846
+ rb_define_class_under(cTxnStat,"Active",rb_cObject);
2847
+ rb_define_method(cTxnStatActive,"[]",stat_aref,1);
2848
+
2849
+ cTxn = rb_define_class_under(mBdb,"Txn",rb_cObject);
2850
+ rb_define_method(cTxn,"commit",txn_commit,1);
2851
+ rb_define_method(cTxn,"abort",txn_abort,0);
2852
+ rb_define_method(cTxn,"discard",txn_discard,0);
2853
+ rb_define_method(cTxn,"tid",txn_id,0);
2854
+ rb_define_method(cTxn,"set_timeout",txn_set_timeout,2);
2855
+ }