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