Main Page | Class Hierarchy | Data Structures | Directories | File List | Data Fields | Related Pages

DataView.java

00001 /*-
00002  * See the file LICENSE for redistribution information.
00003  *
00004  * Copyright (c) 2000-2005
00005  *      Sleepycat Software.  All rights reserved.
00006  *
00007  * $Id: DataView.java,v 12.1 2005/01/31 19:27:32 mark Exp $
00008  */
00009 
00010 package com.sleepycat.collections;
00011 
00012 import com.sleepycat.bind.EntityBinding;
00013 import com.sleepycat.bind.EntryBinding;
00014 import com.sleepycat.compat.DbCompat;
00015 import com.sleepycat.db.CursorConfig;
00016 import com.sleepycat.db.Database;
00017 import com.sleepycat.db.DatabaseConfig;
00018 import com.sleepycat.db.DatabaseEntry;
00019 import com.sleepycat.db.DatabaseException;
00020 import com.sleepycat.db.Environment;
00021 import com.sleepycat.db.JoinConfig;
00022 import com.sleepycat.db.OperationStatus;
00023 import com.sleepycat.db.SecondaryConfig;
00024 import com.sleepycat.db.SecondaryDatabase;
00025 import com.sleepycat.db.SecondaryKeyCreator;
00026 import com.sleepycat.db.Transaction;
00027 import com.sleepycat.util.RuntimeExceptionWrapper;
00028 
00038 final class DataView implements Cloneable {
00039 
00040     Database db;
00041     SecondaryDatabase secDb;
00042     CurrentTransaction currentTxn;
00043     KeyRange range;
00044     EntryBinding keyBinding;
00045     EntryBinding valueBinding;
00046     EntityBinding entityBinding;
00047     PrimaryKeyAssigner keyAssigner;
00048     SecondaryKeyCreator secKeyCreator;
00049     CursorConfig cursorConfig;      // Used for all operations via this view
00050     boolean writeAllowed;           // Read-write view
00051     boolean ordered;                // Not a HASH Db
00052     boolean recNumAllowed;          // QUEUE, RECNO, or BTREE-RECNUM Db
00053     boolean recNumAccess;           // recNumAllowed && using a rec num binding
00054     boolean btreeRecNumDb;          // BTREE-RECNUM Db
00055     boolean btreeRecNumAccess;      // recNumAccess && BTREE-RECNUM Db
00056     boolean recNumRenumber;         // RECNO-RENUM Db
00057     boolean keysRenumbered;         // recNumRenumber || btreeRecNumAccess
00058     boolean dupsAllowed;            // Dups configured
00059     boolean dupsOrdered;            // Sorted dups configured
00060     boolean transactional;          // Db is transactional
00061     boolean readUncommittedAllowed; // Read-uncommited is optional in DB-CORE
00062 
00067     DataView(Database database, EntryBinding keyBinding,
00068              EntryBinding valueBinding, EntityBinding entityBinding,
00069              boolean writeAllowed, PrimaryKeyAssigner keyAssigner)
00070         throws IllegalArgumentException {
00071 
00072         if (database == null) {
00073             throw new IllegalArgumentException("database is null");
00074         }
00075         db = database;
00076         try {
00077             currentTxn =
00078                 CurrentTransaction.getInstanceInternal(db.getEnvironment());
00079             DatabaseConfig dbConfig;
00080             if (db instanceof SecondaryDatabase) {
00081                 secDb = (SecondaryDatabase) database;
00082                 SecondaryConfig secConfig = secDb.getSecondaryConfig();
00083                 secKeyCreator = secConfig.getKeyCreator();
00084                 dbConfig = secConfig;
00085             } else {
00086                 dbConfig = db.getConfig();
00087             }
00088             ordered = !DbCompat.isTypeHash(dbConfig);
00089             recNumAllowed = DbCompat.isTypeQueue(dbConfig) ||
00090                             DbCompat.isTypeRecno(dbConfig) ||
00091                             DbCompat.getBtreeRecordNumbers(dbConfig);
00092             recNumRenumber = DbCompat.getRenumbering(dbConfig);
00093             dupsAllowed = DbCompat.getSortedDuplicates(dbConfig) ||
00094                           DbCompat.getUnsortedDuplicates(dbConfig);
00095             dupsOrdered = DbCompat.getSortedDuplicates(dbConfig);
00096             transactional = currentTxn.isTxnMode() &&
00097                             dbConfig.getTransactional();
00098             readUncommittedAllowed = DbCompat.getReadUncommitted(dbConfig);
00099             btreeRecNumDb = recNumAllowed && DbCompat.isTypeBtree(dbConfig);
00100             range = new KeyRange(dbConfig.getBtreeComparator());
00101         } catch (DatabaseException e) {
00102             throw new RuntimeExceptionWrapper(e);
00103         }
00104         this.writeAllowed = writeAllowed;
00105         this.keyBinding = keyBinding;
00106         this.valueBinding = valueBinding;
00107         this.entityBinding = entityBinding;
00108         this.keyAssigner = keyAssigner;
00109         cursorConfig = CursorConfig.DEFAULT;
00110 
00111         if (valueBinding != null && entityBinding != null)
00112             throw new IllegalArgumentException(
00113                 "both valueBinding and entityBinding are non-null");
00114 
00115         if (keyBinding instanceof com.sleepycat.bind.RecordNumberBinding) {
00116             if (!recNumAllowed) {
00117                 throw new IllegalArgumentException(
00118                     "RecordNumberBinding requires DB_BTREE/DB_RECNUM, " +
00119                     "DB_RECNO, or DB_QUEUE");
00120             }
00121             recNumAccess = true;
00122             if (btreeRecNumDb) {
00123                 btreeRecNumAccess = true;
00124             }
00125         }
00126         keysRenumbered = recNumRenumber || btreeRecNumAccess;
00127     }
00128 
00132     private DataView cloneView() {
00133 
00134         try {
00135             return (DataView) super.clone();
00136         } catch (CloneNotSupportedException willNeverOccur) {
00137             throw new IllegalStateException();
00138         }
00139     }
00140 
00147     DataView keySetView() {
00148 
00149         if (keyBinding == null) {
00150             throw new UnsupportedOperationException("must have keyBinding");
00151         }
00152         DataView view = cloneView();
00153         view.valueBinding = null;
00154         view.entityBinding = null;
00155         return view;
00156     }
00157 
00164     DataView valueSetView() {
00165 
00166         if (valueBinding == null && entityBinding == null) {
00167             throw new UnsupportedOperationException(
00168                 "must have valueBinding or entityBinding");
00169         }
00170         DataView view = cloneView();
00171         view.keyBinding = null;
00172         return view;
00173     }
00174 
00187     DataView valueSetView(Object singleKey)
00188         throws DatabaseException, KeyRangeException {
00189 
00190         /*
00191          * Must do subRange before valueSetView since the latter clears the
00192          * key binding needed for the former.
00193          */
00194         KeyRange singleKeyRange = subRange(singleKey);
00195         DataView view = valueSetView();
00196         view.range = singleKeyRange;
00197         return view;
00198     }
00199 
00204     DataView subView(Object beginKey, boolean beginInclusive,
00205                      Object endKey, boolean endInclusive,
00206                      EntryBinding keyBinding)
00207         throws DatabaseException, KeyRangeException {
00208 
00209         DataView view = cloneView();
00210         view.setRange(beginKey, beginInclusive, endKey, endInclusive);
00211         if (keyBinding != null) view.keyBinding = keyBinding;
00212         return view;
00213     }
00214 
00218     DataView configuredView(CursorConfig config) {
00219 
00220         DataView view = cloneView();
00221         view.cursorConfig = (config != null) ?
00222             DbCompat.cloneCursorConfig(config) : CursorConfig.DEFAULT;
00223         return view;
00224     }
00225 
00230     CurrentTransaction getCurrentTxn() {
00231 
00232         return transactional ? currentTxn : null;
00233     }
00234 
00238     private void setRange(Object beginKey, boolean beginInclusive,
00239                           Object endKey, boolean endInclusive)
00240         throws DatabaseException, KeyRangeException {
00241 
00242         range = subRange(beginKey, beginInclusive, endKey, endInclusive);
00243     }
00244 
00249     DatabaseEntry getSingleKeyThang() {
00250 
00251         return range.getSingleKey();
00252     }
00253 
00257     final Environment getEnv() {
00258 
00259         return currentTxn.getEnvironment();
00260     }
00261 
00266     final boolean isSecondary() {
00267 
00268         return (secDb != null);
00269     }
00270 
00274     boolean isEmpty()
00275         throws DatabaseException {
00276 
00277         DataCursor cursor = new DataCursor(this, false);
00278         try {
00279             return cursor.getFirst(false) != OperationStatus.SUCCESS;
00280         } finally {
00281             cursor.close();
00282         }
00283     }
00284 
00289     OperationStatus append(Object value, Object[] retPrimaryKey,
00290                            Object[] retValue)
00291         throws DatabaseException {
00292 
00293         /*
00294          * Flags will be NOOVERWRITE if used with assigner, or APPEND
00295          * otherwise.
00296          * Requires: if value param, value or entity binding
00297          * Requires: if retPrimaryKey, primary key binding (no index).
00298          * Requires: if retValue, value or entity binding
00299          */
00300         DatabaseEntry keyThang = new DatabaseEntry();
00301         DatabaseEntry valueThang = new DatabaseEntry();
00302         useValue(value, valueThang, null);
00303         OperationStatus status;
00304         if (keyAssigner != null) {
00305             keyAssigner.assignKey(keyThang);
00306             if (!range.check(keyThang)) {
00307                 throw new IllegalArgumentException(
00308                     "assigned key out of range");
00309             }
00310             DataCursor cursor = new DataCursor(this, true);
00311             try {
00312                 status = cursor.getCursor().putNoOverwrite(keyThang,
00313                                                            valueThang);
00314             } finally {
00315                 cursor.close();
00316             }
00317         } else {
00318             /* Assume QUEUE/RECNO access method. */
00319             if (currentTxn.isCDBCursorOpen(db)) {
00320                 throw new IllegalStateException(
00321                   "cannot open CDB write cursor when read cursor is open");
00322             }
00323             status = DbCompat.append(db, useTransaction(),
00324                                      keyThang, valueThang);
00325             if (status == OperationStatus.SUCCESS && !range.check(keyThang)) {
00326                 db.delete(useTransaction(), keyThang);
00327                 throw new IllegalArgumentException(
00328                     "appended record number out of range");
00329             }
00330         }
00331         if (status == OperationStatus.SUCCESS) {
00332             returnPrimaryKeyAndValue(keyThang, valueThang,
00333                                      retPrimaryKey, retValue);
00334         }
00335         return status;
00336     }
00337 
00342     Transaction useTransaction() {
00343         return transactional ?  currentTxn.getTransaction() : null;
00344     }
00345 
00349     void clear()
00350         throws DatabaseException {
00351 
00352         DataCursor cursor = new DataCursor(this, true);
00353         try {
00354             OperationStatus status = OperationStatus.SUCCESS;
00355             while (status == OperationStatus.SUCCESS) {
00356                 if (keysRenumbered) {
00357                     status = cursor.getFirst(true);
00358                 } else {
00359                     status = cursor.getNext(true);
00360                 }
00361                 if (status == OperationStatus.SUCCESS) {
00362                     cursor.delete();
00363                 }
00364             }
00365         } finally {
00366             cursor.close();
00367         }
00368     }
00369 
00374     DataCursor join(DataView[] indexViews, Object[] indexKeys,
00375                     JoinConfig joinConfig)
00376         throws DatabaseException {
00377 
00378         DataCursor joinCursor = null;
00379         DataCursor[] indexCursors = new DataCursor[indexViews.length];
00380         try {
00381             for (int i = 0; i < indexViews.length; i += 1) {
00382                 indexCursors[i] = new DataCursor(indexViews[i], false);
00383                 indexCursors[i].getSearchKey(indexKeys[i], null, false);
00384             }
00385             joinCursor = new DataCursor(this, indexCursors, joinConfig, true);
00386             return joinCursor;
00387         } finally {
00388             if (joinCursor == null) {
00389                 // An exception is being thrown, so close cursors we opened.
00390                 for (int i = 0; i < indexCursors.length; i += 1) {
00391                     if (indexCursors[i] != null) {
00392                         try { indexCursors[i].close(); }
00393                         catch (Exception e) {
00394                             /* FindBugs, this is ok. */
00395                         }
00396                     }
00397                 }
00398             }
00399         }
00400     }
00401 
00406     DataCursor join(DataCursor[] indexCursors, JoinConfig joinConfig)
00407         throws DatabaseException {
00408 
00409         return new DataCursor(this, indexCursors, joinConfig, false);
00410     }
00411 
00415     private void returnPrimaryKeyAndValue(DatabaseEntry keyThang,
00416                                           DatabaseEntry valueThang,
00417                                           Object[] retPrimaryKey,
00418                                           Object[] retValue)
00419         throws DatabaseException {
00420 
00421         // Requires: if retPrimaryKey, primary key binding (no index).
00422         // Requires: if retValue, value or entity binding
00423 
00424         if (retPrimaryKey != null) {
00425             if (keyBinding == null) {
00426                 throw new IllegalArgumentException(
00427                     "returning key requires primary key binding");
00428             } else if (isSecondary()) {
00429                 throw new IllegalArgumentException(
00430                     "returning key requires unindexed view");
00431             } else {
00432                 retPrimaryKey[0] = keyBinding.entryToObject(keyThang);
00433             }
00434         }
00435         if (retValue != null) {
00436             retValue[0] = makeValue(keyThang, valueThang);
00437         }
00438     }
00439 
00443     boolean useKey(Object key, Object value, DatabaseEntry keyThang,
00444                    KeyRange checkRange)
00445         throws DatabaseException {
00446 
00447         if (key != null) {
00448             if (keyBinding == null) {
00449                 throw new IllegalArgumentException(
00450                     "non-null key with null key binding");
00451             }
00452             keyBinding.objectToEntry(key, keyThang);
00453         } else {
00454             if (value == null) {
00455                 throw new IllegalArgumentException(
00456                     "null key and null value");
00457             }
00458             if (entityBinding == null) {
00459                 throw new IllegalStateException(
00460                     "EntityBinding required to derive key from value");
00461             }
00462             if (isSecondary()) {
00463                 DatabaseEntry primaryKeyThang = new DatabaseEntry();
00464                 entityBinding.objectToKey(value, primaryKeyThang);
00465                 DatabaseEntry valueThang = new DatabaseEntry();
00466                 entityBinding.objectToData(value, valueThang);
00467                 secKeyCreator.createSecondaryKey(secDb, primaryKeyThang,
00468                                                  valueThang, keyThang);
00469             } else {
00470                 entityBinding.objectToKey(value, keyThang);
00471             }
00472         }
00473         if (recNumAccess && DbCompat.getRecordNumber(keyThang) <= 0) {
00474             return false;
00475         }
00476         if (checkRange != null && !checkRange.check(keyThang)) {
00477             return false;
00478         }
00479         return true;
00480     }
00481 
00487     final boolean canDeriveKeyFromValue() {
00488 
00489         return (entityBinding != null);
00490     }
00491 
00496     void useValue(Object value, DatabaseEntry valueThang,
00497                   DatabaseEntry checkKeyThang)
00498         throws DatabaseException {
00499 
00500         if (value != null) {
00501             if (valueBinding != null) {
00502                 valueBinding.objectToEntry(value, valueThang);
00503             } else if (entityBinding != null) {
00504                 entityBinding.objectToData(value, valueThang);
00505                 if (checkKeyThang != null) {
00506                     DatabaseEntry thang = new DatabaseEntry();
00507                     entityBinding.objectToKey(value, thang);
00508                     if (!KeyRange.equalBytes(thang, checkKeyThang)) {
00509                         throw new IllegalArgumentException(
00510                             "cannot change primary key");
00511                     }
00512                 }
00513             } else {
00514                 throw new IllegalArgumentException(
00515                     "non-null value with null value/entity binding");
00516             }
00517         } else {
00518             valueThang.setData(new byte[0]);
00519             valueThang.setOffset(0);
00520             valueThang.setSize(0);
00521         }
00522     }
00523 
00527     Object makeKey(DatabaseEntry keyThang)
00528         throws DatabaseException {
00529 
00530         if (keyThang.getSize() == 0) return null;
00531         return keyBinding.entryToObject(keyThang);
00532     }
00533 
00537     Object makeValue(DatabaseEntry primaryKeyThang, DatabaseEntry valueThang)
00538         throws DatabaseException {
00539 
00540         Object value;
00541         if (valueBinding != null) {
00542             value = valueBinding.entryToObject(valueThang);
00543         } else if (entityBinding != null) {
00544             value = entityBinding.entryToObject(primaryKeyThang,
00545                                                     valueThang);
00546         } else {
00547             throw new UnsupportedOperationException(
00548                 "requires valueBinding or entityBinding");
00549         }
00550         return value;
00551     }
00552 
00556     KeyRange subRange(Object singleKey)
00557         throws DatabaseException, KeyRangeException {
00558 
00559         return range.subRange(makeRangeKey(singleKey));
00560     }
00561 
00565     KeyRange subRange(Object beginKey, boolean beginInclusive,
00566                       Object endKey, boolean endInclusive)
00567         throws DatabaseException, KeyRangeException {
00568 
00569         if (beginKey == endKey && beginInclusive && endInclusive) {
00570             return subRange(beginKey);
00571         }
00572         if (!ordered) {
00573             throw new UnsupportedOperationException(
00574                     "Cannot use key ranges on an unsorted database");
00575         }
00576         DatabaseEntry beginThang =
00577             (beginKey != null) ? makeRangeKey(beginKey) : null;
00578         DatabaseEntry endThang =
00579             (endKey != null) ? makeRangeKey(endKey) : null;
00580 
00581         return range.subRange(beginThang, beginInclusive,
00582                               endThang, endInclusive);
00583     }
00584 
00588     private DatabaseEntry makeRangeKey(Object key)
00589         throws DatabaseException {
00590 
00591         DatabaseEntry thang = new DatabaseEntry();
00592         if (keyBinding != null) {
00593             useKey(key, null, thang, null);
00594         } else {
00595             useKey(null, key, thang, null);
00596         }
00597         return thang;
00598     }
00599 }

Generated on Sun Dec 25 12:14:31 2005 for Berkeley DB 4.4.16 by  doxygen 1.4.2