1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
2 // This source code is licensed under both the GPLv2 (found in the
3 // COPYING file in the root directory) and Apache 2.0 License
4 // (found in the LICENSE.Apache file in the root directory).
5 //
6 // This file implements the "bridge" between Java and C++
7 // for rocksdb::TransactionDB.
8
9 #include <jni.h>
10 #include <functional>
11 #include <memory>
12 #include <utility>
13
14 #include "include/org_rocksdb_TransactionDB.h"
15
16 #include "rocksdb/options.h"
17 #include "rocksdb/utilities/transaction.h"
18 #include "rocksdb/utilities/transaction_db.h"
19
20 #include "rocksjni/portal.h"
21
22 /*
23 * Class: org_rocksdb_TransactionDB
24 * Method: open
25 * Signature: (JJLjava/lang/String;)J
26 */
Java_org_rocksdb_TransactionDB_open__JJLjava_lang_String_2(JNIEnv * env,jclass,jlong joptions_handle,jlong jtxn_db_options_handle,jstring jdb_path)27 jlong Java_org_rocksdb_TransactionDB_open__JJLjava_lang_String_2(
28 JNIEnv* env, jclass, jlong joptions_handle,
29 jlong jtxn_db_options_handle, jstring jdb_path) {
30 auto* options = reinterpret_cast<rocksdb::Options*>(joptions_handle);
31 auto* txn_db_options =
32 reinterpret_cast<rocksdb::TransactionDBOptions*>(jtxn_db_options_handle);
33 rocksdb::TransactionDB* tdb = nullptr;
34 const char* db_path = env->GetStringUTFChars(jdb_path, nullptr);
35 if (db_path == nullptr) {
36 // exception thrown: OutOfMemoryError
37 return 0;
38 }
39 rocksdb::Status s =
40 rocksdb::TransactionDB::Open(*options, *txn_db_options, db_path, &tdb);
41 env->ReleaseStringUTFChars(jdb_path, db_path);
42
43 if (s.ok()) {
44 return reinterpret_cast<jlong>(tdb);
45 } else {
46 rocksdb::RocksDBExceptionJni::ThrowNew(env, s);
47 return 0;
48 }
49 }
50
51 /*
52 * Class: org_rocksdb_TransactionDB
53 * Method: open
54 * Signature: (JJLjava/lang/String;[[B[J)[J
55 */
Java_org_rocksdb_TransactionDB_open__JJLjava_lang_String_2_3_3B_3J(JNIEnv * env,jclass,jlong jdb_options_handle,jlong jtxn_db_options_handle,jstring jdb_path,jobjectArray jcolumn_names,jlongArray jcolumn_options_handles)56 jlongArray Java_org_rocksdb_TransactionDB_open__JJLjava_lang_String_2_3_3B_3J(
57 JNIEnv* env, jclass, jlong jdb_options_handle,
58 jlong jtxn_db_options_handle, jstring jdb_path, jobjectArray jcolumn_names,
59 jlongArray jcolumn_options_handles) {
60 const char* db_path = env->GetStringUTFChars(jdb_path, nullptr);
61 if (db_path == nullptr) {
62 // exception thrown: OutOfMemoryError
63 return nullptr;
64 }
65
66 const jsize len_cols = env->GetArrayLength(jcolumn_names);
67 if (env->EnsureLocalCapacity(len_cols) != 0) {
68 // out of memory
69 env->ReleaseStringUTFChars(jdb_path, db_path);
70 return nullptr;
71 }
72
73 jlong* jco = env->GetLongArrayElements(jcolumn_options_handles, nullptr);
74 if (jco == nullptr) {
75 // exception thrown: OutOfMemoryError
76 env->ReleaseStringUTFChars(jdb_path, db_path);
77 return nullptr;
78 }
79 std::vector<rocksdb::ColumnFamilyDescriptor> column_families;
80 for (int i = 0; i < len_cols; i++) {
81 const jobject jcn = env->GetObjectArrayElement(jcolumn_names, i);
82 if (env->ExceptionCheck()) {
83 // exception thrown: ArrayIndexOutOfBoundsException
84 env->ReleaseLongArrayElements(jcolumn_options_handles, jco, JNI_ABORT);
85 env->ReleaseStringUTFChars(jdb_path, db_path);
86 return nullptr;
87 }
88 const jbyteArray jcn_ba = reinterpret_cast<jbyteArray>(jcn);
89 jbyte* jcf_name = env->GetByteArrayElements(jcn_ba, nullptr);
90 if (jcf_name == nullptr) {
91 // exception thrown: OutOfMemoryError
92 env->DeleteLocalRef(jcn);
93 env->ReleaseLongArrayElements(jcolumn_options_handles, jco, JNI_ABORT);
94 env->ReleaseStringUTFChars(jdb_path, db_path);
95 return nullptr;
96 }
97
98 const int jcf_name_len = env->GetArrayLength(jcn_ba);
99 if (env->EnsureLocalCapacity(jcf_name_len) != 0) {
100 // out of memory
101 env->ReleaseByteArrayElements(jcn_ba, jcf_name, JNI_ABORT);
102 env->DeleteLocalRef(jcn);
103 env->ReleaseLongArrayElements(jcolumn_options_handles, jco, JNI_ABORT);
104 env->ReleaseStringUTFChars(jdb_path, db_path);
105 return nullptr;
106 }
107 const std::string cf_name(reinterpret_cast<char*>(jcf_name), jcf_name_len);
108 const rocksdb::ColumnFamilyOptions* cf_options =
109 reinterpret_cast<rocksdb::ColumnFamilyOptions*>(jco[i]);
110 column_families.push_back(
111 rocksdb::ColumnFamilyDescriptor(cf_name, *cf_options));
112
113 env->ReleaseByteArrayElements(jcn_ba, jcf_name, JNI_ABORT);
114 env->DeleteLocalRef(jcn);
115 }
116 env->ReleaseLongArrayElements(jcolumn_options_handles, jco, JNI_ABORT);
117
118 auto* db_options = reinterpret_cast<rocksdb::DBOptions*>(jdb_options_handle);
119 auto* txn_db_options =
120 reinterpret_cast<rocksdb::TransactionDBOptions*>(jtxn_db_options_handle);
121 std::vector<rocksdb::ColumnFamilyHandle*> handles;
122 rocksdb::TransactionDB* tdb = nullptr;
123 const rocksdb::Status s = rocksdb::TransactionDB::Open(
124 *db_options, *txn_db_options, db_path, column_families, &handles, &tdb);
125
126 // check if open operation was successful
127 if (s.ok()) {
128 const jsize resultsLen = 1 + len_cols; // db handle + column family handles
129 std::unique_ptr<jlong[]> results =
130 std::unique_ptr<jlong[]>(new jlong[resultsLen]);
131 results[0] = reinterpret_cast<jlong>(tdb);
132 for (int i = 1; i <= len_cols; i++) {
133 results[i] = reinterpret_cast<jlong>(handles[i - 1]);
134 }
135
136 jlongArray jresults = env->NewLongArray(resultsLen);
137 if (jresults == nullptr) {
138 // exception thrown: OutOfMemoryError
139 return nullptr;
140 }
141 env->SetLongArrayRegion(jresults, 0, resultsLen, results.get());
142 if (env->ExceptionCheck()) {
143 // exception thrown: ArrayIndexOutOfBoundsException
144 env->DeleteLocalRef(jresults);
145 return nullptr;
146 }
147 return jresults;
148 } else {
149 rocksdb::RocksDBExceptionJni::ThrowNew(env, s);
150 return nullptr;
151 }
152 }
153
154 /*
155 * Class: org_rocksdb_TransactionDB
156 * Method: disposeInternal
157 * Signature: (J)V
158 */
Java_org_rocksdb_TransactionDB_disposeInternal(JNIEnv *,jobject,jlong jhandle)159 void Java_org_rocksdb_TransactionDB_disposeInternal(
160 JNIEnv*, jobject, jlong jhandle) {
161 auto* txn_db = reinterpret_cast<rocksdb::TransactionDB*>(jhandle);
162 assert(txn_db != nullptr);
163 delete txn_db;
164 }
165
166 /*
167 * Class: org_rocksdb_TransactionDB
168 * Method: closeDatabase
169 * Signature: (J)V
170 */
Java_org_rocksdb_TransactionDB_closeDatabase(JNIEnv * env,jclass,jlong jhandle)171 void Java_org_rocksdb_TransactionDB_closeDatabase(
172 JNIEnv* env, jclass, jlong jhandle) {
173 auto* txn_db = reinterpret_cast<rocksdb::TransactionDB*>(jhandle);
174 assert(txn_db != nullptr);
175 rocksdb::Status s = txn_db->Close();
176 rocksdb::RocksDBExceptionJni::ThrowNew(env, s);
177 }
178
179 /*
180 * Class: org_rocksdb_TransactionDB
181 * Method: beginTransaction
182 * Signature: (JJ)J
183 */
Java_org_rocksdb_TransactionDB_beginTransaction__JJ(JNIEnv *,jobject,jlong jhandle,jlong jwrite_options_handle)184 jlong Java_org_rocksdb_TransactionDB_beginTransaction__JJ(
185 JNIEnv*, jobject, jlong jhandle, jlong jwrite_options_handle) {
186 auto* txn_db = reinterpret_cast<rocksdb::TransactionDB*>(jhandle);
187 auto* write_options =
188 reinterpret_cast<rocksdb::WriteOptions*>(jwrite_options_handle);
189 rocksdb::Transaction* txn = txn_db->BeginTransaction(*write_options);
190 return reinterpret_cast<jlong>(txn);
191 }
192
193 /*
194 * Class: org_rocksdb_TransactionDB
195 * Method: beginTransaction
196 * Signature: (JJJ)J
197 */
Java_org_rocksdb_TransactionDB_beginTransaction__JJJ(JNIEnv *,jobject,jlong jhandle,jlong jwrite_options_handle,jlong jtxn_options_handle)198 jlong Java_org_rocksdb_TransactionDB_beginTransaction__JJJ(
199 JNIEnv*, jobject, jlong jhandle, jlong jwrite_options_handle,
200 jlong jtxn_options_handle) {
201 auto* txn_db = reinterpret_cast<rocksdb::TransactionDB*>(jhandle);
202 auto* write_options =
203 reinterpret_cast<rocksdb::WriteOptions*>(jwrite_options_handle);
204 auto* txn_options =
205 reinterpret_cast<rocksdb::TransactionOptions*>(jtxn_options_handle);
206 rocksdb::Transaction* txn =
207 txn_db->BeginTransaction(*write_options, *txn_options);
208 return reinterpret_cast<jlong>(txn);
209 }
210
211 /*
212 * Class: org_rocksdb_TransactionDB
213 * Method: beginTransaction_withOld
214 * Signature: (JJJ)J
215 */
Java_org_rocksdb_TransactionDB_beginTransaction_1withOld__JJJ(JNIEnv *,jobject,jlong jhandle,jlong jwrite_options_handle,jlong jold_txn_handle)216 jlong Java_org_rocksdb_TransactionDB_beginTransaction_1withOld__JJJ(
217 JNIEnv*, jobject, jlong jhandle, jlong jwrite_options_handle,
218 jlong jold_txn_handle) {
219 auto* txn_db = reinterpret_cast<rocksdb::TransactionDB*>(jhandle);
220 auto* write_options =
221 reinterpret_cast<rocksdb::WriteOptions*>(jwrite_options_handle);
222 auto* old_txn = reinterpret_cast<rocksdb::Transaction*>(jold_txn_handle);
223 rocksdb::TransactionOptions txn_options;
224 rocksdb::Transaction* txn =
225 txn_db->BeginTransaction(*write_options, txn_options, old_txn);
226
227 // RocksJava relies on the assumption that
228 // we do not allocate a new Transaction object
229 // when providing an old_txn
230 assert(txn == old_txn);
231
232 return reinterpret_cast<jlong>(txn);
233 }
234
235 /*
236 * Class: org_rocksdb_TransactionDB
237 * Method: beginTransaction_withOld
238 * Signature: (JJJJ)J
239 */
Java_org_rocksdb_TransactionDB_beginTransaction_1withOld__JJJJ(JNIEnv *,jobject,jlong jhandle,jlong jwrite_options_handle,jlong jtxn_options_handle,jlong jold_txn_handle)240 jlong Java_org_rocksdb_TransactionDB_beginTransaction_1withOld__JJJJ(
241 JNIEnv*, jobject, jlong jhandle, jlong jwrite_options_handle,
242 jlong jtxn_options_handle, jlong jold_txn_handle) {
243 auto* txn_db = reinterpret_cast<rocksdb::TransactionDB*>(jhandle);
244 auto* write_options =
245 reinterpret_cast<rocksdb::WriteOptions*>(jwrite_options_handle);
246 auto* txn_options =
247 reinterpret_cast<rocksdb::TransactionOptions*>(jtxn_options_handle);
248 auto* old_txn = reinterpret_cast<rocksdb::Transaction*>(jold_txn_handle);
249 rocksdb::Transaction* txn =
250 txn_db->BeginTransaction(*write_options, *txn_options, old_txn);
251
252 // RocksJava relies on the assumption that
253 // we do not allocate a new Transaction object
254 // when providing an old_txn
255 assert(txn == old_txn);
256
257 return reinterpret_cast<jlong>(txn);
258 }
259
260 /*
261 * Class: org_rocksdb_TransactionDB
262 * Method: getTransactionByName
263 * Signature: (JLjava/lang/String;)J
264 */
Java_org_rocksdb_TransactionDB_getTransactionByName(JNIEnv * env,jobject,jlong jhandle,jstring jname)265 jlong Java_org_rocksdb_TransactionDB_getTransactionByName(
266 JNIEnv* env, jobject, jlong jhandle, jstring jname) {
267 auto* txn_db = reinterpret_cast<rocksdb::TransactionDB*>(jhandle);
268 const char* name = env->GetStringUTFChars(jname, nullptr);
269 if (name == nullptr) {
270 // exception thrown: OutOfMemoryError
271 return 0;
272 }
273 rocksdb::Transaction* txn = txn_db->GetTransactionByName(name);
274 env->ReleaseStringUTFChars(jname, name);
275 return reinterpret_cast<jlong>(txn);
276 }
277
278 /*
279 * Class: org_rocksdb_TransactionDB
280 * Method: getAllPreparedTransactions
281 * Signature: (J)[J
282 */
Java_org_rocksdb_TransactionDB_getAllPreparedTransactions(JNIEnv * env,jobject,jlong jhandle)283 jlongArray Java_org_rocksdb_TransactionDB_getAllPreparedTransactions(
284 JNIEnv* env, jobject, jlong jhandle) {
285 auto* txn_db = reinterpret_cast<rocksdb::TransactionDB*>(jhandle);
286 std::vector<rocksdb::Transaction*> txns;
287 txn_db->GetAllPreparedTransactions(&txns);
288
289 const size_t size = txns.size();
290 assert(size < UINT32_MAX); // does it fit in a jint?
291
292 const jsize len = static_cast<jsize>(size);
293 std::vector<jlong> tmp(len);
294 for (jsize i = 0; i < len; ++i) {
295 tmp[i] = reinterpret_cast<jlong>(txns[i]);
296 }
297
298 jlongArray jtxns = env->NewLongArray(len);
299 if (jtxns == nullptr) {
300 // exception thrown: OutOfMemoryError
301 return nullptr;
302 }
303 env->SetLongArrayRegion(jtxns, 0, len, tmp.data());
304 if (env->ExceptionCheck()) {
305 // exception thrown: ArrayIndexOutOfBoundsException
306 env->DeleteLocalRef(jtxns);
307 return nullptr;
308 }
309
310 return jtxns;
311 }
312
313 /*
314 * Class: org_rocksdb_TransactionDB
315 * Method: getLockStatusData
316 * Signature: (J)Ljava/util/Map;
317 */
Java_org_rocksdb_TransactionDB_getLockStatusData(JNIEnv * env,jobject,jlong jhandle)318 jobject Java_org_rocksdb_TransactionDB_getLockStatusData(
319 JNIEnv* env, jobject, jlong jhandle) {
320 auto* txn_db = reinterpret_cast<rocksdb::TransactionDB*>(jhandle);
321 const std::unordered_multimap<uint32_t, rocksdb::KeyLockInfo>
322 lock_status_data = txn_db->GetLockStatusData();
323 const jobject jlock_status_data = rocksdb::HashMapJni::construct(
324 env, static_cast<uint32_t>(lock_status_data.size()));
325 if (jlock_status_data == nullptr) {
326 // exception occurred
327 return nullptr;
328 }
329
330 const rocksdb::HashMapJni::FnMapKV<const int32_t, const rocksdb::KeyLockInfo, jobject, jobject>
331 fn_map_kv =
332 [env](
333 const std::pair<const int32_t, const rocksdb::KeyLockInfo>&
334 pair) {
335 const jobject jlong_column_family_id =
336 rocksdb::LongJni::valueOf(env, pair.first);
337 if (jlong_column_family_id == nullptr) {
338 // an error occurred
339 return std::unique_ptr<std::pair<jobject, jobject>>(nullptr);
340 }
341 const jobject jkey_lock_info =
342 rocksdb::KeyLockInfoJni::construct(env, pair.second);
343 if (jkey_lock_info == nullptr) {
344 // an error occurred
345 return std::unique_ptr<std::pair<jobject, jobject>>(nullptr);
346 }
347 return std::unique_ptr<std::pair<jobject, jobject>>(
348 new std::pair<jobject, jobject>(jlong_column_family_id,
349 jkey_lock_info));
350 };
351
352 if (!rocksdb::HashMapJni::putAll(env, jlock_status_data,
353 lock_status_data.begin(),
354 lock_status_data.end(), fn_map_kv)) {
355 // exception occcurred
356 return nullptr;
357 }
358
359 return jlock_status_data;
360 }
361
362 /*
363 * Class: org_rocksdb_TransactionDB
364 * Method: getDeadlockInfoBuffer
365 * Signature: (J)[Lorg/rocksdb/TransactionDB/DeadlockPath;
366 */
Java_org_rocksdb_TransactionDB_getDeadlockInfoBuffer(JNIEnv * env,jobject jobj,jlong jhandle)367 jobjectArray Java_org_rocksdb_TransactionDB_getDeadlockInfoBuffer(
368 JNIEnv* env, jobject jobj, jlong jhandle) {
369 auto* txn_db = reinterpret_cast<rocksdb::TransactionDB*>(jhandle);
370 const std::vector<rocksdb::DeadlockPath> deadlock_info_buffer =
371 txn_db->GetDeadlockInfoBuffer();
372
373 const jsize deadlock_info_buffer_len =
374 static_cast<jsize>(deadlock_info_buffer.size());
375 jobjectArray jdeadlock_info_buffer =
376 env->NewObjectArray(deadlock_info_buffer_len,
377 rocksdb::DeadlockPathJni::getJClass(env), nullptr);
378 if (jdeadlock_info_buffer == nullptr) {
379 // exception thrown: OutOfMemoryError
380 return nullptr;
381 }
382 jsize jdeadlock_info_buffer_offset = 0;
383
384 auto buf_end = deadlock_info_buffer.end();
385 for (auto buf_it = deadlock_info_buffer.begin(); buf_it != buf_end;
386 ++buf_it) {
387 const rocksdb::DeadlockPath deadlock_path = *buf_it;
388 const std::vector<rocksdb::DeadlockInfo> deadlock_infos =
389 deadlock_path.path;
390 const jsize deadlock_infos_len =
391 static_cast<jsize>(deadlock_info_buffer.size());
392 jobjectArray jdeadlock_infos = env->NewObjectArray(
393 deadlock_infos_len, rocksdb::DeadlockInfoJni::getJClass(env), nullptr);
394 if (jdeadlock_infos == nullptr) {
395 // exception thrown: OutOfMemoryError
396 env->DeleteLocalRef(jdeadlock_info_buffer);
397 return nullptr;
398 }
399 jsize jdeadlock_infos_offset = 0;
400
401 auto infos_end = deadlock_infos.end();
402 for (auto infos_it = deadlock_infos.begin(); infos_it != infos_end;
403 ++infos_it) {
404 const rocksdb::DeadlockInfo deadlock_info = *infos_it;
405 const jobject jdeadlock_info = rocksdb::TransactionDBJni::newDeadlockInfo(
406 env, jobj, deadlock_info.m_txn_id, deadlock_info.m_cf_id,
407 deadlock_info.m_waiting_key, deadlock_info.m_exclusive);
408 if (jdeadlock_info == nullptr) {
409 // exception occcurred
410 env->DeleteLocalRef(jdeadlock_info_buffer);
411 return nullptr;
412 }
413 env->SetObjectArrayElement(jdeadlock_infos, jdeadlock_infos_offset++,
414 jdeadlock_info);
415 if (env->ExceptionCheck()) {
416 // exception thrown: ArrayIndexOutOfBoundsException or
417 // ArrayStoreException
418 env->DeleteLocalRef(jdeadlock_info);
419 env->DeleteLocalRef(jdeadlock_info_buffer);
420 return nullptr;
421 }
422 }
423
424 const jobject jdeadlock_path = rocksdb::DeadlockPathJni::construct(
425 env, jdeadlock_infos, deadlock_path.limit_exceeded);
426 if (jdeadlock_path == nullptr) {
427 // exception occcurred
428 env->DeleteLocalRef(jdeadlock_info_buffer);
429 return nullptr;
430 }
431 env->SetObjectArrayElement(jdeadlock_info_buffer,
432 jdeadlock_info_buffer_offset++, jdeadlock_path);
433 if (env->ExceptionCheck()) {
434 // exception thrown: ArrayIndexOutOfBoundsException or ArrayStoreException
435 env->DeleteLocalRef(jdeadlock_path);
436 env->DeleteLocalRef(jdeadlock_info_buffer);
437 return nullptr;
438 }
439 }
440
441 return jdeadlock_info_buffer;
442 }
443
444 /*
445 * Class: org_rocksdb_TransactionDB
446 * Method: setDeadlockInfoBufferSize
447 * Signature: (JI)V
448 */
Java_org_rocksdb_TransactionDB_setDeadlockInfoBufferSize(JNIEnv *,jobject,jlong jhandle,jint jdeadlock_info_buffer_size)449 void Java_org_rocksdb_TransactionDB_setDeadlockInfoBufferSize(
450 JNIEnv*, jobject, jlong jhandle, jint jdeadlock_info_buffer_size) {
451 auto* txn_db = reinterpret_cast<rocksdb::TransactionDB*>(jhandle);
452 txn_db->SetDeadlockInfoBufferSize(jdeadlock_info_buffer_size);
453 }
454