KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sleepycat > je > SecondaryDatabase


1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2002,2006 Oracle. All rights reserved.
5  *
6  * $Id: SecondaryDatabase.java,v 1.52 2006/11/06 20:36:55 linda Exp $
7  */

8
9 package com.sleepycat.je;
10
11 import java.util.Collections JavaDoc;
12 import java.util.HashSet JavaDoc;
13 import java.util.Iterator JavaDoc;
14 import java.util.Set JavaDoc;
15 import java.util.logging.Level JavaDoc;
16 import java.util.logging.Logger JavaDoc;
17
18 import com.sleepycat.je.dbi.DatabaseImpl;
19 import com.sleepycat.je.dbi.GetMode;
20 import com.sleepycat.je.dbi.PutMode;
21 import com.sleepycat.je.dbi.CursorImpl.SearchMode;
22 import com.sleepycat.je.txn.Locker;
23 import com.sleepycat.je.txn.LockerFactory;
24 import com.sleepycat.je.utilint.DatabaseUtil;
25
26 /**
27  * Javadoc for this public class is generated via
28  * the doc templates in the doc_src directory.
29  */

30 public class SecondaryDatabase extends Database {
31
32     private Database primaryDb;
33     private SecondaryConfig secondaryConfig;
34     private SecondaryTrigger secondaryTrigger;
35     private ForeignKeyTrigger foreignKeyTrigger;
36
37     /**
38      * Creates a secondary database but does not open or fully initialize it.
39      */

40     SecondaryDatabase(Environment env,
41               SecondaryConfig secConfig,
42                       Database primaryDatabase)
43         throws DatabaseException {
44
45         super(env);
46         DatabaseUtil.checkForNullParam(primaryDatabase, "primaryDatabase");
47         primaryDatabase.checkRequiredDbState(OPEN, "Can't use as primary:");
48         if (primaryDatabase.configuration.getSortedDuplicates()) {
49             throw new IllegalArgumentException JavaDoc
50                 ("Duplicates must not be allowed for a primary database: " +
51                  primaryDatabase.getDebugName());
52         }
53         if (env.getEnvironmentImpl() !=
54                 primaryDatabase.getEnvironment().getEnvironmentImpl()) {
55             throw new IllegalArgumentException JavaDoc
56                 ("Primary and secondary databases must be in the same" +
57                  " environment");
58         }
59         if (secConfig.getKeyCreator() != null &&
60             secConfig.getMultiKeyCreator() != null) {
61             throw new IllegalArgumentException JavaDoc
62                 ("secConfig.getKeyCreator() and getMultiKeyCreator() may not" +
63                  " both be non-null");
64         }
65         if (!primaryDatabase.configuration.getReadOnly() &&
66             secConfig.getKeyCreator() == null &&
67             secConfig.getMultiKeyCreator() == null) {
68             throw new NullPointerException JavaDoc
69                 ("secConfig and getKeyCreator()/getMultiKeyCreator()" +
70                  " may be null only if the primary database is read-only");
71         }
72         if (secConfig.getForeignKeyNullifier() != null &&
73             secConfig.getForeignMultiKeyNullifier() != null) {
74             throw new IllegalArgumentException JavaDoc
75                 ("secConfig.getForeignKeyNullifier() and" +
76                  " getForeignMultiKeyNullifier() may not both be non-null");
77         }
78         if (secConfig.getForeignKeyDeleteAction() ==
79                          ForeignKeyDeleteAction.NULLIFY &&
80             secConfig.getForeignKeyNullifier() == null &&
81             secConfig.getForeignMultiKeyNullifier() == null) {
82             throw new NullPointerException JavaDoc
83                 ("ForeignKeyNullifier or ForeignMultiKeyNullifier must be" +
84                  " non-null when ForeignKeyDeleteAction is NULLIFY");
85         }
86         if (secConfig.getForeignKeyNullifier() != null &&
87             secConfig.getMultiKeyCreator() != null) {
88             throw new IllegalArgumentException JavaDoc
89                 ("ForeignKeyNullifier may not be used with" +
90                  " SecondaryMultiKeyCreator -- use" +
91                  " ForeignMultiKeyNullifier instead");
92         }
93         if (secConfig.getForeignKeyDatabase() != null) {
94             Database foreignDb = secConfig.getForeignKeyDatabase();
95             if (foreignDb.getDatabaseImpl().getSortedDuplicates()) {
96                 throw new IllegalArgumentException JavaDoc
97                     ("Duplicates must not be allowed for a foreign key " +
98                      " database: " + foreignDb.getDebugName());
99             }
100         }
101         primaryDb = primaryDatabase;
102         secondaryTrigger = new SecondaryTrigger(this);
103         if (secConfig.getForeignKeyDatabase() != null) {
104             foreignKeyTrigger = new ForeignKeyTrigger(this);
105         }
106     }
107
108     /**
109      * Create a database, called by Environment
110      */

111     void initNew(Environment env,
112                  Locker locker,
113                  String JavaDoc databaseName,
114                  DatabaseConfig dbConfig)
115         throws DatabaseException {
116
117         super.initNew(env, locker, databaseName, dbConfig);
118         init(locker);
119     }
120
121     /**
122      * Open a database, called by Environment
123      */

124     void initExisting(Environment env,
125                       Locker locker,
126                       DatabaseImpl database,
127                       DatabaseConfig dbConfig)
128         throws DatabaseException {
129
130         /* Disallow one secondary associated with two different primaries. */
131         Database otherPriDb = database.findPrimaryDatabase();
132         if (otherPriDb != null &&
133             otherPriDb.getDatabaseImpl() != primaryDb.getDatabaseImpl()) {
134             throw new IllegalArgumentException JavaDoc
135                 ("Secondary is already associated with a different primary: " +
136                  otherPriDb.getDebugName());
137         }
138
139         super.initExisting(env, locker, database, dbConfig);
140         init(locker);
141     }
142
143     /**
144      * Adds secondary to primary's list, and populates the secondary if needed.
145      */

146     private void init(Locker locker)
147         throws DatabaseException {
148
149         trace(Level.FINEST, "SecondaryDatabase open");
150
151         secondaryConfig = (SecondaryConfig) configuration;
152
153         /* Insert foreign key triggers at the front of the list and append
154          * secondary triggers at the end, so that ForeignKeyDeleteAction.ABORT
155          * is applied before deleting the secondary keys. */

156
157         primaryDb.addTrigger(secondaryTrigger, false);
158
159         Database foreignDb = secondaryConfig.getForeignKeyDatabase();
160         if (foreignDb != null) {
161             foreignDb.addTrigger(foreignKeyTrigger, true);
162         }
163
164         /* Populate secondary if requested and secondary is empty. */
165         if (secondaryConfig.getAllowPopulate()) {
166             Cursor secCursor = null;
167             Cursor priCursor = null;
168             try {
169                 secCursor = new Cursor(this, locker, null);
170                 DatabaseEntry key = new DatabaseEntry();
171                 DatabaseEntry data = new DatabaseEntry();
172                 OperationStatus status = secCursor.position(key, data,
173                                                             LockMode.DEFAULT,
174                                                             true);
175                 if (status == OperationStatus.NOTFOUND) {
176                     /* Is empty, so populate */
177                     priCursor = new Cursor(primaryDb, locker, null);
178                     status = priCursor.position(key, data, LockMode.DEFAULT,
179                                                 true);
180                     while (status == OperationStatus.SUCCESS) {
181                         updateSecondary(locker, secCursor, key, null, data);
182                         status = priCursor.retrieveNext(key, data,
183                                                         LockMode.DEFAULT,
184                                                         GetMode.NEXT);
185                     }
186                 }
187             } finally {
188                 if (secCursor != null) {
189                     secCursor.close();
190                 }
191                 if (priCursor != null) {
192                     priCursor.close();
193                 }
194             }
195         }
196     }
197
198     /**
199      * Javadoc for this public method is generated via
200      * the doc templates in the doc_src directory.
201      */

202     public synchronized void close()
203         throws DatabaseException {
204
205         if (primaryDb != null && secondaryTrigger != null) {
206             primaryDb.removeTrigger(secondaryTrigger);
207         }
208         Database foreignDb = secondaryConfig.getForeignKeyDatabase();
209         if (foreignDb != null && foreignKeyTrigger != null) {
210             foreignDb.removeTrigger(foreignKeyTrigger);
211         }
212         super.close();
213     }
214
215     /**
216      * Should be called by the secondaryTrigger while holding a write lock on
217      * the trigger list.
218      */

219     void clearPrimary() {
220         primaryDb = null;
221         secondaryTrigger = null;
222     }
223
224     /**
225      * Should be called by the foreignKeyTrigger while holding a write lock on
226      * the trigger list.
227      */

228     void clearForeignKeyTrigger() {
229         foreignKeyTrigger = null;
230     }
231
232     /**
233      * Javadoc for this public method is generated via
234      * the doc templates in the doc_src directory.
235      */

236     public Database getPrimaryDatabase()
237         throws DatabaseException {
238
239         return primaryDb;
240     }
241
242     /**
243      * Javadoc for this public method is generated via
244      * the doc templates in the doc_src directory.
245      */

246     public SecondaryConfig getSecondaryConfig()
247         throws DatabaseException {
248
249         return (SecondaryConfig) getConfig();
250     }
251
252     /**
253      * Returns the secondary config without cloning, for internal use.
254      */

255     public SecondaryConfig getPrivateSecondaryConfig() {
256         return secondaryConfig;
257     }
258
259     /**
260      * Javadoc for this public method is generated via
261      * the doc templates in the doc_src directory.
262      */

263     public SecondaryCursor openSecondaryCursor(Transaction txn,
264                                                CursorConfig cursorConfig)
265         throws DatabaseException {
266
267         return (SecondaryCursor) openCursor(txn, cursorConfig);
268     }
269  
270     /**
271      * Overrides Database method.
272      */

273     Cursor newDbcInstance(Transaction txn,
274                           CursorConfig cursorConfig)
275         throws DatabaseException {
276
277         return new SecondaryCursor(this, txn, cursorConfig);
278     }
279
280     /**
281      * Javadoc for this public method is generated via
282      * the doc templates in the doc_src directory.
283      */

284     public OperationStatus delete(Transaction txn,
285                                   DatabaseEntry key)
286         throws DatabaseException {
287
288         checkEnv();
289         DatabaseUtil.checkForNullDbt(key, "key", true);
290         checkRequiredDbState(OPEN, "Can't call SecondaryDatabase.delete:");
291         trace(Level.FINEST, "SecondaryDatabase.delete", txn,
292               key, null, null);
293
294         Locker locker = null;
295         Cursor cursor = null;
296
297         OperationStatus commitStatus = OperationStatus.NOTFOUND;
298         try {
299             locker = LockerFactory.getWritableLocker
300                 (envHandle, txn, isTransactional());
301
302             /* Read the primary key (the data of a secondary). */
303             cursor = new Cursor(this, locker, null);
304             DatabaseEntry pKey = new DatabaseEntry();
305             OperationStatus searchStatus =
306                 cursor.search(key, pKey, LockMode.RMW, SearchMode.SET);
307
308             /*
309              * For each duplicate secondary key, delete the primary record and
310              * all its associated secondary records, including the one
311              * referenced by this secondary cursor.
312              */

313             while (searchStatus == OperationStatus.SUCCESS) {
314                 commitStatus = primaryDb.deleteInternal(locker, pKey, null);
315                 if (commitStatus != OperationStatus.SUCCESS) {
316                     throw secondaryCorruptException();
317                 }
318                 searchStatus = cursor.retrieveNext
319                     (key, pKey, LockMode.RMW, GetMode.NEXT_DUP);
320             }
321             return commitStatus;
322     } catch (Error JavaDoc E) {
323         DbInternal.envGetEnvironmentImpl(envHandle).invalidate(E);
324         throw E;
325         } finally {
326             if (cursor != null) {
327                 cursor.close();
328             }
329             if (locker != null) {
330                 locker.operationEnd(commitStatus);
331             }
332         }
333     }
334
335     /**
336      * Javadoc for this public method is generated via
337      * the doc templates in the doc_src directory.
338      */

339     public OperationStatus get(Transaction txn,
340                                DatabaseEntry key,
341                                DatabaseEntry data,
342                                LockMode lockMode)
343         throws DatabaseException {
344
345         return get(txn, key, new DatabaseEntry(), data, lockMode);
346     }
347
348     /**
349      * Javadoc for this public method is generated via
350      * the doc templates in the doc_src directory.
351      */

352     public OperationStatus get(Transaction txn,
353                                DatabaseEntry key,
354                                DatabaseEntry pKey,
355                                DatabaseEntry data,
356                                LockMode lockMode)
357         throws DatabaseException {
358
359         checkEnv();
360         DatabaseUtil.checkForNullDbt(key, "key", true);
361         DatabaseUtil.checkForNullDbt(pKey, "pKey", false);
362         DatabaseUtil.checkForNullDbt(data, "data", false);
363         checkRequiredDbState(OPEN, "Can't call SecondaryDatabase.get:");
364         trace(Level.FINEST, "SecondaryDatabase.get", txn, key, null, lockMode);
365
366         CursorConfig cursorConfig = CursorConfig.DEFAULT;
367         if (lockMode == LockMode.READ_COMMITTED) {
368             cursorConfig = CursorConfig.READ_COMMITTED;
369             lockMode = null;
370         }
371
372         SecondaryCursor cursor = null;
373         try {
374         cursor = new SecondaryCursor(this, txn, cursorConfig);
375             return cursor.search(key, pKey, data, lockMode, SearchMode.SET);
376     } catch (Error JavaDoc E) {
377         DbInternal.envGetEnvironmentImpl(envHandle).invalidate(E);
378         throw E;
379         } finally {
380             if (cursor != null) {
381                 cursor.close();
382             }
383         }
384     }
385
386     /**
387      * Javadoc for this public method is generated via
388      * the doc templates in the doc_src directory.
389      */

390     public OperationStatus getSearchBoth(Transaction txn,
391                                          DatabaseEntry key,
392                                          DatabaseEntry data,
393                                          LockMode lockMode)
394         throws DatabaseException {
395
396         throw notAllowedException();
397     }
398
399     /**
400      * Javadoc for this public method is generated via
401      * the doc templates in the doc_src directory.
402      */

403     public OperationStatus getSearchBoth(Transaction txn,
404                                          DatabaseEntry key,
405                                          DatabaseEntry pKey,
406                                          DatabaseEntry data,
407                                          LockMode lockMode)
408         throws DatabaseException {
409
410         checkEnv();
411         DatabaseUtil.checkForNullDbt(key, "key", true);
412         DatabaseUtil.checkForNullDbt(pKey, "pKey", true);
413         DatabaseUtil.checkForNullDbt(data, "data", false);
414         checkRequiredDbState(OPEN,
415                              "Can't call SecondaryDatabase.getSearchBoth:");
416         trace(Level.FINEST, "SecondaryDatabase.getSearchBoth", txn, key, data,
417               lockMode);
418
419         CursorConfig cursorConfig = CursorConfig.DEFAULT;
420         if (lockMode == LockMode.READ_COMMITTED) {
421             cursorConfig = CursorConfig.READ_COMMITTED;
422             lockMode = null;
423         }
424
425         SecondaryCursor cursor = null;
426         try {
427         cursor = new SecondaryCursor(this, txn, cursorConfig);
428             return cursor.search(key, pKey, data, lockMode, SearchMode.BOTH);
429     } catch (Error JavaDoc E) {
430         DbInternal.envGetEnvironmentImpl(envHandle).invalidate(E);
431         throw E;
432         } finally {
433             if (cursor != null) {
434                 cursor.close();
435             }
436         }
437     }
438
439     /**
440      * Javadoc for this public method is generated via
441      * the doc templates in the doc_src directory.
442      */

443     public OperationStatus put(Transaction txn,
444                                DatabaseEntry key,
445                                DatabaseEntry data)
446         throws DatabaseException {
447
448         throw notAllowedException();
449     }
450
451     /**
452      * Javadoc for this public method is generated via
453      * the doc templates in the doc_src directory.
454      */

455     public OperationStatus putNoOverwrite(Transaction txn,
456                                           DatabaseEntry key,
457                                           DatabaseEntry data)
458         throws DatabaseException {
459
460         throw notAllowedException();
461     }
462
463     /**
464      * Javadoc for this public method is generated via
465      * the doc templates in the doc_src directory.
466      */

467     public OperationStatus putNoDupData(Transaction txn,
468                                         DatabaseEntry key,
469                                         DatabaseEntry data)
470         throws DatabaseException {
471
472         throw notAllowedException();
473     }
474
475     /**
476      * Javadoc for this public method is generated via
477      * the doc templates in the doc_src directory.
478      */

479     public JoinCursor join(Cursor[] cursors, JoinConfig config)
480         throws DatabaseException {
481
482         throw notAllowedException();
483     }
484
485     /**
486      * Javadoc for this public method is generated via
487      * the doc templates in the doc_src directory.
488      * @deprecated
489      */

490     public int truncate(Transaction txn, boolean countRecords)
491         throws DatabaseException {
492
493         throw notAllowedException();
494     }
495     
496     /**
497      * Updates a single secondary when a put() or delete() is performed on
498      * the primary.
499      *
500      * @param locker the internal locker.
501      *
502      * @param cursor secondary cursor to use, or null if this method should
503      * open and close a cursor if one is needed.
504      *
505      * @param priKey the primary key.
506      *
507      * @param oldData the primary data before the change, or null if the record
508      * did not previously exist.
509      *
510      * @param newData the primary data after the change, or null if the record
511      * has been deleted.
512      */

513     void updateSecondary(Locker locker,
514                          Cursor cursor,
515                          DatabaseEntry priKey,
516                          DatabaseEntry oldData,
517                          DatabaseEntry newData)
518         throws DatabaseException {
519
520         /*
521          * If we're updating the primary and the secondary key cannot be
522          * changed, optimize for that case by doing nothing.
523          */

524         if (secondaryConfig.getImmutableSecondaryKey() &&
525             oldData != null && newData != null) {
526             return;
527         }
528
529         SecondaryKeyCreator keyCreator = secondaryConfig.getKeyCreator();
530         if (keyCreator != null) {
531             /* Each primary record may have a single secondary key. */
532             assert secondaryConfig.getMultiKeyCreator() == null;
533
534             /* Get old and new secondary keys. */
535             DatabaseEntry oldSecKey = null;
536             if (oldData != null) {
537                 oldSecKey = new DatabaseEntry();
538                 if (!keyCreator.createSecondaryKey(this, priKey, oldData,
539                                                    oldSecKey)) {
540                     oldSecKey = null;
541                 }
542             }
543             DatabaseEntry newSecKey = null;
544             if (newData != null) {
545                 newSecKey = new DatabaseEntry();
546                 if (!keyCreator.createSecondaryKey(this, priKey, newData,
547                                                    newSecKey)) {
548                     newSecKey = null;
549                 }
550             }
551
552             /* Update secondary if old and new keys are unequal. */
553             if ((oldSecKey != null && !oldSecKey.equals(newSecKey)) ||
554                 (newSecKey != null && !newSecKey.equals(oldSecKey))) {
555
556                 boolean localCursor = (cursor == null);
557                 if (localCursor) {
558                     cursor = new Cursor(this, locker, null);
559                 }
560                 try {
561                     /* Delete the old key. */
562                     if (oldSecKey != null) {
563                         deleteKey(cursor, priKey, oldSecKey);
564                     }
565                     /* Insert the new key. */
566                     if (newSecKey != null) {
567                         insertKey(locker, cursor, priKey, newSecKey);
568                     }
569                 } finally {
570                     if (localCursor && cursor != null) {
571                         cursor.close();
572                     }
573                 }
574             }
575         } else {
576             /* Each primary record may have multiple secondary keys. */
577             SecondaryMultiKeyCreator multiKeyCreator =
578                 secondaryConfig.getMultiKeyCreator();
579             assert multiKeyCreator != null;
580
581             /* Get old and new secondary keys. */
582             Set JavaDoc oldKeys = Collections.EMPTY_SET;
583             Set JavaDoc newKeys = Collections.EMPTY_SET;
584             if (oldData != null) {
585                 oldKeys = new HashSet JavaDoc();
586                 multiKeyCreator.createSecondaryKeys(this, priKey,
587                                                     oldData, oldKeys);
588             }
589             if (newData != null) {
590                 newKeys = new HashSet JavaDoc();
591                 multiKeyCreator.createSecondaryKeys(this, priKey,
592                                                     newData, newKeys);
593             }
594
595             /* Update the secondary if there is a difference. */
596             if (!oldKeys.equals(newKeys)) {
597
598                 boolean localCursor = (cursor == null);
599                 if (localCursor) {
600                     cursor = new Cursor(this, locker, null);
601                 }
602                 try {
603                     /* Delete old keys that are no longer present. */
604                     Set JavaDoc oldKeysCopy = oldKeys;
605                     if (oldKeys != Collections.EMPTY_SET) {
606                         oldKeysCopy = new HashSet JavaDoc(oldKeys);
607                         oldKeys.removeAll(newKeys);
608                         for (Iterator JavaDoc i = oldKeys.iterator(); i.hasNext();) {
609                             DatabaseEntry oldKey = (DatabaseEntry) i.next();
610                             deleteKey(cursor, priKey, oldKey);
611                         }
612                     }
613                     /* Insert new keys that were not present before. */
614                     if (newKeys != Collections.EMPTY_SET) {
615                         newKeys.removeAll(oldKeysCopy);
616                         for (Iterator JavaDoc i = newKeys.iterator(); i.hasNext();) {
617                             DatabaseEntry newKey = (DatabaseEntry) i.next();
618                             insertKey(locker, cursor, priKey, newKey);
619                         }
620                     }
621                 } finally {
622                     if (localCursor && cursor != null) {
623                         cursor.close();
624                     }
625                 }
626             }
627         }
628     }
629
630     /**
631      * Deletes an old secondary key.
632      */

633     private void deleteKey(Cursor cursor,
634                            DatabaseEntry priKey,
635                            DatabaseEntry oldSecKey)
636         throws DatabaseException {
637
638         OperationStatus status =
639             cursor.search(oldSecKey, priKey,
640                           LockMode.RMW,
641                           SearchMode.BOTH);
642         if (status == OperationStatus.SUCCESS) {
643             cursor.deleteInternal();
644         } else {
645             throw new DatabaseException
646                 ("Secondary " + getDebugName() +
647                 " is corrupt: the primary record contains a key" +
648                 " that is not present in the secondary");
649         }
650     }
651
652     /**
653      * Inserts a new secondary key.
654      */

655     private void insertKey(Locker locker,
656                            Cursor cursor,
657                            DatabaseEntry priKey,
658                            DatabaseEntry newSecKey)
659         throws DatabaseException {
660
661         /* Check for the existence of a foreign key. */
662         Database foreignDb =
663             secondaryConfig.getForeignKeyDatabase();
664         if (foreignDb != null) {
665             Cursor foreignCursor = null;
666             try {
667                 foreignCursor = new Cursor(foreignDb, locker,
668                                            null);
669                 DatabaseEntry tmpData = new DatabaseEntry();
670                 OperationStatus status =
671                     foreignCursor.search(newSecKey, tmpData,
672                                          LockMode.DEFAULT,
673                                          SearchMode.SET);
674                 if (status != OperationStatus.SUCCESS) {
675                     throw new DatabaseException
676                         ("Secondary " + getDebugName() +
677                          " foreign key not allowed: it is not" +
678                          " present in the foreign database " +
679                          foreignDb.getDebugName());
680                 }
681             } finally {
682                 if (foreignCursor != null) {
683                     foreignCursor.close();
684                 }
685             }
686         }
687
688         /* Insert the new key. */
689         OperationStatus status;
690         if (configuration.getSortedDuplicates()) {
691             status = cursor.putInternal(newSecKey, priKey,
692                                         PutMode.NODUP);
693         } else {
694             status = cursor.putInternal(newSecKey, priKey,
695                                         PutMode.NOOVERWRITE);
696         }
697         if (status != OperationStatus.SUCCESS) {
698             throw new DatabaseException
699                 ("Could not insert secondary key in " +
700                  getDebugName() + ' ' + status);
701         }
702     }
703
704     /**
705      * Called by the ForeignKeyTrigger when a record in the foreign database is
706      * deleted.
707      *
708      * @param secKey is the primary key of the foreign database, which is the
709      * secondary key (ordinary key) of this secondary database.
710      */

711     void onForeignKeyDelete(Locker locker, DatabaseEntry secKey)
712         throws DatabaseException {
713
714         ForeignKeyDeleteAction deleteAction =
715             secondaryConfig.getForeignKeyDeleteAction();
716
717         /* Use RMW if we're going to be deleting the secondary records. */
718         LockMode lockMode = (deleteAction == ForeignKeyDeleteAction.ABORT)
719                             ? LockMode.DEFAULT : LockMode.RMW;
720
721         /*
722          * Use the deleted foreign primary key to read the data of this
723          * database, which is the associated primary's key.
724          */

725         DatabaseEntry priKey = new DatabaseEntry();
726         Cursor cursor = null;
727         OperationStatus status;
728         try {
729         cursor = new Cursor(this, locker, null);
730             status = cursor.search(secKey, priKey, lockMode,
731                                    SearchMode.SET);
732             while (status == OperationStatus.SUCCESS) {
733
734                 if (deleteAction == ForeignKeyDeleteAction.ABORT) {
735
736                     /*
737                      * ABORT - throw an exception to cause the user to abort
738                      * the transaction.
739                      */

740                     throw new DatabaseException
741                         ("Secondary " + getDebugName() +
742                          " refers to a foreign key that has been deleted" +
743                          " (ForeignKeyDeleteAction.ABORT)");
744
745                 } else if (deleteAction == ForeignKeyDeleteAction.CASCADE) {
746
747                     /*
748                      * CASCADE - delete the associated primary record.
749                      */

750                     Cursor priCursor = null;
751                     try {
752                         DatabaseEntry data = new DatabaseEntry();
753                         priCursor = new Cursor(primaryDb, locker, null);
754                         status = priCursor.search(priKey, data, LockMode.RMW,
755                                                   SearchMode.SET);
756                         if (status == OperationStatus.SUCCESS) {
757                             priCursor.delete();
758                         } else {
759                             throw secondaryCorruptException();
760                         }
761                     } finally {
762                         if (priCursor != null) {
763                             priCursor.close();
764                         }
765                     }
766
767                 } else if (deleteAction == ForeignKeyDeleteAction.NULLIFY) {
768
769                     /*
770                      * NULLIFY - set the secondary key to null in the
771                      * associated primary record.
772                      */

773                     Cursor priCursor = null;
774                     try {
775                         DatabaseEntry data = new DatabaseEntry();
776                         priCursor = new Cursor(primaryDb, locker, null);
777                         status = priCursor.search(priKey, data, LockMode.RMW,
778                                                   SearchMode.SET);
779                         if (status == OperationStatus.SUCCESS) {
780                             ForeignMultiKeyNullifier multiNullifier =
781                                 secondaryConfig.getForeignMultiKeyNullifier();
782                             if (multiNullifier != null) {
783                                 if (multiNullifier.nullifyForeignKey
784                                         (this, priKey, data, secKey)) {
785                                     priCursor.putCurrent(data);
786                                 }
787                             } else {
788                                 ForeignKeyNullifier nullifier =
789                                     secondaryConfig.getForeignKeyNullifier();
790                                 if (nullifier.nullifyForeignKey
791                                         (this, data)) {
792                                     priCursor.putCurrent(data);
793                                 }
794                             }
795                         } else {
796                             throw secondaryCorruptException();
797                         }
798                     } finally {
799                         if (priCursor != null) {
800                             priCursor.close();
801                         }
802                     }
803                 } else {
804                     /* Should never occur. */
805                     throw new IllegalStateException JavaDoc();
806                 }
807
808                 status = cursor.retrieveNext(secKey, priKey, LockMode.DEFAULT,
809                                              GetMode.NEXT_DUP);
810             }
811         } finally {
812             if (cursor != null) {
813                 cursor.close();
814             }
815         }
816     }
817
818     DatabaseException secondaryCorruptException()
819         throws DatabaseException {
820
821         throw new DatabaseException
822             ("Secondary " + getDebugName() + " is corrupt: it refers" +
823              " to a missing key in the primary database");
824     }
825
826     static UnsupportedOperationException JavaDoc notAllowedException() {
827
828         throw new UnsupportedOperationException JavaDoc
829             ("Operation not allowed on a secondary");
830     }
831
832     /**
833      * Send trace messages to the java.util.logger. Don't rely on the
834      * logger alone to conditionalize whether we send this message,
835      * we don't even want to construct the message if the level is
836      * not enabled.
837      */

838     void trace(Level JavaDoc level,
839                String JavaDoc methodName)
840         throws DatabaseException {
841
842         Logger JavaDoc logger = envHandle.getEnvironmentImpl().getLogger();
843         if (logger.isLoggable(level)) {
844             StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
845             sb.append(methodName);
846             sb.append(" name=").append(getDebugName());
847             sb.append(" primary=").append(primaryDb.getDebugName());
848
849             logger.log(level, sb.toString());
850         }
851     }
852 }
853
Popular Tags