Skip to content

Commit 1a21fff

Browse files
committed
Dbi and Cursor to more extensively check Txn state
1 parent 40f00bb commit 1a21fff

3 files changed

Lines changed: 105 additions & 84 deletions

File tree

src/main/java/org/lmdbjava/Cursor.java

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import static org.lmdbjava.ResultCodeMapper.checkRc;
2828
import org.lmdbjava.Txn.CommittedException;
2929
import org.lmdbjava.Txn.ReadOnlyRequiredException;
30+
import org.lmdbjava.Txn.ReadWriteRequiredException;
3031
import static org.lmdbjava.ValueBuffers.createVal;
3132
import static org.lmdbjava.ValueBuffers.wrap;
3233

@@ -37,7 +38,7 @@ public class Cursor implements AutoCloseable {
3738

3839
private boolean closed;
3940
private final Pointer ptr;
40-
private final Txn tx;
41+
private Txn tx;
4142

4243
Cursor(final Pointer ptr, final Txn tx) {
4344
this.ptr = ptr;
@@ -79,7 +80,7 @@ public void close() throws CommittedException {
7980
public long count() throws LmdbNativeException, CommittedException,
8081
ClosedException {
8182
checkNotClosed();
82-
checkNotCommitted();
83+
tx.checkNotCommitted();
8384
final NativeLongByReference longByReference = new NativeLongByReference();
8485
checkRc(lib.mdb_cursor_count(ptr, longByReference));
8586
return longByReference.longValue();
@@ -90,14 +91,16 @@ public long count() throws LmdbNativeException, CommittedException,
9091
* <p>
9192
* This function deletes the key/data pair to which the cursor refers.
9293
*
93-
* @throws LmdbNativeException if a native C error occurred
94-
* @throws CommittedException if the transaction was committed
95-
* @throws ClosedException if the cursor is already closed
94+
* @throws LmdbNativeException if a native C error occurred
95+
* @throws CommittedException if the transaction was committed
96+
* @throws ClosedException if the cursor is already closed
97+
* @throws ReadWriteRequiredException if cursor using a read-only transaction
9698
*/
9799
public void delete() throws LmdbNativeException, CommittedException,
98-
ClosedException {
100+
ClosedException, ReadWriteRequiredException {
99101
checkNotClosed();
100-
checkNotCommitted();
102+
tx.checkNotCommitted();
103+
tx.checkWritesAllowed();
101104
checkRc(lib.mdb_cursor_del(ptr, 0));
102105
}
103106

@@ -121,7 +124,7 @@ public void get(final ByteBuffer key, final ByteBuffer val, final CursorOp op)
121124
requireNonNull(val);
122125
requireNonNull(op);
123126
checkNotClosed();
124-
checkNotCommitted();
127+
tx.checkNotCommitted();
125128
final MDB_val k;
126129
final MDB_val v = new MDB_val(runtime);
127130
// set operations 15, 16, 17
@@ -144,19 +147,21 @@ public void get(final ByteBuffer key, final ByteBuffer val, final CursorOp op)
144147
* @param key key to store
145148
* @param val data to store
146149
* @param op options for this operation
147-
* @throws BufferNotDirectException if a passed buffer is invalid
148-
* @throws LmdbNativeException if a native C error occurred
149-
* @throws CommittedException if the transaction was committed
150-
* @throws ClosedException if the cursor is already closed
150+
* @throws BufferNotDirectException if a passed buffer is invalid
151+
* @throws LmdbNativeException if a native C error occurred
152+
* @throws CommittedException if the transaction was committed
153+
* @throws ClosedException if the cursor is already closed
154+
* @throws ReadWriteRequiredException if cursor using a read-only transaction
151155
*/
152156
public void put(final ByteBuffer key, final ByteBuffer val,
153157
final PutFlags... op)
154158
throws BufferNotDirectException, LmdbNativeException, CommittedException,
155-
ClosedException {
159+
ClosedException, ReadWriteRequiredException {
156160
requireNonNull(key);
157161
requireNonNull(val);
158162
checkNotClosed();
159-
checkNotCommitted();
163+
tx.checkNotCommitted();
164+
tx.checkWritesAllowed();
160165
final MDB_val k = createVal(key);
161166
final MDB_val v = createVal(val);
162167
final int flags = mask(op);
@@ -176,18 +181,19 @@ public void put(final ByteBuffer key, final ByteBuffer val,
176181
* @param tx transaction handle
177182
* @throws LmdbNativeException if a native C error occurred
178183
* @throws ClosedException if the cursor is already closed
179-
* @throws CommittedException if the transaction was committed
180-
* @throws ReadOnlyRequiredException if a read-write transaction is in use
184+
* @throws CommittedException if the new transaction was committed
185+
* @throws ReadOnlyRequiredException if a R-W transaction was presented
181186
*/
182187
public void renew(final Txn tx)
183188
throws LmdbNativeException, ClosedException,
184189
ReadOnlyRequiredException,
185190
CommittedException {
191+
requireNonNull(tx);
186192
checkNotClosed();
187-
checkNotCommitted();
188-
if (!tx.isReadOnly()) {
189-
throw new ReadOnlyRequiredException();
190-
}
193+
this.tx.checkReadOnly(); // existing
194+
tx.checkReadOnly(); // new
195+
tx.checkNotCommitted(); // new
196+
this.tx = tx;
191197
checkRc(lib.mdb_cursor_renew(tx.ptr, ptr));
192198
}
193199

@@ -197,12 +203,6 @@ private void checkNotClosed() throws ClosedException {
197203
}
198204
}
199205

200-
private void checkNotCommitted() throws CommittedException {
201-
if (tx.isCommitted()) {
202-
throw new CommittedException();
203-
}
204-
}
205-
206206
/**
207207
* Cursor has already been closed.
208208
*/

src/main/java/org/lmdbjava/Dbi.java

Lines changed: 77 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import static org.lmdbjava.MaskedFlag.mask;
1515
import static org.lmdbjava.ResultCodeMapper.checkRc;
1616
import org.lmdbjava.Txn.CommittedException;
17+
import org.lmdbjava.Txn.ReadWriteRequiredException;
1718
import static org.lmdbjava.TxnFlags.MDB_RDONLY;
1819
import static org.lmdbjava.ValueBuffers.createVal;
1920
import static org.lmdbjava.ValueBuffers.wrap;
@@ -33,18 +34,19 @@ public final class Dbi {
3334
* The passed transaction will automatically commit and the database handle
3435
* will become available to other transactions.
3536
*
36-
* @param tx transaction to open and commit this database within (required)
37+
* @param tx transaction to open and commit this database within (not null;
38+
* not committed; must be R-W)
3739
* @param name name of the database (or null if no name is required)
3840
* @param flags to open the database with
39-
* @throws CommittedException if already committed
40-
* @throws LmdbNativeException if a native C error occurred
41+
* @throws CommittedException if already committed
42+
* @throws LmdbNativeException if a native C error occurred
43+
* @throws ReadWriteRequiredException if a read-only transaction presented
4144
*/
4245
public Dbi(final Txn tx, final String name, final DbiFlags... flags)
43-
throws CommittedException, LmdbNativeException {
46+
throws CommittedException, LmdbNativeException, ReadWriteRequiredException {
4447
requireNonNull(tx);
45-
if (tx.isCommitted()) {
46-
throw new CommittedException();
47-
}
48+
tx.checkNotCommitted();
49+
tx.checkWritesAllowed();
4850
this.env = tx.env;
4951
this.name = name;
5052
final int flagsMask = mask(flags);
@@ -58,17 +60,18 @@ public Dbi(final Txn tx, final String name, final DbiFlags... flags)
5860
* <p>
5961
* WARNING: Convenience method. Do not use if latency sensitive.
6062
* <p>
61-
* @param key The key to delete from the database (required)
62-
* @throws CommittedException if already committed
63-
* @throws BufferNotDirectException if a passed buffer is invalid
64-
* @throws NotOpenException if the environment is not currently open
65-
* @throws LmdbNativeException if a native C error occurred
63+
* @param key key to delete from the database (not null)
64+
* @throws CommittedException if already committed
65+
* @throws BufferNotDirectException if a passed buffer is invalid
66+
* @throws NotOpenException if the environment is not currently open
67+
* @throws LmdbNativeException if a native C error occurred
68+
* @throws ReadWriteRequiredException if a read-only transaction presented
6669
* @see #delete(Txn, ByteBuffer, ByteBuffer)
6770
*/
6871
public void delete(final ByteBuffer key) throws
6972
CommittedException, BufferNotDirectException, LmdbNativeException,
70-
NotOpenException {
71-
try (Txn tx = new Txn(env)) {
73+
NotOpenException, ReadWriteRequiredException {
74+
try (final Txn tx = new Txn(env)) {
7275
delete(tx, key);
7376
tx.commit();
7477
}
@@ -77,15 +80,17 @@ public void delete(final ByteBuffer key) throws
7780
/**
7881
* Deletes the key using the passed transaction.
7982
*
80-
* @param tx Transaction handle
81-
* @param key The key to delete from the database (requierd)
82-
* @throws CommittedException if already committed
83-
* @throws BufferNotDirectException if a passed buffer is invalid
84-
* @throws LmdbNativeException if a native C error occurred
83+
* @param tx transaction handle (not null; not committed; must be R-W)
84+
* @param key key to delete from the database (not null)
85+
* @throws CommittedException if already committed
86+
* @throws BufferNotDirectException if a passed buffer is invalid
87+
* @throws LmdbNativeException if a native C error occurred
88+
* @throws ReadWriteRequiredException if a read-only transaction presented
8589
* @see #delete(Txn, ByteBuffer, ByteBuffer)
8690
*/
8791
public void delete(final Txn tx, final ByteBuffer key) throws
88-
CommittedException, BufferNotDirectException, LmdbNativeException {
92+
CommittedException, BufferNotDirectException, LmdbNativeException,
93+
ReadWriteRequiredException {
8994
delete(tx, key, null);
9095
}
9196

@@ -101,17 +106,22 @@ public void delete(final Txn tx, final ByteBuffer key) throws
101106
* This function will throw {@link KeyNotFoundException} if the key/data pair
102107
* is not found.
103108
*
104-
* @param tx Transaction handle
105-
* @param key The key to delete from the database
106-
* @param val The value to delete from the database (null permitted)
107-
* @throws CommittedException if already committed
108-
* @throws BufferNotDirectException if a passed buffer is invalid
109-
* @throws LmdbNativeException if a native C error occurred
109+
* @param tx transaction handle (not null; not committed; must be R-W)
110+
* @param key key to delete from the database (not null)
111+
* @param val value to delete from the database (null permitted)
112+
* @throws CommittedException if already committed
113+
* @throws BufferNotDirectException if a passed buffer is invalid
114+
* @throws LmdbNativeException if a native C error occurred
115+
* @throws ReadWriteRequiredException if a read-only transaction presented
110116
*/
111117
public void delete(final Txn tx, final ByteBuffer key, final ByteBuffer val)
112118
throws
113-
CommittedException, BufferNotDirectException, LmdbNativeException {
114-
119+
CommittedException, BufferNotDirectException, LmdbNativeException,
120+
ReadWriteRequiredException {
121+
requireNonNull(tx);
122+
requireNonNull(key);
123+
tx.checkNotCommitted();
124+
tx.checkWritesAllowed();
115125
final MDB_val k = createVal(key);
116126
final MDB_val v = val == null ? null : createVal(key);
117127

@@ -123,9 +133,8 @@ public void delete(final Txn tx, final ByteBuffer key, final ByteBuffer val)
123133
* <p>
124134
* WARNING: Convenience method. Do not use if latency sensitive.
125135
* <p>
126-
* @param key The key to get from the database
127-
* @return A value placeholder for the memory address to be wrapped if found
128-
* by key
136+
* @param key key to get from the database (not null)
137+
* @return the value found
129138
* @throws CommittedException if already committed
130139
* @throws BufferNotDirectException if a passed buffer is invalid
131140
* @throws NotOpenException if the environment is not currently open
@@ -135,7 +144,7 @@ public void delete(final Txn tx, final ByteBuffer key, final ByteBuffer val)
135144
public ByteBuffer get(final ByteBuffer key) throws
136145
CommittedException, BufferNotDirectException, LmdbNativeException,
137146
NotOpenException {
138-
try (Txn tx = new Txn(env, MDB_RDONLY)) {
147+
try (final Txn tx = new Txn(env, MDB_RDONLY)) {
139148
return get(tx, key);
140149
}
141150
}
@@ -152,18 +161,18 @@ public ByteBuffer get(final ByteBuffer key) throws
152161
* the key will be returned. Retrieval of other items requires the use of
153162
* #mdb_cursor_get().
154163
*
155-
* @param tx transaction handle
156-
* @param key The key to search for in the database
157-
* @return A value placeholder for the memory address to be wrapped if found
158-
* by key
164+
* @param tx transaction handle (not null; not committed)
165+
* @param key key to search for in the database (not null)
166+
* @return the value found
159167
* @throws CommittedException if already committed
160168
* @throws BufferNotDirectException if a passed buffer is invalid
161169
* @throws LmdbNativeException if a native C error occurred
162170
*/
163171
public ByteBuffer get(final Txn tx, final ByteBuffer key) throws
164172
CommittedException, BufferNotDirectException, LmdbNativeException {
165-
assert key.isDirect();
166-
173+
requireNonNull(tx);
174+
requireNonNull(key);
175+
tx.checkNotCommitted();
167176
final MDB_val k = createVal(key);
168177
final MDB_val v = new MDB_val(runtime);
169178

@@ -196,11 +205,15 @@ public String getName() {
196205
* explicitly, before or after its transaction ends. It can be reused with
197206
* {@link Cursor#renew(org.lmdbjava.Txn)} before finally closing it.
198207
*
199-
* @param tx transaction handle
208+
* @param tx transaction handle (not null; not committed)
200209
* @return cursor handle
201210
* @throws LmdbNativeException if a native C error occurred
211+
* @throws CommittedException if already committed
202212
*/
203-
public Cursor openCursor(final Txn tx) throws LmdbNativeException {
213+
public Cursor openCursor(final Txn tx) throws LmdbNativeException,
214+
CommittedException {
215+
requireNonNull(tx);
216+
tx.checkNotCommitted();
204217
final PointerByReference ptr = new PointerByReference();
205218
checkRc(lib.mdb_cursor_open(tx.ptr, dbi, ptr));
206219
return new Cursor(ptr.getValue(), tx);
@@ -210,19 +223,20 @@ public Cursor openCursor(final Txn tx) throws LmdbNativeException {
210223
* Starts a new read-write transaction and puts the key/data pair.
211224
* <p>
212225
* WARNING: Convenience method. Do not use if latency sensitive.
213-
*
214-
* @param key The key to store in the database
215-
* @param val The value to store in the database
216-
* @throws CommittedException if already committed
217-
* @throws BufferNotDirectException if a passed buffer is invalid
218-
* @throws NotOpenException if the environment is not currently open
219-
* @throws LmdbNativeException if a native C error occurred
226+
*
227+
* @param key key to store in the database (not null)
228+
* @param val value to store in the database (not null)
229+
* @throws CommittedException if already committed
230+
* @throws BufferNotDirectException if a passed buffer is invalid
231+
* @throws NotOpenException if the environment is not currently open
232+
* @throws LmdbNativeException if a native C error occurred
233+
* @throws ReadWriteRequiredException if a read-only transaction presented
220234
* @see Dbi#put(Txn, ByteBuffer, ByteBuffer, PutFlags...)
221235
*/
222236
public void put(final ByteBuffer key, final ByteBuffer val) throws
223237
CommittedException, BufferNotDirectException, LmdbNativeException,
224-
NotOpenException {
225-
try (Txn tx = new Txn(env)) {
238+
NotOpenException, ReadWriteRequiredException {
239+
try (final Txn tx = new Txn(env)) {
226240
put(tx, key, val);
227241
tx.commit();
228242
}
@@ -236,18 +250,24 @@ public void put(final ByteBuffer key, final ByteBuffer val) throws
236250
* duplicates are disallowed, or adding a duplicate data item if duplicates
237251
* are allowed ({@link DbiFlags#MDB_DUPSORT}).
238252
*
239-
* @param tx transaction handle
240-
* @param key The key to store in the database
241-
* @param val The value to store in the database
253+
* @param tx transaction handle (not null; not committed; must be R-W)
254+
* @param key key to store in the database (not null)
255+
* @param val value to store in the database (not null)
242256
* @param flags Special options for this operation
243-
* @throws CommittedException if already committed
244-
* @throws BufferNotDirectException if a passed buffer is invalid
245-
* @throws LmdbNativeException if a native C error occurred
257+
* @throws CommittedException if already committed
258+
* @throws BufferNotDirectException if a passed buffer is invalid
259+
* @throws LmdbNativeException if a native C error occurred
260+
* @throws ReadWriteRequiredException if a read-only transaction presented
246261
*/
247262
public void put(final Txn tx, final ByteBuffer key, final ByteBuffer val,
248263
final PutFlags... flags)
249-
throws CommittedException, BufferNotDirectException, LmdbNativeException {
250-
264+
throws CommittedException, BufferNotDirectException, LmdbNativeException,
265+
ReadWriteRequiredException {
266+
requireNonNull(tx);
267+
requireNonNull(key);
268+
requireNonNull(val);
269+
tx.checkNotCommitted();
270+
tx.checkWritesAllowed();
251271
final MDB_val k = createVal(key);
252272
final MDB_val v = createVal(val);
253273
int mask = mask(flags);

src/test/java/org/lmdbjava/DbiTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import static org.lmdbjava.TestUtils.POSIX_MODE;
2727
import static org.lmdbjava.TestUtils.createBb;
2828
import org.lmdbjava.Txn.CommittedException;
29+
import org.lmdbjava.Txn.ReadWriteRequiredException;
2930

3031
public class DbiTest {
3132

@@ -147,7 +148,7 @@ public void testParallelWritesStress() throws Exception {
147148
try {
148149
dbi.put(createBb(random.nextInt()), createBb(random.nextInt()));
149150
} catch (CommittedException | LmdbNativeException | NotOpenException |
150-
BufferNotDirectException e) {
151+
BufferNotDirectException | ReadWriteRequiredException e) {
151152
throw new RuntimeException(e);
152153
}
153154
}

0 commit comments

Comments
 (0)