lmdb 0.6.7 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 79e94aae1eb049c37cc44abdde80712dd5c8fa900d1ec2b68c33b773862f4e1a
4
- data.tar.gz: ccf377de1e2e910c81459fad2fe0bce8eb831ca40e3f95fc311ef7ccd3eb1e57
3
+ metadata.gz: 27c33b768709ded58c1414c9102a0a4024219abb305e875279b705d6d784aa68
4
+ data.tar.gz: 6d9b3f61f5ec981329fe42b6c310aca2760d5569f72202cbbe590258d1693591
5
5
  SHA512:
6
- metadata.gz: 0e3d55b2b4486caea098bb175b6ae4aeff87d770010ce4359c003e1707e6089da204a1f6f62fd0bf1e300170e0954b6245dc6fd3d9e951211d87f0b94cf958e8
7
- data.tar.gz: 96e68bf724dfc2f47a5bfd72dbc5fcda8ede5e8b7edc69e1cb2e55f5c6230997bd319e04d47bbea8aaca39c5d0e322f70e37b2f9d0cb8f5e991b50ca7df51469
6
+ metadata.gz: 8e1816c955d847948075046de78e382316ea599e56090bde14c669b4318cb12fc42d0c90cfed3089dded7be185c2589b99d756b343ae126c4e2c6f425e19451e
7
+ data.tar.gz: 2bab39420ba0d6243bf261239a1264694e8be367f2b221522b3e596acfc3697225190cd8b6ba41fc34596e73731cc7f870312e13b15f36ae66f9041186268eb0
data/CHANGES CHANGED
@@ -1,3 +1,12 @@
1
+ 0.7.0
2
+
3
+ * Nesting read-only transactions finally works, by making it a noop
4
+ and using the parent transaction (djt)
5
+
6
+ 0.6.6
7
+
8
+ * added `put?` and `delete?` (djt)
9
+
1
10
  0.5.1
2
11
 
3
12
  * Move read-only operations to read-only transactions (djt)
@@ -137,6 +137,28 @@ static VALUE transaction_is_readonly(VALUE self) {
137
137
  return (transaction->flags & MDB_RDONLY) ? Qtrue : Qfalse;
138
138
  }
139
139
 
140
+ /**
141
+ * @overload finished?
142
+ * @note This predicate is considered *unstable*; do not get used to it.
143
+ * @return [false,true] whether the transaction is finished.
144
+ */
145
+ static VALUE transaction_is_finished(VALUE self) {
146
+ TRANSACTION(self, transaction);
147
+ // MDB_TXN_FINISHED
148
+ return (transaction->flags & 0x01) ? Qtrue : Qfalse;
149
+ }
150
+
151
+ /**
152
+ * @overload error?
153
+ * @note This predicate is considered *unstable*; do not get used to it.
154
+ * @return [false,true] whether the transaction incurred an error.
155
+ */
156
+ static VALUE transaction_is_error(VALUE self) {
157
+ TRANSACTION(self, transaction);
158
+ // MDB_TXN_ERROR
159
+ return (transaction->flags & 0x02) ? Qtrue : Qfalse;
160
+ }
161
+
140
162
 
