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