1 /*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2000, 2010 Oracle and/or its affiliates. All rights reserved. 5 * 6 */ 7 8 package com.sleepycat.compat; 9 10 import java.util.Comparator; 11 12 import com.sleepycat.compat.DbCompat; 13 import com.sleepycat.je.Cursor; 14 import com.sleepycat.je.CursorConfig; 15 import com.sleepycat.je.Database; 16 import com.sleepycat.je.DatabaseConfig; 17 import com.sleepycat.je.DatabaseEntry; 18 import com.sleepycat.je.DatabaseException; 19 import com.sleepycat.je.DatabaseExistsException; 20 import com.sleepycat.je.DatabaseNotFoundException; 21 import com.sleepycat.je.Durability; 22 import com.sleepycat.je.DbInternal; 23 import com.sleepycat.je.Environment; 24 import com.sleepycat.je.EnvironmentConfig; 25 import com.sleepycat.je.EnvironmentFailureException; 26 import com.sleepycat.je.LockMode; 27 import com.sleepycat.je.OperationStatus; 28 import com.sleepycat.je.SecondaryConfig; 29 import com.sleepycat.je.SecondaryCursor; 30 import com.sleepycat.je.SecondaryDatabase; 31 import com.sleepycat.je.Transaction; 32 import com.sleepycat.je.TransactionConfig; 33 34 /** 35 * A minimal set of BDB DB-JE compatibility constants and static methods, for 36 * internal use only. 37 * 38 * Two versions of this class, with the same public interface but different 39 * implementations, are maintained in parallel in the DB and JE source trees. 40 * By the use of the constants and methods in this class, along with a script 41 * that moves the source code from JE to DB, the source code in certain 42 * packages is kept "portable" and is shared by the two products. The script 43 * translates the package names from com.sleepycat.je to com.sleepycat.db, and 44 * perform other fix-ups as described further below. 45 * 46 * The JE directories that contain portable code are: 47 * 48 * src/com/sleepycat/bind 49 * /collections 50 * /persist 51 * /util 52 * test/com/sleepycat/bind 53 * /collections 54 * /persist 55 * /util 56 * 57 * In DB, these sources are stored in the following locations: 58 * 59 * Sources: 60 * src/java 61 * Tests: 62 * test/java/compat 63 * 64 * To keep this source code portable there are additional coding rules, above 65 * and beyond the standard rules (such as coding style) for all JE code. 66 * 67 * + In general we should try to use the JE/DB public API, since it is usually 68 * the same or similar in both products. If we use internal APIs, they will 69 * always be different and will require special handling. 70 * 71 * + When there are differences between products, the first choice for 72 * handling the difference is to use a DbCompat static method or constant. 73 * This keeps the source code the same for both products (except in this 74 * DbCompat class). 75 * 76 * + When JE-only code is needed -- for example, some APIs only exist in JE, 77 * and special handling of JE exceptions is sometimes needed -- the 78 * following special comment syntax can be used to bracket the JE-only code: 79 * 80 * <!-- begin JE only --> 81 * JE-only code goes here 82 * <!-- end JE only --> 83 * 84 * This syntax must be used inside of a comment: either inside a javadoc 85 * section as shown above, or inside a single-line comment (space before 86 * last slash is to prevent ending this javadoc comment): 87 * 88 * /* <!-- begin JE only --> * / 89 * JE-only code goes here 90 * /* <!-- end JE only --> * / 91 * 92 * All lines between the <!-- begin JE only --> and <!-- end JE only --> 93 * lines, and including these lines, will be removed by the script that 94 * transfers code from JE to DB. 95 * 96 * + When DB-only code is needed, the code will exist in the JE product but 97 * will never be executed. For DB-only APIs, we hide the API from the user 98 * with the @hidden javadoc tag. The @hidden tag is ignored on the DB side. 99 * We do not have a way to remove DB-only code completely from the JE 100 * product, because we do not use a proprocessor for building JE. 101 * 102 * + Because DatabaseException (and all subclasses) are checked exceptions in 103 * DB but runtime exceptions in JE, we cannot omit the 'throws' declaration. 104 * Another difference is that DB normally throws DatabaseException for all 105 * errors, while JE has many specific subclasses for specific errors. 106 * Therefore, any method that calls a DB API method (for example, 107 * Database.get or put) will have a "throws DatabaseException" clause. 108 * 109 * + Special consideration is needed for the @throws clauses in javadoc. We do 110 * want to javadoc the JE-only exceptions that are thrown, so the @throws 111 * for these exceptions should be inside the "begin/end JE only" brackets. 112 * We also need to document the fact that DB may throw DatabaseException for 113 * almost any method, so we do that with a final @throws clause that looks 114 * like this: 115 * 116 * @throws DatabaseException the base class for all BDB exceptions. 117 * 118 * This is a compromise. JE doesn't throw this exception, but we've 119 * described it in a way that still makes some sense for JE, sort of. 120 * 121 * + Other special handling can be implemented in the transfer script, which 122 * uses SED. Entire files can be excluded from the transfer, for example, 123 * the JE-only exception classes. Name changes can also be made using SED, 124 * for example: s/LockConflictException/DeadlockException/. See the 125 * db/dist/s_je2db script for details. 126 */ 127 public class DbCompat { 128 129 /* Capabilities */ 130 131 public static final boolean CDB = false; 132 public static final boolean JOIN = true; 133 public static final boolean NESTED_TRANSACTIONS = false; 134 public static final boolean INSERTION_ORDERED_DUPLICATES = false; 135 public static final boolean SEPARATE_DATABASE_FILES = false; 136 public static final boolean MEMORY_SUBSYSTEM = false; 137 public static final boolean LOCK_SUBSYSTEM = false; 138 public static final boolean HASH_METHOD = false; 139 public static final boolean RECNO_METHOD = false; 140 public static final boolean QUEUE_METHOD = false; 141 public static final boolean BTREE_RECNUM_METHOD = false; 142 public static final boolean OPTIONAL_READ_UNCOMMITTED = false; 143 public static final boolean SECONDARIES = true; 144 public static boolean TRANSACTION_RUNNER_PRINT_STACK_TRACES = true; 145 public static final boolean DATABASE_COUNT = true; 146 public static final boolean NEW_JE_EXCEPTIONS = true; 147 public static final boolean POPULATE_ENFORCES_CONSTRAINTS = true; 148 149 /** 150 * For read-only cursor operations on a replicated node, we must use a 151 * transaction to satisfy HA requirements. However, we use a Durability 152 * that avoids consistency checks on the Master, and we use ReadCommitted 153 * isolation since that gives the same behavior as a non-transactional 154 * cursor: locks are released when the cursor is moved or closed. 155 */ 156 public static final TransactionConfig READ_ONLY_TXN_CONFIG; 157 static { 158 READ_ONLY_TXN_CONFIG = new TransactionConfig(); 159 READ_ONLY_TXN_CONFIG.setDurability(Durability.READ_ONLY_TXN); 160 READ_ONLY_TXN_CONFIG.setReadCommitted(true); 161 } 162 getInitializeCache(EnvironmentConfig config)163 public static boolean getInitializeCache(EnvironmentConfig config) { 164 return true; 165 } 166 getInitializeLocking(EnvironmentConfig config)167 public static boolean getInitializeLocking(EnvironmentConfig config) { 168 return config.getLocking(); 169 } 170 getInitializeCDB(EnvironmentConfig config)171 public static boolean getInitializeCDB(EnvironmentConfig config) { 172 return false; 173 } 174 isReplicated(Environment env)175 public static boolean isReplicated(Environment env) { 176 return DbInternal.getEnvironmentImpl(env).isReplicated(); 177 } 178 isTypeBtree(DatabaseConfig dbConfig)179 public static boolean isTypeBtree(DatabaseConfig dbConfig) { 180 return true; 181 } 182 isTypeHash(DatabaseConfig dbConfig)183 public static boolean isTypeHash(DatabaseConfig dbConfig) { 184 return false; 185 } 186 isTypeQueue(DatabaseConfig dbConfig)187 public static boolean isTypeQueue(DatabaseConfig dbConfig) { 188 return false; 189 } 190 isTypeRecno(DatabaseConfig dbConfig)191 public static boolean isTypeRecno(DatabaseConfig dbConfig) { 192 return false; 193 } 194 getBtreeRecordNumbers(DatabaseConfig dbConfig)195 public static boolean getBtreeRecordNumbers(DatabaseConfig dbConfig) { 196 return false; 197 } 198 getReadUncommitted(DatabaseConfig dbConfig)199 public static boolean getReadUncommitted(DatabaseConfig dbConfig) { 200 return true; 201 } 202 getRenumbering(DatabaseConfig dbConfig)203 public static boolean getRenumbering(DatabaseConfig dbConfig) { 204 return false; 205 } 206 getSortedDuplicates(DatabaseConfig dbConfig)207 public static boolean getSortedDuplicates(DatabaseConfig dbConfig) { 208 return dbConfig.getSortedDuplicates(); 209 } 210 getUnsortedDuplicates(DatabaseConfig dbConfig)211 public static boolean getUnsortedDuplicates(DatabaseConfig dbConfig) { 212 return false; 213 } 214 getDeferredWrite(DatabaseConfig dbConfig)215 public static boolean getDeferredWrite(DatabaseConfig dbConfig) { 216 return dbConfig.getDeferredWrite(); 217 } 218 219 // XXX Remove this when DB and JE support CursorConfig.cloneConfig cloneCursorConfig(CursorConfig config)220 public static CursorConfig cloneCursorConfig(CursorConfig config) { 221 CursorConfig newConfig = new CursorConfig(); 222 newConfig.setReadCommitted(config.getReadCommitted()); 223 newConfig.setReadUncommitted(config.getReadUncommitted()); 224 return newConfig; 225 } 226 getWriteCursor(CursorConfig config)227 public static boolean getWriteCursor(CursorConfig config) { 228 return false; 229 } 230 setWriteCursor(CursorConfig config, boolean write)231 public static void setWriteCursor(CursorConfig config, boolean write) { 232 if (write) { 233 throw new UnsupportedOperationException(); 234 } 235 } 236 setRecordNumber(DatabaseEntry entry, int recNum)237 public static void setRecordNumber(DatabaseEntry entry, int recNum) { 238 throw new UnsupportedOperationException(); 239 } 240 getRecordNumber(DatabaseEntry entry)241 public static int getRecordNumber(DatabaseEntry entry) { 242 throw new UnsupportedOperationException(); 243 } 244 getDatabaseFile(Database db)245 public static String getDatabaseFile(Database db) { 246 return null; 247 } 248 getDatabaseCount(Database db)249 public static long getDatabaseCount(Database db) 250 throws DatabaseException { 251 252 return db.count(); 253 } 254 255 /** 256 * @throws DatabaseException from DB core. 257 */ getCurrentRecordNumber(Cursor cursor, DatabaseEntry key, LockMode lockMode)258 public static OperationStatus getCurrentRecordNumber(Cursor cursor, 259 DatabaseEntry key, 260 LockMode lockMode) 261 throws DatabaseException { 262 263 throw new UnsupportedOperationException(); 264 } 265 266 /** 267 * @throws DatabaseException from DB core. 268 */ getSearchRecordNumber(Cursor cursor, DatabaseEntry key, DatabaseEntry data, LockMode lockMode)269 public static OperationStatus getSearchRecordNumber(Cursor cursor, 270 DatabaseEntry key, 271 DatabaseEntry data, 272 LockMode lockMode) 273 throws DatabaseException { 274 275 throw new UnsupportedOperationException(); 276 } 277 278 /** 279 * @throws DatabaseException from DB core. 280 */ getSearchRecordNumber(SecondaryCursor cursor, DatabaseEntry key, DatabaseEntry pKey, DatabaseEntry data, LockMode lockMode)281 public static OperationStatus getSearchRecordNumber(SecondaryCursor cursor, 282 DatabaseEntry key, 283 DatabaseEntry pKey, 284 DatabaseEntry data, 285 LockMode lockMode) 286 throws DatabaseException { 287 288 throw new UnsupportedOperationException(); 289 } 290 291 /** 292 * @throws DatabaseException from DB core. 293 */ putAfter(Cursor cursor, DatabaseEntry key, DatabaseEntry data)294 public static OperationStatus putAfter(Cursor cursor, 295 DatabaseEntry key, 296 DatabaseEntry data) 297 throws DatabaseException { 298 299 throw new UnsupportedOperationException(); 300 } 301 302 /** 303 * @throws DatabaseException from DB core. 304 */ putBefore(Cursor cursor, DatabaseEntry key, DatabaseEntry data)305 public static OperationStatus putBefore(Cursor cursor, 306 DatabaseEntry key, 307 DatabaseEntry data) 308 throws DatabaseException { 309 310 throw new UnsupportedOperationException(); 311 } 312 append(Database db, Transaction txn, DatabaseEntry key, DatabaseEntry data)313 public static OperationStatus append(Database db, 314 Transaction txn, 315 DatabaseEntry key, 316 DatabaseEntry data) { 317 throw new UnsupportedOperationException(); 318 } 319 getThreadTransaction(Environment env)320 public static Transaction getThreadTransaction(Environment env) 321 throws DatabaseException { 322 323 return env.getThreadTransaction(); 324 } 325 getClassLoader(Environment env)326 public static ClassLoader getClassLoader(Environment env) { 327 return DbInternal.getEnvironmentImpl(env).getClassLoader(); 328 } 329 330 /* Methods used by the collections tests. */ 331 setInitializeCache(EnvironmentConfig config, boolean val)332 public static void setInitializeCache(EnvironmentConfig config, 333 boolean val) { 334 if (!val) { 335 throw new UnsupportedOperationException(); 336 } 337 } 338 setInitializeLocking(EnvironmentConfig config, boolean val)339 public static void setInitializeLocking(EnvironmentConfig config, 340 boolean val) { 341 if (!val) { 342 throw new UnsupportedOperationException(); 343 } 344 } 345 setInitializeCDB(EnvironmentConfig config, boolean val)346 public static void setInitializeCDB(EnvironmentConfig config, 347 boolean val) { 348 if (val) { 349 throw new UnsupportedOperationException(); 350 } 351 } 352 setLockDetectModeOldest(EnvironmentConfig config)353 public static void setLockDetectModeOldest(EnvironmentConfig config) { 354 /* JE does this by default, since it uses timeouts. */ 355 } 356 setSerializableIsolation(TransactionConfig config, boolean val)357 public static void setSerializableIsolation(TransactionConfig config, 358 boolean val) { 359 config.setSerializableIsolation(val); 360 } 361 setImportunate(final Transaction txn, final boolean importunate)362 public static boolean setImportunate(final Transaction txn, 363 final boolean importunate) { 364 final boolean oldVal = DbInternal.getTxn(txn).getImportunate(); 365 DbInternal.getTxn(txn).setImportunate(importunate); 366 return oldVal; 367 } 368 setBtreeComparator(DatabaseConfig dbConfig, Comparator<byte[]> comparator)369 public static void setBtreeComparator(DatabaseConfig dbConfig, 370 Comparator<byte[]> comparator) { 371 dbConfig.setBtreeComparator(comparator); 372 } 373 setTypeBtree(DatabaseConfig dbConfig)374 public static void setTypeBtree(DatabaseConfig dbConfig) { 375 } 376 setTypeHash(DatabaseConfig dbConfig)377 public static void setTypeHash(DatabaseConfig dbConfig) { 378 throw new UnsupportedOperationException(); 379 } 380 setTypeRecno(DatabaseConfig dbConfig)381 public static void setTypeRecno(DatabaseConfig dbConfig) { 382 throw new UnsupportedOperationException(); 383 } 384 setTypeQueue(DatabaseConfig dbConfig)385 public static void setTypeQueue(DatabaseConfig dbConfig) { 386 throw new UnsupportedOperationException(); 387 } 388 setBtreeRecordNumbers(DatabaseConfig dbConfig, boolean val)389 public static void setBtreeRecordNumbers(DatabaseConfig dbConfig, 390 boolean val) { 391 throw new UnsupportedOperationException(); 392 } 393 setReadUncommitted(DatabaseConfig dbConfig, boolean val)394 public static void setReadUncommitted(DatabaseConfig dbConfig, 395 boolean val) { 396 } 397 setRenumbering(DatabaseConfig dbConfig, boolean val)398 public static void setRenumbering(DatabaseConfig dbConfig, 399 boolean val) { 400 throw new UnsupportedOperationException(); 401 } 402 setSortedDuplicates(DatabaseConfig dbConfig, boolean val)403 public static void setSortedDuplicates(DatabaseConfig dbConfig, 404 boolean val) { 405 dbConfig.setSortedDuplicates(val); 406 } 407 setUnsortedDuplicates(DatabaseConfig dbConfig, boolean val)408 public static void setUnsortedDuplicates(DatabaseConfig dbConfig, 409 boolean val) { 410 if (val) { 411 throw new UnsupportedOperationException(); 412 } 413 } 414 setDeferredWrite(DatabaseConfig dbConfig, boolean val)415 public static void setDeferredWrite(DatabaseConfig dbConfig, boolean val) { 416 dbConfig.setDeferredWrite(val); 417 } 418 setRecordLength(DatabaseConfig dbConfig, int val)419 public static void setRecordLength(DatabaseConfig dbConfig, int val) { 420 if (val != 0) { 421 throw new UnsupportedOperationException(); 422 } 423 } 424 setRecordPad(DatabaseConfig dbConfig, int val)425 public static void setRecordPad(DatabaseConfig dbConfig, int val) { 426 throw new UnsupportedOperationException(); 427 } 428 databaseExists(Environment env, String fileName, String dbName)429 public static boolean databaseExists(Environment env, 430 String fileName, 431 String dbName) { 432 assert fileName == null; 433 return env.getDatabaseNames().contains(dbName); 434 } 435 436 /** 437 * Returns null if the database is not found (and AllowCreate is false) or 438 * already exists (and ExclusiveCreate is true). 439 */ openDatabase(Environment env, Transaction txn, String fileName, String dbName, DatabaseConfig config)440 public static Database openDatabase(Environment env, 441 Transaction txn, 442 String fileName, 443 String dbName, 444 DatabaseConfig config) { 445 assert fileName == null; 446 try { 447 return env.openDatabase(txn, dbName, config); 448 } catch (DatabaseNotFoundException e) { 449 return null; 450 } catch (DatabaseExistsException e) { 451 return null; 452 } 453 } 454 455 /** 456 * Returns null if the database is not found (and AllowCreate is false) or 457 * already exists (and ExclusiveCreate is true). 458 */ 459 public static SecondaryDatabase openSecondaryDatabase(Environment env, Transaction txn, String fileName, String dbName, Database primaryDatabase, SecondaryConfig config)460 openSecondaryDatabase(Environment env, 461 Transaction txn, 462 String fileName, 463 String dbName, 464 Database primaryDatabase, 465 SecondaryConfig config) { 466 assert fileName == null; 467 try { 468 return env.openSecondaryDatabase(txn, dbName, primaryDatabase, 469 config); 470 } catch (DatabaseNotFoundException e) { 471 return null; 472 } catch (DatabaseExistsException e) { 473 return null; 474 } 475 } 476 477 /** 478 * Returns false if the database is not found. 479 */ truncateDatabase(Environment env, Transaction txn, String fileName, String dbName)480 public static boolean truncateDatabase(Environment env, 481 Transaction txn, 482 String fileName, 483 String dbName) { 484 assert fileName == null; 485 try { 486 env.truncateDatabase(txn, dbName, false /*returnCount*/); 487 return true; 488 } catch (DatabaseNotFoundException e) { 489 return false; 490 } 491 } 492 493 /** 494 * Returns false if the database is not found. 495 */ removeDatabase(Environment env, Transaction txn, String fileName, String dbName)496 public static boolean removeDatabase(Environment env, 497 Transaction txn, 498 String fileName, 499 String dbName) { 500 assert fileName == null; 501 try { 502 env.removeDatabase(txn, dbName); 503 return true; 504 } catch (DatabaseNotFoundException e) { 505 return false; 506 } 507 } 508 509 /** 510 * Returns false if the database is not found. 511 */ renameDatabase(Environment env, Transaction txn, String oldFileName, String oldDbName, String newFileName, String newDbName)512 public static boolean renameDatabase(Environment env, 513 Transaction txn, 514 String oldFileName, 515 String oldDbName, 516 String newFileName, 517 String newDbName) { 518 assert oldFileName == null; 519 assert newFileName == null; 520 try { 521 env.renameDatabase(txn, oldDbName, newDbName); 522 return true; 523 } catch (DatabaseNotFoundException e) { 524 return false; 525 } 526 } 527 528 /** 529 * Fires an assertion if the database is not found (and AllowCreate is 530 * false) or already exists (and ExclusiveCreate is true). 531 */ testOpenDatabase(Environment env, Transaction txn, String file, String name, DatabaseConfig config)532 public static Database testOpenDatabase(Environment env, 533 Transaction txn, 534 String file, 535 String name, 536 DatabaseConfig config) { 537 try { 538 return env.openDatabase(txn, makeTestDbName(file, name), config); 539 } catch (DatabaseNotFoundException e) { 540 assert false; 541 return null; 542 } catch (DatabaseExistsException e) { 543 assert false; 544 return null; 545 } 546 } 547 548 /** 549 * Fires an assertion if the database is not found (and AllowCreate is 550 * false) or already exists (and ExclusiveCreate is true). 551 */ 552 public static SecondaryDatabase testOpenSecondaryDatabase(Environment env, Transaction txn, String file, String name, Database primary, SecondaryConfig config)553 testOpenSecondaryDatabase(Environment env, 554 Transaction txn, 555 String file, 556 String name, 557 Database primary, 558 SecondaryConfig config) { 559 try { 560 return env.openSecondaryDatabase(txn, makeTestDbName(file, name), 561 primary, config); 562 } catch (DatabaseNotFoundException e) { 563 assert false; 564 return null; 565 } catch (DatabaseExistsException e) { 566 assert false; 567 return null; 568 } 569 } 570 makeTestDbName(String file, String name)571 private static String makeTestDbName(String file, String name) { 572 if (file == null) { 573 return name; 574 } else { 575 if (name != null) { 576 return file + '.' + name; 577 } else { 578 return file; 579 } 580 } 581 } 582 unexpectedException(Exception cause)583 public static RuntimeException unexpectedException(Exception cause) { 584 return EnvironmentFailureException.unexpectedException(cause); 585 } 586 unexpectedException(String msg, Exception cause)587 public static RuntimeException unexpectedException(String msg, 588 Exception cause) { 589 return EnvironmentFailureException.unexpectedException(msg, cause); 590 } 591 unexpectedState(String msg)592 public static RuntimeException unexpectedState(String msg) { 593 return EnvironmentFailureException.unexpectedState(msg); 594 } 595 unexpectedState()596 public static RuntimeException unexpectedState() { 597 return EnvironmentFailureException.unexpectedState(); 598 } 599 } 600