• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/ap/gpl/timemachine/db-4.7.25.NC/java/src/com/sleepycat/collections/
1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 2000,2008 Oracle.  All rights reserved.
5 *
6 * $Id: DataView.java,v 12.8 2008/02/07 17:12:26 mark Exp $
7 */
8
9package com.sleepycat.collections;
10
11import com.sleepycat.bind.EntityBinding;
12import com.sleepycat.bind.EntryBinding;
13import com.sleepycat.compat.DbCompat;
14import com.sleepycat.db.CursorConfig;
15import com.sleepycat.db.Database;
16import com.sleepycat.db.DatabaseConfig;
17import com.sleepycat.db.DatabaseEntry;
18import com.sleepycat.db.DatabaseException;
19import com.sleepycat.db.Environment;
20import com.sleepycat.db.JoinConfig;
21import com.sleepycat.db.OperationStatus;
22import com.sleepycat.db.SecondaryConfig;
23import com.sleepycat.db.SecondaryDatabase;
24import com.sleepycat.db.SecondaryKeyCreator;
25import com.sleepycat.db.Transaction;
26import com.sleepycat.util.RuntimeExceptionWrapper;
27import com.sleepycat.util.keyrange.KeyRange;
28import com.sleepycat.util.keyrange.KeyRangeException;
29
30/**
31 * Represents a Berkeley DB database and adds support for indices, bindings and
32 * key ranges.
33 *
34 * <p>This class defines a view and takes care of reading and updating indices,
35 * calling bindings, constraining access to a key range, etc.</p>
36 *
37 * @author Mark Hayes
38 */
39final class DataView implements Cloneable {
40
41    Database db;
42    SecondaryDatabase secDb;
43    CurrentTransaction currentTxn;
44    KeyRange range;
45    EntryBinding keyBinding;
46    EntryBinding valueBinding;
47    EntityBinding entityBinding;
48    PrimaryKeyAssigner keyAssigner;
49    SecondaryKeyCreator secKeyCreator;
50    CursorConfig cursorConfig;      // Used for all operations via this view
51    boolean writeAllowed;           // Read-write view
52    boolean ordered;                // Not a HASH Db
53    boolean keyRangesAllowed;       // BTREE only
54    boolean recNumAllowed;          // QUEUE, RECNO, or BTREE-RECNUM Db
55    boolean recNumAccess;           // recNumAllowed && using a rec num binding
56    boolean btreeRecNumDb;          // BTREE-RECNUM Db
57    boolean btreeRecNumAccess;      // recNumAccess && BTREE-RECNUM Db
58    boolean recNumRenumber;         // RECNO-RENUM Db
59    boolean keysRenumbered;         // recNumRenumber || btreeRecNumAccess
60    boolean dupsAllowed;            // Dups configured
61    boolean dupsOrdered;            // Sorted dups configured
62    boolean transactional;          // Db is transactional
63    boolean readUncommittedAllowed; // Read-uncommited is optional in DB-CORE
64
65    /*
66     * If duplicatesView is called, dupsView will be true and dupsKey will be
67     * the secondary key used as the "single key" range.  dupRange will be set
68     * as the range of the primary key values if subRange is subsequently
69     * called, to further narrow the view.
70     */
71    DatabaseEntry dupsKey;
72    boolean dupsView;
73    KeyRange dupsRange;
74
75    /**
76     * Creates a view for a given database and bindings.  The initial key range
77     * of the view will be open.
78     */
79    DataView(Database database, EntryBinding keyBinding,
80             EntryBinding valueBinding, EntityBinding entityBinding,
81             boolean writeAllowed, PrimaryKeyAssigner keyAssigner)
82        throws IllegalArgumentException {
83
84        if (database == null) {
85            throw new IllegalArgumentException("database is null");
86        }
87        db = database;
88        try {
89            currentTxn =
90                CurrentTransaction.getInstanceInternal(db.getEnvironment());
91            DatabaseConfig dbConfig;
92            if (db instanceof SecondaryDatabase) {
93                secDb = (SecondaryDatabase) database;
94                SecondaryConfig secConfig = secDb.getSecondaryConfig();
95                secKeyCreator = secConfig.getKeyCreator();
96                dbConfig = secConfig;
97            } else {
98                dbConfig = db.getConfig();
99            }
100            ordered = !DbCompat.isTypeHash(dbConfig);
101            keyRangesAllowed = DbCompat.isTypeBtree(dbConfig);
102            recNumAllowed = DbCompat.isTypeQueue(dbConfig) ||
103                            DbCompat.isTypeRecno(dbConfig) ||
104                            DbCompat.getBtreeRecordNumbers(dbConfig);
105            recNumRenumber = DbCompat.getRenumbering(dbConfig);
106            dupsAllowed = DbCompat.getSortedDuplicates(dbConfig) ||
107                          DbCompat.getUnsortedDuplicates(dbConfig);
108            dupsOrdered = DbCompat.getSortedDuplicates(dbConfig);
109            transactional = currentTxn.isTxnMode() &&
110                            dbConfig.getTransactional();
111            readUncommittedAllowed = DbCompat.getReadUncommitted(dbConfig);
112            btreeRecNumDb = recNumAllowed && DbCompat.isTypeBtree(dbConfig);
113            range = new KeyRange(dbConfig.getBtreeComparator());
114        } catch (DatabaseException e) {
115            throw new RuntimeExceptionWrapper(e);
116        }
117        this.writeAllowed = writeAllowed;
118        this.keyBinding = keyBinding;
119        this.valueBinding = valueBinding;
120        this.entityBinding = entityBinding;
121        this.keyAssigner = keyAssigner;
122        cursorConfig = CursorConfig.DEFAULT;
123
124        if (valueBinding != null && entityBinding != null)
125            throw new IllegalArgumentException(
126                "both valueBinding and entityBinding are non-null");
127
128        if (keyBinding instanceof com.sleepycat.bind.RecordNumberBinding) {
129            if (!recNumAllowed) {
130                throw new IllegalArgumentException(
131                    "RecordNumberBinding requires DB_BTREE/DB_RECNUM, " +
132                    "DB_RECNO, or DB_QUEUE");
133            }
134            recNumAccess = true;
135            if (btreeRecNumDb) {
136                btreeRecNumAccess = true;
137            }
138        }
139        keysRenumbered = recNumRenumber || btreeRecNumAccess;
140    }
141
142    /**
143     * Clones the view.
144     */
145    private DataView cloneView() {
146
147        try {
148            return (DataView) super.clone();
149        } catch (CloneNotSupportedException willNeverOccur) {
150            throw new IllegalStateException();
151        }
152    }
153
154    /**
155     * Return a new key-set view derived from this view by setting the
156     * entity and value binding to null.
157     *
158     * @return the derived view.
159     */
160    DataView keySetView() {
161
162        if (keyBinding == null) {
163            throw new UnsupportedOperationException("must have keyBinding");
164        }
165        DataView view = cloneView();
166        view.valueBinding = null;
167        view.entityBinding = null;
168        return view;
169    }
170
171    /**
172     * Return a new value-set view derived from this view by setting the
173     * key binding to null.
174     *
175     * @return the derived view.
176     */
177    DataView valueSetView() {
178
179        if (valueBinding == null && entityBinding == null) {
180            throw new UnsupportedOperationException(
181                "must have valueBinding or entityBinding");
182        }
183        DataView view = cloneView();
184        view.keyBinding = null;
185        return view;
186    }
187
188    /**
189     * Return a new value-set view for single key range.
190     *
191     * @param singleKey the single key value.
192     *
193     * @return the derived view.
194     *
195     * @throws DatabaseException if a database problem occurs.
196     *
197     * @throws KeyRangeException if the specified range is not within the
198     * current range.
199     */
200    DataView valueSetView(Object singleKey)
201        throws DatabaseException, KeyRangeException {
202
203        /*
204         * Must do subRange before valueSetView since the latter clears the
205         * key binding needed for the former.
206         */
207        KeyRange singleKeyRange = subRange(range, singleKey);
208        DataView view = valueSetView();
209        view.range = singleKeyRange;
210        return view;
211    }
212
213    /**
214     * Return a new value-set view for key range, optionally changing
215     * the key binding.
216     */
217    DataView subView(Object beginKey, boolean beginInclusive,
218                     Object endKey, boolean endInclusive,
219                     EntryBinding keyBinding)
220        throws DatabaseException, KeyRangeException {
221
222        DataView view = cloneView();
223        view.setRange(beginKey, beginInclusive, endKey, endInclusive);
224        if (keyBinding != null) view.keyBinding = keyBinding;
225        return view;
226    }
227
228    /**
229     * Return a new duplicates view for a given secondary key.
230     */
231    DataView duplicatesView(Object secondaryKey,
232                            EntryBinding primaryKeyBinding)
233        throws DatabaseException, KeyRangeException {
234
235        if (!isSecondary()) {
236            throw new UnsupportedOperationException
237                ("Only allowed for maps on secondary databases");
238        }
239        if (dupsView) {
240            throw new IllegalStateException();
241        }
242        DataView view = cloneView();
243        view.range = subRange(view.range, secondaryKey);
244        view.dupsKey = view.range.getSingleKey();
245        view.dupsView = true;
246        view.keyBinding = primaryKeyBinding;
247        return view;
248    }
249
250    /**
251     * Returns a new view with a specified cursor configuration.
252     */
253    DataView configuredView(CursorConfig config) {
254
255        DataView view = cloneView();
256        view.cursorConfig = (config != null) ?
257            DbCompat.cloneCursorConfig(config) : CursorConfig.DEFAULT;
258        return view;
259    }
260
261    /**
262     * Returns the current transaction for the view or null if the environment
263     * is non-transactional.
264     */
265    CurrentTransaction getCurrentTxn() {
266
267        return transactional ? currentTxn : null;
268    }
269
270    /**
271     * Sets this view's range to a subrange with the given parameters.
272     */
273    private void setRange(Object beginKey, boolean beginInclusive,
274                          Object endKey, boolean endInclusive)
275        throws DatabaseException, KeyRangeException {
276
277        if ((beginKey != null || endKey != null) && !keyRangesAllowed) {
278            throw new UnsupportedOperationException
279                ("Key ranges allowed only for BTREE databases");
280        }
281        KeyRange useRange = useSubRange();
282        useRange = subRange
283            (useRange, beginKey, beginInclusive, endKey, endInclusive);
284        if (dupsView) {
285            dupsRange = useRange;
286        } else {
287            range = useRange;
288        }
289    }
290
291    /**
292     * Returns the key thang for a single key range, or null if a single key
293     * range is not used.
294     */
295    DatabaseEntry getSingleKeyThang() {
296
297        return range.getSingleKey();
298    }
299
300    /**
301     * Returns the environment for the database.
302     */
303    final Environment getEnv() {
304
305        return currentTxn.getEnvironment();
306    }
307
308    /**
309     * Returns whether this is a view on a secondary database rather
310     * than directly on a primary database.
311     */
312    final boolean isSecondary() {
313
314        return (secDb != null);
315    }
316
317    /**
318     * Returns whether no records are present in the view.
319     */
320    boolean isEmpty()
321        throws DatabaseException {
322
323        DataCursor cursor = new DataCursor(this, false);
324        try {
325            return cursor.getFirst(false) != OperationStatus.SUCCESS;
326        } finally {
327            cursor.close();
328        }
329    }
330
331    /**
332     * Appends a value and returns the new key.  If a key assigner is used
333     * it assigns the key, otherwise a QUEUE or RECNO database is required.
334     */
335    OperationStatus append(Object value, Object[] retPrimaryKey,
336                           Object[] retValue)
337        throws DatabaseException {
338
339        /*
340         * Flags will be NOOVERWRITE if used with assigner, or APPEND
341         * otherwise.
342         * Requires: if value param, value or entity binding
343         * Requires: if retPrimaryKey, primary key binding (no index).
344         * Requires: if retValue, value or entity binding
345         */
346        DatabaseEntry keyThang = new DatabaseEntry();
347        DatabaseEntry valueThang = new DatabaseEntry();
348        useValue(value, valueThang, null);
349        OperationStatus status;
350        if (keyAssigner != null) {
351            keyAssigner.assignKey(keyThang);
352            if (!range.check(keyThang)) {
353                throw new IllegalArgumentException(
354                    "assigned key out of range");
355            }
356            DataCursor cursor = new DataCursor(this, true);
357            try {
358                status = cursor.getCursor().putNoOverwrite(keyThang,
359                                                           valueThang);
360            } finally {
361                cursor.close();
362            }
363        } else {
364            /* Assume QUEUE/RECNO access method. */
365            if (currentTxn.isCDBCursorOpen(db)) {
366                throw new IllegalStateException(
367                  "cannot open CDB write cursor when read cursor is open");
368            }
369            status = DbCompat.append(db, useTransaction(),
370                                     keyThang, valueThang);
371            if (status == OperationStatus.SUCCESS && !range.check(keyThang)) {
372                db.delete(useTransaction(), keyThang);
373                throw new IllegalArgumentException(
374                    "appended record number out of range");
375            }
376        }
377        if (status == OperationStatus.SUCCESS) {
378            returnPrimaryKeyAndValue(keyThang, valueThang,
379                                     retPrimaryKey, retValue);
380        }
381        return status;
382    }
383
384    /**
385     * Returns the current transaction if the database is transaction, or null
386     * if the database is not transactional or there is no current transaction.
387     */
388    Transaction useTransaction() {
389        return transactional ?  currentTxn.getTransaction() : null;
390    }
391
392    /**
393     * Deletes all records in the current range.
394     */
395    void clear()
396        throws DatabaseException {
397
398        DataCursor cursor = new DataCursor(this, true);
399        try {
400            OperationStatus status = OperationStatus.SUCCESS;
401            while (status == OperationStatus.SUCCESS) {
402                if (keysRenumbered) {
403                    status = cursor.getFirst(true);
404                } else {
405                    status = cursor.getNext(true);
406                }
407                if (status == OperationStatus.SUCCESS) {
408                    cursor.delete();
409                }
410            }
411        } finally {
412            cursor.close();
413        }
414    }
415
416    /**
417     * Returns a cursor for this view that reads only records having the
418     * specified index key values.
419     */
420    DataCursor join(DataView[] indexViews, Object[] indexKeys,
421                    JoinConfig joinConfig)
422        throws DatabaseException {
423
424        DataCursor joinCursor = null;
425        DataCursor[] indexCursors = new DataCursor[indexViews.length];
426        try {
427            for (int i = 0; i < indexViews.length; i += 1) {
428                indexCursors[i] = new DataCursor(indexViews[i], false);
429                indexCursors[i].getSearchKey(indexKeys[i], null, false);
430            }
431            joinCursor = new DataCursor(this, indexCursors, joinConfig, true);
432            return joinCursor;
433        } finally {
434            if (joinCursor == null) {
435                // An exception is being thrown, so close cursors we opened.
436                for (int i = 0; i < indexCursors.length; i += 1) {
437                    if (indexCursors[i] != null) {
438                        try { indexCursors[i].close(); }
439                        catch (Exception e) {
440			    /* FindBugs, this is ok. */
441			}
442                    }
443                }
444            }
445        }
446    }
447
448    /**
449     * Returns a cursor for this view that reads only records having the
450     * index key values at the specified cursors.
451     */
452    DataCursor join(DataCursor[] indexCursors, JoinConfig joinConfig)
453        throws DatabaseException {
454
455        return new DataCursor(this, indexCursors, joinConfig, false);
456    }
457
458    /**
459     * Returns primary key and value if return parameters are non-null.
460     */
461    private void returnPrimaryKeyAndValue(DatabaseEntry keyThang,
462                                          DatabaseEntry valueThang,
463                                          Object[] retPrimaryKey,
464                                          Object[] retValue)
465        throws DatabaseException {
466
467        // Requires: if retPrimaryKey, primary key binding (no index).
468        // Requires: if retValue, value or entity binding
469
470        if (retPrimaryKey != null) {
471            if (keyBinding == null) {
472                throw new IllegalArgumentException(
473                    "returning key requires primary key binding");
474            } else if (isSecondary()) {
475                throw new IllegalArgumentException(
476                    "returning key requires unindexed view");
477            } else {
478                retPrimaryKey[0] = keyBinding.entryToObject(keyThang);
479            }
480        }
481        if (retValue != null) {
482            retValue[0] = makeValue(keyThang, valueThang);
483        }
484    }
485
486    /**
487     * Populates the key entry and returns whether the key is within range.
488     */
489    boolean useKey(Object key, Object value, DatabaseEntry keyThang,
490                   KeyRange checkRange)
491        throws DatabaseException {
492
493        if (key != null) {
494            if (keyBinding == null) {
495                throw new IllegalArgumentException(
496                    "non-null key with null key binding");
497            }
498            keyBinding.objectToEntry(key, keyThang);
499        } else {
500            if (value == null) {
501                throw new IllegalArgumentException(
502                    "null key and null value");
503            }
504            if (entityBinding == null) {
505                throw new IllegalStateException(
506                    "EntityBinding required to derive key from value");
507            }
508            if (!dupsView && isSecondary()) {
509                DatabaseEntry primaryKeyThang = new DatabaseEntry();
510                entityBinding.objectToKey(value, primaryKeyThang);
511                DatabaseEntry valueThang = new DatabaseEntry();
512                entityBinding.objectToData(value, valueThang);
513                secKeyCreator.createSecondaryKey(secDb, primaryKeyThang,
514                                                 valueThang, keyThang);
515            } else {
516                entityBinding.objectToKey(value, keyThang);
517            }
518        }
519        if (recNumAccess && DbCompat.getRecordNumber(keyThang) <= 0) {
520            return false;
521        }
522        if (checkRange != null && !checkRange.check(keyThang)) {
523            return false;
524        }
525        return true;
526    }
527
528    /**
529     * Returns whether data keys can be derived from the value/entity binding
530     * of this view, which determines whether a value/entity object alone is
531     * sufficient for operations that require keys.
532     */
533    final boolean canDeriveKeyFromValue() {
534
535        return (entityBinding != null);
536    }
537
538    /**
539     * Populates the value entry and throws an exception if the primary key
540     * would be changed via an entity binding.
541     */
542    void useValue(Object value, DatabaseEntry valueThang,
543                  DatabaseEntry checkKeyThang)
544        throws DatabaseException {
545
546        if (value != null) {
547            if (valueBinding != null) {
548                valueBinding.objectToEntry(value, valueThang);
549            } else if (entityBinding != null) {
550                entityBinding.objectToData(value, valueThang);
551                if (checkKeyThang != null) {
552                    DatabaseEntry thang = new DatabaseEntry();
553                    entityBinding.objectToKey(value, thang);
554                    if (!KeyRange.equalBytes(thang, checkKeyThang)) {
555                        throw new IllegalArgumentException(
556                            "cannot change primary key");
557                    }
558                }
559            } else {
560                throw new IllegalArgumentException(
561                    "non-null value with null value/entity binding");
562            }
563        } else {
564            valueThang.setData(KeyRange.ZERO_LENGTH_BYTE_ARRAY);
565            valueThang.setOffset(0);
566            valueThang.setSize(0);
567        }
568    }
569
570    /**
571     * Converts a key entry to a key object.
572     */
573    Object makeKey(DatabaseEntry keyThang, DatabaseEntry priKeyThang) {
574
575        if (keyBinding == null) {
576            throw new UnsupportedOperationException();
577        } else {
578            DatabaseEntry thang = dupsView ? priKeyThang : keyThang;
579            if (thang.getSize() == 0) {
580                return null;
581            } else {
582                return keyBinding.entryToObject(thang);
583            }
584        }
585    }
586
587    /**
588     * Converts a key-value entry pair to a value object.
589     */
590    Object makeValue(DatabaseEntry primaryKeyThang, DatabaseEntry valueThang) {
591
592        Object value;
593        if (valueBinding != null) {
594            value = valueBinding.entryToObject(valueThang);
595        } else if (entityBinding != null) {
596            value = entityBinding.entryToObject(primaryKeyThang,
597                                                    valueThang);
598        } else {
599            throw new UnsupportedOperationException(
600                "requires valueBinding or entityBinding");
601        }
602        return value;
603    }
604
605    /**
606     * Intersects the given key and the current range.
607     */
608    KeyRange subRange(KeyRange useRange, Object singleKey)
609        throws DatabaseException, KeyRangeException {
610
611        return useRange.subRange(makeRangeKey(singleKey));
612    }
613
614    /**
615     * Intersects the given range and the current range.
616     */
617    KeyRange subRange(KeyRange useRange,
618                      Object beginKey, boolean beginInclusive,
619                      Object endKey, boolean endInclusive)
620        throws DatabaseException, KeyRangeException {
621
622        if (beginKey == endKey && beginInclusive && endInclusive) {
623            return subRange(useRange, beginKey);
624        }
625        if (!ordered) {
626            throw new UnsupportedOperationException(
627                    "Cannot use key ranges on an unsorted database");
628        }
629        DatabaseEntry beginThang =
630            (beginKey != null) ? makeRangeKey(beginKey) : null;
631        DatabaseEntry endThang =
632            (endKey != null) ? makeRangeKey(endKey) : null;
633
634        return useRange.subRange(beginThang, beginInclusive,
635                                 endThang, endInclusive);
636    }
637
638    /**
639     * Returns the range to use for sub-ranges.  Returns range if this is not a
640     * dupsView, or the dupsRange if this is a dupsView, creating dupsRange if
641     * necessary.
642     */
643    KeyRange useSubRange()
644        throws DatabaseException {
645
646        if (dupsView) {
647            synchronized (this) {
648                if (dupsRange == null) {
649                    DatabaseConfig config =
650                        secDb.getPrimaryDatabase().getConfig();
651                    dupsRange = new KeyRange(config.getBtreeComparator());
652                }
653            }
654            return dupsRange;
655        } else {
656            return range;
657        }
658    }
659
660    /**
661     * Given a key object, make a key entry that can be used in a range.
662     */
663    private DatabaseEntry makeRangeKey(Object key)
664        throws DatabaseException {
665
666        DatabaseEntry thang = new DatabaseEntry();
667        if (keyBinding != null) {
668            useKey(key, null, thang, null);
669        } else {
670            useKey(null, key, thang, null);
671        }
672        return thang;
673    }
674}
675