141
163
  static void transaction_finish(VALUE self, int commit) {
142
164
  TRANSACTION(self, transaction);
@@ -285,69 +307,92 @@ static void stop_txn_begin(void *arg)
285
307
  */
286
308
 
287
309
  static VALUE with_transaction(VALUE venv, VALUE(*fn)(VALUE), VALUE arg, int flags) {
288
- ENVIRONMENT(venv, environment);
289
-
290
- MDB_txn* txn;
291
- TxnArgs txn_args;
310
+ ENVIRONMENT(venv, environment);
311
+
312
+ MDB_txn* txn;
313
+ TxnArgs txn_args;
314
+
315
+ VALUE thread = rb_thread_current();
316
+ VALUE vparent = environment_active_txn(venv);
317
+
318
+ //
319
+ Transaction* tparent = NULL;
320
+ if (vparent && !NIL_P(vparent))
321
+ Data_Get_Struct(vparent, Transaction, tparent);
322
+
323
+ /*
324
+ * If the requested transaction is read-only and there is a parent
325
+ * transaction (whether read-only or not), we need to use the
326
+ * parent transaction.
327
+ *
328
+ * XXX except conceivably you could do a transaction (RO or RW)
329
+ * that spawns a thread that opens another transaction.
330
+ *
331
+ * note we do *NOT* re-begin the parent transaction, nor do we
332
+ * want to commit it
333
+ *
334
+ */
335
+
336
+ if (tparent && flags & MDB_RDONLY) {
337
+ // We are reusing the parent transaction.
292
338
 
293
- VALUE thread = rb_thread_current();
294
- VALUE vparent = environment_active_txn(venv);
339
+ int exception;
340
+ VALUE ret = rb_protect(fn, NIL_P(arg) ? vparent : arg, &exception);
295
341
 
296
- Transaction* tparent = NULL;
297
- if (vparent && !NIL_P(vparent))
298
- Data_Get_Struct(vparent, Transaction, tparent);
342
+ if (exception) {
343
+ // this is a cargo cult; i just copied it from below
344
+ if (vparent == environment_active_txn(venv)) transaction_abort(vparent);
345
+ rb_jump_tag(exception);
346
+ }
347
+ return ret;
348
+ }
349
+ else {
350
+ // We are creating a new transaction.
299
351
 
300
352
  // XXX note this is a cursed goto loop that could almost certainly
301
353
  // be rewritten as a do-while
302
- retry:
354
+ retry:
303
355
  txn = NULL;
304
356
 
305
357
  txn_args.env = environment->env;
306
- txn_args.parent = active_txn(venv);
358
+ txn_args.parent = active_txn(venv); // this returns an MDB_txn
307
359
  txn_args.flags = flags;
308
360
  txn_args.htxn = &txn;
309
361
  txn_args.result = 0;
310
362
  txn_args.stop = 0;
311
363
 
312
- if (flags & MDB_RDONLY) {
313
- if (tparent && tparent->flags & MDB_RDONLY)
314
- // this is a no-op: put the same actual transaction in a
315
- // different wrapper struct
316
- txn = txn_args.parent;
317
- else
318
- // this will return an error if the parent transaction is
319
- // read-write, so we don't need to handle the case explicitly
320
- call_txn_begin(&txn_args);
321
- }
364
+ if (flags & MDB_RDONLY)
365
+ call_txn_begin(&txn_args);
322
366
  else {
323
- if (tparent) {
324
- // first we have to determine if we're on the same thread
325
- // as the parent, which in turn must be the same as the
326
- // environment's registry for which thread has the
327
- // read-write transaction
328
- if (thread != tparent->thread ||
329
- thread != environment->rw_txn_thread)
330
- rb_raise(cError,
331
- "Attempt to nest transaction on a different thread");
367
+ if (tparent) {
368
+ // first we have to determine if we're on the same thread
369
+ // as the parent, which in turn must be the same as the
370
+ // environment's registry for which thread has the
371
+ // read-write transaction
372
+ if (thread != tparent->thread ||
373
+ thread != environment->rw_txn_thread)
374
+ rb_raise(cError,
375
+ "Attempt to nest transaction on a different thread");
376
+ }
377
+
378
+ // try to acquire the new transaction
379
+ CALL_WITHOUT_GVL(call_txn_begin, &txn_args, stop_txn_begin, &txn_args);
380
+
381
+ if (txn_args.stop || !txn) {
382
+ // !txn is when rb_thread_call_without_gvl2
383
+ // returns before calling txn_begin
384
+ if (txn) {
385
+ mdb_txn_abort(txn);
386
+ txn_args.result = 0;
332
387
  }
333
388
 
334
- CALL_WITHOUT_GVL(call_txn_begin, &txn_args, stop_txn_begin, &txn_args);
389
+ //rb_warn("got here lol");
390
+ rb_thread_check_ints();
391
+ goto retry; // in what cases do we get here?
392
+ }
335
393
 
336
- if (txn_args.stop || !txn) {
337
- // !txn is when rb_thread_call_without_gvl2
338
- // returns before calling txn_begin
339
- if (txn) {
340
- mdb_txn_abort(txn);
341
- txn_args.result = 0;
342
- }
343
-
344
- //rb_warn("got here lol");
345
- rb_thread_check_ints();
346
- goto retry; // in what cases do we get here?
347
- }
348
-
349
- // set the thread
350
- environment->rw_txn_thread = thread;
394
+ // set the thread
395
+ environment->rw_txn_thread = thread;
351
396
  }
352
397
 
353
398
  // this will raise unless result is zero
@@ -373,14 +418,15 @@ static VALUE with_transaction(VALUE venv, VALUE(*fn)(VALUE), VALUE arg, int flag
373
418
  VALUE ret = rb_protect(fn, NIL_P(arg) ? vtxn : arg, &exception);
374
419
 
375
420
  if (exception) {
376
- // rb_warn("lol got exception");
377
- if (vtxn == environment_active_txn(venv))
378
- transaction_abort(vtxn);
379
- rb_jump_tag(exception);
421
+ // rb_warn("lol got exception");
422
+ if (vtxn == environment_active_txn(venv))
423
+ transaction_abort(vtxn);
424
+ rb_jump_tag(exception);
380
425
  }
381
426
  if (vtxn == environment_active_txn(venv))
382
- transaction_commit(vtxn);
427
+ transaction_commit(vtxn);
383
428
  return ret;
429
+ }
384
430
  }
385
431
 
386
432
  static void environment_check(Environment* environment) {
@@ -773,23 +819,24 @@ static void environment_set_active_txn(VALUE self, VALUE thread, VALUE txn) {
773
819
  }
774
820
  }
775
821
 
822
+ static MDB_txn* extract_txn(VALUE vtxn) {
823
+ if (NIL_P(vtxn)) return NULL;
824
+ TRANSACTION(vtxn, transaction);
825
+ if (!transaction->txn) rb_raise(cError, "Transaction is already terminated");
826
+ if (transaction->thread != rb_thread_current())
827
+ rb_raise(cError, "Transaction is from another thread");
828
+ return transaction->txn;
829
+ }
776
830
 
777
831
  static MDB_txn* active_txn(VALUE self) {
778
832
  VALUE vtxn = environment_active_txn(self);
779
- if (NIL_P(vtxn))
780
- return 0;
781
- TRANSACTION(vtxn, transaction);
782
- if (!transaction->txn)
783
- rb_raise(cError, "Transaction is already terminated");
784
- if (transaction->thread != rb_thread_current())
785
- rb_raise(cError, "Wrong thread");
786
- return transaction->txn;
833
+ return extract_txn(vtxn);
787
834
  }
788
835
 
789
836
  static MDB_txn* need_txn(VALUE self) {
790
837
  MDB_txn* txn = active_txn(self);
791
- if (!txn)
792
- rb_raise(cError, "No active transaction");
838
+
839
+ if (!txn) rb_raise(cError, "No active transaction");
793
840
  return txn;
794
841
  }
795
842
 
@@ -1782,6 +1829,8 @@ void Init_lmdb_ext() {
1782
1829
  rb_define_method(cTransaction, "abort", transaction_abort, 0);
1783
1830
  rb_define_method(cTransaction, "env", transaction_env, 0);
1784
1831
  rb_define_method(cTransaction, "readonly?", transaction_is_readonly, 0);
1832
+ rb_define_method(cTransaction, "finished?", transaction_is_finished, 0);
1833
+ rb_define_method(cTransaction, "error?", transaction_is_error, 0);
1785
1834
 
1786
1835
  /**
1787
1836
  * Document-class: LMDB::Cursor
@@ -104,12 +104,12 @@ typedef struct {
104
104
  } EnvironmentOptions;
105
105
 
106
106
  typedef struct {
107
- MDB_env *env;
108
- MDB_txn *parent;
109
- unsigned int flags;
110
- MDB_txn **htxn;
111
- int result;
112
- int stop;
107
+ MDB_env *env;
108
+ MDB_txn *parent;
109
+ unsigned int flags;
110
+ MDB_txn **htxn; // not sure why this is a pointer to a pointer
111
+ int result;
112
+ int stop;
113
113
  } TxnArgs;
114
114
 
115
115
  static VALUE cEnvironment, cDatabase, cTransaction, cCursor, cError;
data/lib/lmdb/database.rb CHANGED
@@ -126,7 +126,7 @@ module LMDB
126
126
 
127
127
  ret = false
128
128
  # read-only txn was having trouble being nested inside a read-write
129
- maybe_txn true do
129
+ maybe_txn(true) do
130
130
  # env.transaction true do
131
131
  # env.transaction do
132
132
  cursor { |c| ret = !!c.set(key, value) }
@@ -145,12 +145,20 @@ module LMDB
145
145
  # @return [void]
146
146
  #
147
147
  def put?(key, value = nil, **options)
148
- flags = {}
149
- flags[dupsort? ? :nodupdata : :nooverwrite] = true
150
- begin
151
- put key, value, **options.merge(flags)
152
- rescue LMDB::Error::KEYEXIST
153
- nil
148
+ # early bailout
149
+ return if value.nil?
150
+
151
+ flags = { (dupsort? ? :nodupdata : :nooverwrite) => true }
152
+
153
+ env.transaction do |txn|
154
+ # begin
155
+ put(key, value, **options.merge(flags)) unless has?(key, value)
156
+ # rescue LMDB::Error::KEYEXIST
157
+ # # this should never be reached lol
158
+ # # warn 'lol'
159
+ # txn.abort
160
+ # nil
161
+ # end
154
162
  end
155
163
  end
156
164
 
@@ -164,10 +172,13 @@ module LMDB
164
172
  # @return [void]
165
173
  #
166
174
  def delete?(key, value = nil)
167
- begin
168
- delete key, value
169
- rescue LMDB::Error::NOTFOUND
170
- nil
175
+ env.transaction do |txn|
176
+ begin
177
+ delete key, value
178
+ rescue LMDB::Error::NOTFOUND
179
+ txn.abort
180
+ nil
181
+ end
171
182
  end
172
183
  end
173
184
 
data/lib/lmdb/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module LMDB
2
- VERSION = '0.6.7'.freeze
2
+ VERSION = '0.7.2'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lmdb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.7
4
+ version: 0.7.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Mendler
8
8
  - Dorian Taylor
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-11-21 00:00:00.000000000 Z
11
+ date: 2026-05-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -143,7 +143,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
143
143
  - !ruby/object:Gem::Version
144
144
  version: '0'
145
145
  requirements: []
146
- rubygems_version: 3.6.3
146
+ rubygems_version: 3.6.7
147
147
  specification_version: 4
148
148
  summary: Ruby bindings to Lightning MDB
149
149
  test_files: