• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/ap/gpl/timemachine/db-4.7.25.NC/test/scr024/src/com/sleepycat/persist/test/
1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 2002,2008 Oracle.  All rights reserved.
5 *
6 * $Id: OperationTest.java,v 1.2 2008/02/12 19:15:26 mark Exp $
7 */
8
9package com.sleepycat.persist.test;
10
11import static com.sleepycat.persist.model.Relationship.MANY_TO_ONE;
12import static com.sleepycat.persist.model.Relationship.ONE_TO_MANY;
13import static com.sleepycat.persist.model.Relationship.ONE_TO_ONE;
14import static com.sleepycat.persist.model.DeleteAction.CASCADE;
15
16import java.util.ArrayList;
17import java.util.HashSet;
18import java.util.List;
19import java.util.Set;
20
21import junit.framework.Test;
22
23import com.sleepycat.db.Database;
24import com.sleepycat.db.DatabaseConfig;
25import com.sleepycat.db.DatabaseException;
26import com.sleepycat.db.Transaction;
27import com.sleepycat.persist.EntityCursor;
28import com.sleepycat.persist.EntityIndex;
29import com.sleepycat.persist.EntityStore;
30import com.sleepycat.persist.PrimaryIndex;
31import com.sleepycat.persist.SecondaryIndex;
32import com.sleepycat.persist.StoreConfig;
33import com.sleepycat.persist.impl.Store;
34import com.sleepycat.persist.model.Entity;
35import com.sleepycat.persist.model.KeyField;
36import com.sleepycat.persist.model.Persistent;
37import com.sleepycat.persist.model.PrimaryKey;
38import com.sleepycat.persist.model.SecondaryKey;
39import com.sleepycat.persist.raw.RawStore;
40import com.sleepycat.util.test.TxnTestCase;
41
42/**
43 * Tests misc store and index operations that are not tested by IndexTest.
44 *
45 * @author Mark Hayes
46 */
47public class OperationTest extends TxnTestCase {
48
49    private static final String STORE_NAME = "test";
50
51    public static Test suite() {
52        return txnTestSuite(OperationTest.class, null, null);
53    }
54
55    private EntityStore store;
56
57    private void openReadOnly()
58        throws DatabaseException {
59
60        StoreConfig config = new StoreConfig();
61        config.setReadOnly(true);
62        open(config);
63    }
64
65    private void open()
66        throws DatabaseException {
67
68        StoreConfig config = new StoreConfig();
69        config.setAllowCreate(envConfig.getAllowCreate());
70        open(config);
71    }
72
73    private void open(StoreConfig config)
74        throws DatabaseException {
75
76        config.setTransactional(envConfig.getTransactional());
77        store = new EntityStore(env, STORE_NAME, config);
78    }
79
80    private void close()
81        throws DatabaseException {
82
83        store.close();
84        store = null;
85    }
86
87    /**
88     * The store must be closed before closing the environment.
89     */
90    public void tearDown()
91        throws Exception {
92
93        try {
94            if (store != null) {
95                store.close();
96            }
97        } catch (Throwable e) {
98            System.out.println("During tearDown: " + e);
99        }
100        store = null;
101        super.tearDown();
102    }
103
104    public void testReadOnly()
105        throws DatabaseException {
106
107        open();
108        PrimaryIndex<Integer,SharedSequenceEntity1> priIndex =
109            store.getPrimaryIndex(Integer.class, SharedSequenceEntity1.class);
110        Transaction txn = txnBegin();
111        SharedSequenceEntity1 e = new SharedSequenceEntity1();
112        priIndex.put(txn, e);
113        assertEquals(1, e.key);
114        txnCommit(txn);
115        close();
116
117        /*
118         * Check that we can open the store read-only and read the records
119         * written above.
120         */
121        openReadOnly();
122        priIndex =
123            store.getPrimaryIndex(Integer.class, SharedSequenceEntity1.class);
124        e = priIndex.get(1);
125        assertNotNull(e);
126        close();
127    }
128
129
130    public void testUninitializedCursor()
131        throws DatabaseException {
132
133        open();
134
135        PrimaryIndex<Integer,MyEntity> priIndex =
136            store.getPrimaryIndex(Integer.class, MyEntity.class);
137
138        Transaction txn = txnBeginCursor();
139
140        MyEntity e = new MyEntity();
141        e.priKey = 1;
142        e.secKey = 1;
143        priIndex.put(txn, e);
144
145        EntityCursor<MyEntity> entities = priIndex.entities(txn, null);
146        try {
147            entities.nextDup();
148            fail();
149        } catch (IllegalStateException expected) {}
150        try {
151            entities.prevDup();
152            fail();
153        } catch (IllegalStateException expected) {}
154        try {
155            entities.current();
156            fail();
157        } catch (IllegalStateException expected) {}
158        try {
159            entities.delete();
160            fail();
161        } catch (IllegalStateException expected) {}
162        try {
163            entities.update(e);
164            fail();
165        } catch (IllegalStateException expected) {}
166        try {
167            entities.count();
168            fail();
169        } catch (IllegalStateException expected) {}
170
171        entities.close();
172        txnCommit(txn);
173        close();
174    }
175
176    public void testCursorCount()
177        throws DatabaseException {
178
179        open();
180
181        PrimaryIndex<Integer,MyEntity> priIndex =
182            store.getPrimaryIndex(Integer.class, MyEntity.class);
183
184        SecondaryIndex<Integer,Integer,MyEntity> secIndex =
185            store.getSecondaryIndex(priIndex, Integer.class, "secKey");
186
187        Transaction txn = txnBeginCursor();
188
189        MyEntity e = new MyEntity();
190        e.priKey = 1;
191        e.secKey = 1;
192        priIndex.put(txn, e);
193
194        EntityCursor<MyEntity> cursor = secIndex.entities(txn, null);
195        cursor.next();
196        assertEquals(1, cursor.count());
197        cursor.close();
198
199        e.priKey = 2;
200        priIndex.put(txn, e);
201        cursor = secIndex.entities(txn, null);
202        cursor.next();
203        assertEquals(2, cursor.count());
204        cursor.close();
205
206        txnCommit(txn);
207        close();
208    }
209
210    public void testCursorUpdate()
211        throws DatabaseException {
212
213        open();
214
215        PrimaryIndex<Integer,MyEntity> priIndex =
216            store.getPrimaryIndex(Integer.class, MyEntity.class);
217
218        SecondaryIndex<Integer,Integer,MyEntity> secIndex =
219            store.getSecondaryIndex(priIndex, Integer.class, "secKey");
220
221        Transaction txn = txnBeginCursor();
222
223        Integer k;
224        MyEntity e = new MyEntity();
225        e.priKey = 1;
226        e.secKey = 2;
227        priIndex.put(txn, e);
228
229        /* update() with primary entity cursor. */
230        EntityCursor<MyEntity> entities = priIndex.entities(txn, null);
231        e = entities.next();
232        assertNotNull(e);
233        assertEquals(1, e.priKey);
234        assertEquals(Integer.valueOf(2), e.secKey);
235        e.secKey = null;
236        assertTrue(entities.update(e));
237        e = entities.current();
238        assertNotNull(e);
239        assertEquals(1, e.priKey);
240        assertEquals(null, e.secKey);
241        e.secKey = 3;
242        assertTrue(entities.update(e));
243        e = entities.current();
244        assertNotNull(e);
245        assertEquals(1, e.priKey);
246        assertEquals(Integer.valueOf(3), e.secKey);
247        entities.close();
248
249        /* update() with primary keys cursor. */
250        EntityCursor<Integer> keys = priIndex.keys(txn, null);
251        k = keys.next();
252        assertNotNull(k);
253        assertEquals(Integer.valueOf(1), k);
254        try {
255            keys.update(2);
256            fail();
257        } catch (UnsupportedOperationException expected) {
258        }
259        keys.close();
260
261        /* update() with secondary entity cursor. */
262        entities = secIndex.entities(txn, null);
263        e = entities.next();
264        assertNotNull(e);
265        assertEquals(1, e.priKey);
266        assertEquals(Integer.valueOf(3), e.secKey);
267        try {
268            entities.update(e);
269            fail();
270        } catch (UnsupportedOperationException expected) {
271        } catch (IllegalArgumentException expectedForDbCore) {
272        }
273        entities.close();
274
275        /* update() with secondary keys cursor. */
276        keys = secIndex.keys(txn, null);
277        k = keys.next();
278        assertNotNull(k);
279        assertEquals(Integer.valueOf(3), k);
280        try {
281            keys.update(k);
282            fail();
283        } catch (UnsupportedOperationException expected) {
284        }
285        keys.close();
286
287        txnCommit(txn);
288        close();
289    }
290
291    public void testCursorDelete()
292        throws DatabaseException {
293
294        open();
295
296        PrimaryIndex<Integer,MyEntity> priIndex =
297            store.getPrimaryIndex(Integer.class, MyEntity.class);
298
299        SecondaryIndex<Integer,Integer,MyEntity> secIndex =
300            store.getSecondaryIndex(priIndex, Integer.class, "secKey");
301
302        Transaction txn = txnBeginCursor();
303
304        /* delete() with primary and secondary entities cursor. */
305
306        for (EntityIndex index : new EntityIndex[] { priIndex, secIndex }) {
307
308            MyEntity e = new MyEntity();
309            e.priKey = 1;
310            e.secKey = 1;
311            priIndex.put(txn, e);
312            e.priKey = 2;
313            priIndex.put(txn, e);
314
315            EntityCursor<MyEntity> cursor = index.entities(txn, null);
316
317            e = cursor.next();
318            assertNotNull(e);
319            assertEquals(1, e.priKey);
320            e = cursor.current();
321            assertNotNull(e);
322            assertEquals(1, e.priKey);
323            assertTrue(cursor.delete());
324            assertTrue(!cursor.delete());
325            assertNull(cursor.current());
326
327            e = cursor.next();
328            assertNotNull(e);
329            assertEquals(2, e.priKey);
330            e = cursor.current();
331            assertNotNull(e);
332            assertEquals(2, e.priKey);
333            assertTrue(cursor.delete());
334            assertTrue(!cursor.delete());
335            assertNull(cursor.current());
336
337            e = cursor.next();
338            assertNull(e);
339
340            if (index == priIndex) {
341                e = new MyEntity();
342                e.priKey = 2;
343                e.secKey = 1;
344                assertTrue(!cursor.update(e));
345            }
346
347            cursor.close();
348        }
349
350        /* delete() with primary and secondary keys cursor. */
351
352        for (EntityIndex index : new EntityIndex[] { priIndex, secIndex }) {
353
354            MyEntity e = new MyEntity();
355            e.priKey = 1;
356            e.secKey = 1;
357            priIndex.put(txn, e);
358            e.priKey = 2;
359            priIndex.put(txn, e);
360
361            EntityCursor<Integer> cursor = index.keys(txn, null);
362
363            Integer k = cursor.next();
364            assertNotNull(k);
365            assertEquals(1, k.intValue());
366            k = cursor.current();
367            assertNotNull(k);
368            assertEquals(1, k.intValue());
369            assertTrue(cursor.delete());
370            assertTrue(!cursor.delete());
371            assertNull(cursor.current());
372
373            int expectKey = (index == priIndex) ? 2 : 1;
374            k = cursor.next();
375            assertNotNull(k);
376            assertEquals(expectKey, k.intValue());
377            k = cursor.current();
378            assertNotNull(k);
379            assertEquals(expectKey, k.intValue());
380            assertTrue(cursor.delete());
381            assertTrue(!cursor.delete());
382            assertNull(cursor.current());
383
384            k = cursor.next();
385            assertNull(k);
386
387            cursor.close();
388        }
389
390        txnCommit(txn);
391        close();
392    }
393
394    public void testDeleteFromSubIndex()
395        throws DatabaseException {
396
397        open();
398
399        PrimaryIndex<Integer,MyEntity> priIndex =
400            store.getPrimaryIndex(Integer.class, MyEntity.class);
401
402        SecondaryIndex<Integer,Integer,MyEntity> secIndex =
403            store.getSecondaryIndex(priIndex, Integer.class, "secKey");
404
405        Transaction txn = txnBegin();
406        MyEntity e = new MyEntity();
407        e.secKey = 1;
408        e.priKey = 1;
409        priIndex.put(txn, e);
410        e.priKey = 2;
411        priIndex.put(txn, e);
412        e.priKey = 3;
413        priIndex.put(txn, e);
414        e.priKey = 4;
415        priIndex.put(txn, e);
416        txnCommit(txn);
417
418        EntityIndex<Integer,MyEntity> subIndex = secIndex.subIndex(1);
419        txn = txnBeginCursor();
420        e = subIndex.get(txn, 1, null);
421        assertEquals(1, e.priKey);
422        assertEquals(Integer.valueOf(1), e.secKey);
423        e = subIndex.get(txn, 2, null);
424        assertEquals(2, e.priKey);
425        assertEquals(Integer.valueOf(1), e.secKey);
426        e = subIndex.get(txn, 3, null);
427        assertEquals(3, e.priKey);
428        assertEquals(Integer.valueOf(1), e.secKey);
429        e = subIndex.get(txn, 5, null);
430        assertNull(e);
431
432        boolean deleted = subIndex.delete(txn, 1);
433        assertTrue(deleted);
434        assertNull(subIndex.get(txn, 1, null));
435        assertNotNull(subIndex.get(txn, 2, null));
436
437        EntityCursor<MyEntity> cursor = subIndex.entities(txn, null);
438        boolean saw4 = false;
439        for (MyEntity e2 = cursor.first(); e2 != null; e2 = cursor.next()) {
440            if (e2.priKey == 3) {
441                cursor.delete();
442            }
443            if (e2.priKey == 4) {
444                saw4 = true;
445            }
446        }
447        cursor.close();
448        assertTrue(saw4);
449        assertNull(subIndex.get(txn, 1, null));
450        assertNull(subIndex.get(txn, 3, null));
451        assertNotNull(subIndex.get(txn, 2, null));
452        assertNotNull(subIndex.get(txn, 4, null));
453
454        txnCommit(txn);
455        close();
456    }
457
458    @Entity
459    static class MyEntity {
460
461        @PrimaryKey
462        private int priKey;
463
464        @SecondaryKey(relate=MANY_TO_ONE)
465        private Integer secKey;
466
467        private MyEntity() {}
468    }
469
470    public void testSharedSequence()
471        throws DatabaseException {
472
473        open();
474
475        PrimaryIndex<Integer,SharedSequenceEntity1> priIndex1 =
476            store.getPrimaryIndex(Integer.class, SharedSequenceEntity1.class);
477
478        PrimaryIndex<Integer,SharedSequenceEntity2> priIndex2 =
479            store.getPrimaryIndex(Integer.class, SharedSequenceEntity2.class);
480
481        Transaction txn = txnBegin();
482        SharedSequenceEntity1 e1 = new SharedSequenceEntity1();
483        SharedSequenceEntity2 e2 = new SharedSequenceEntity2();
484        priIndex1.put(txn, e1);
485        assertEquals(1, e1.key);
486        priIndex2.putNoOverwrite(txn, e2);
487        assertEquals(Integer.valueOf(2), e2.key);
488        e1.key = 0;
489        priIndex1.putNoOverwrite(txn, e1);
490        assertEquals(3, e1.key);
491        e2.key = null;
492        priIndex2.put(txn, e2);
493        assertEquals(Integer.valueOf(4), e2.key);
494        txnCommit(txn);
495
496        close();
497    }
498
499    @Entity
500    static class SharedSequenceEntity1 {
501
502        @PrimaryKey(sequence="shared")
503        private int key;
504    }
505
506    @Entity
507    static class SharedSequenceEntity2 {
508
509        @PrimaryKey(sequence="shared")
510        private Integer key;
511    }
512
513    public void testSeparateSequence()
514        throws DatabaseException {
515
516        open();
517
518        PrimaryIndex<Integer,SeparateSequenceEntity1> priIndex1 =
519            store.getPrimaryIndex
520                (Integer.class, SeparateSequenceEntity1.class);
521
522        PrimaryIndex<Integer,SeparateSequenceEntity2> priIndex2 =
523            store.getPrimaryIndex
524                (Integer.class, SeparateSequenceEntity2.class);
525
526        Transaction txn = txnBegin();
527        SeparateSequenceEntity1 e1 = new SeparateSequenceEntity1();
528        SeparateSequenceEntity2 e2 = new SeparateSequenceEntity2();
529        priIndex1.put(txn, e1);
530        assertEquals(1, e1.key);
531        priIndex2.putNoOverwrite(txn, e2);
532        assertEquals(Integer.valueOf(1), e2.key);
533        e1.key = 0;
534        priIndex1.putNoOverwrite(txn, e1);
535        assertEquals(2, e1.key);
536        e2.key = null;
537        priIndex2.put(txn, e2);
538        assertEquals(Integer.valueOf(2), e2.key);
539        txnCommit(txn);
540
541        close();
542    }
543
544    @Entity
545    static class SeparateSequenceEntity1 {
546
547        @PrimaryKey(sequence="seq1")
548        private int key;
549    }
550
551    @Entity
552    static class SeparateSequenceEntity2 {
553
554        @PrimaryKey(sequence="seq2")
555        private Integer key;
556    }
557
558    public void testCompositeSequence()
559        throws DatabaseException {
560
561        open();
562
563        PrimaryIndex<CompositeSequenceEntity1.Key,CompositeSequenceEntity1>
564            priIndex1 =
565            store.getPrimaryIndex
566                (CompositeSequenceEntity1.Key.class,
567                 CompositeSequenceEntity1.class);
568
569        PrimaryIndex<CompositeSequenceEntity2.Key,CompositeSequenceEntity2>
570            priIndex2 =
571            store.getPrimaryIndex
572                (CompositeSequenceEntity2.Key.class,
573                 CompositeSequenceEntity2.class);
574
575        Transaction txn = txnBegin();
576        CompositeSequenceEntity1 e1 = new CompositeSequenceEntity1();
577        CompositeSequenceEntity2 e2 = new CompositeSequenceEntity2();
578        priIndex1.put(txn, e1);
579        assertEquals(1, e1.key.key);
580        priIndex2.putNoOverwrite(txn, e2);
581        assertEquals(Integer.valueOf(1), e2.key.key);
582        e1.key = null;
583        priIndex1.putNoOverwrite(txn, e1);
584        assertEquals(2, e1.key.key);
585        e2.key = null;
586        priIndex2.put(txn, e2);
587        assertEquals(Integer.valueOf(2), e2.key.key);
588        txnCommit(txn);
589
590        EntityCursor<CompositeSequenceEntity1> c1 = priIndex1.entities();
591        e1 = c1.next();
592        assertEquals(2, e1.key.key);
593        e1 = c1.next();
594        assertEquals(1, e1.key.key);
595        e1 = c1.next();
596        assertNull(e1);
597        c1.close();
598
599        EntityCursor<CompositeSequenceEntity2> c2 = priIndex2.entities();
600        e2 = c2.next();
601        assertEquals(Integer.valueOf(2), e2.key.key);
602        e2 = c2.next();
603        assertEquals(Integer.valueOf(1), e2.key.key);
604        e2 = c2.next();
605        assertNull(e2);
606        c2.close();
607
608        close();
609    }
610
611    @Entity
612    static class CompositeSequenceEntity1 {
613
614        @Persistent
615        static class Key implements Comparable<Key> {
616
617            @KeyField(1)
618            private int key;
619
620            public int compareTo(Key o) {
621                /* Reverse the natural order. */
622                return o.key - key;
623            }
624        }
625
626        @PrimaryKey(sequence="seq1")
627        private Key key;
628    }
629
630    @Entity
631    static class CompositeSequenceEntity2 {
632
633        @Persistent
634        static class Key implements Comparable<Key> {
635
636            @KeyField(1)
637            private Integer key;
638
639            public int compareTo(Key o) {
640                /* Reverse the natural order. */
641                return o.key - key;
642            }
643        }
644
645        @PrimaryKey(sequence="seq2")
646        private Key key;
647    }
648
649    /**
650     * When opening read-only, secondaries are not opened when the primary is
651     * opened, causing a different code path to be used for opening
652     * secondaries.  For a RawStore in particular, this caused an unreported
653     * NullPointerException in JE 3.0.12.  No SR was created because the use
654     * case is very obscure and was discovered by code inspection.
655     */
656    public void testOpenRawStoreReadOnly()
657        throws DatabaseException {
658
659        open();
660        store.getPrimaryIndex(Integer.class, MyEntity.class);
661        close();
662
663        StoreConfig config = new StoreConfig();
664        config.setReadOnly(true);
665        config.setTransactional(envConfig.getTransactional());
666        RawStore rawStore = new RawStore(env, "test", config);
667
668        String clsName = MyEntity.class.getName();
669        rawStore.getSecondaryIndex(clsName, "secKey");
670
671        rawStore.close();
672    }
673
674    /**
675     * When opening an X_TO_MANY secondary that has a persistent key class, the
676     * key class was not recognized as being persistent if it was never before
677     * referenced when getSecondaryIndex was called.  This was a bug in JE
678     * 3.0.12, reported on OTN.  [#15103]
679     */
680    public void testToManyKeyClass()
681        throws DatabaseException {
682
683        open();
684
685        PrimaryIndex<Integer,ToManyKeyEntity> priIndex =
686            store.getPrimaryIndex(Integer.class, ToManyKeyEntity.class);
687        SecondaryIndex<ToManyKey,Integer,ToManyKeyEntity> secIndex =
688            store.getSecondaryIndex(priIndex, ToManyKey.class, "key2");
689
690        priIndex.put(new ToManyKeyEntity());
691        secIndex.get(new ToManyKey());
692
693        close();
694    }
695
696    /**
697     * Test a fix for a bug where opening a TO_MANY secondary index would fail
698     * fail with "IllegalArgumentException: Wrong secondary key class: ..."
699     * when the store was opened read-only.  [#15156]
700     */
701    public void testToManyReadOnly()
702        throws DatabaseException {
703
704        open();
705        PrimaryIndex<Integer,ToManyKeyEntity> priIndex =
706            store.getPrimaryIndex(Integer.class, ToManyKeyEntity.class);
707        priIndex.put(new ToManyKeyEntity());
708        close();
709
710        openReadOnly();
711        priIndex = store.getPrimaryIndex(Integer.class, ToManyKeyEntity.class);
712        SecondaryIndex<ToManyKey,Integer,ToManyKeyEntity> secIndex =
713            store.getSecondaryIndex(priIndex, ToManyKey.class, "key2");
714        secIndex.get(new ToManyKey());
715        close();
716    }
717
718    @Persistent
719    static class ToManyKey {
720
721        @KeyField(1)
722        int value = 99;
723    }
724
725    @Entity
726    static class ToManyKeyEntity {
727
728        @PrimaryKey
729        int key = 88;
730
731        @SecondaryKey(relate=ONE_TO_MANY)
732        Set<ToManyKey> key2;
733
734        ToManyKeyEntity() {
735            key2 = new HashSet<ToManyKey>();
736            key2.add(new ToManyKey());
737        }
738    }
739
740
741    /**
742     * When Y is opened and X has a key with relatedEntity=Y.class, X should
743     * be opened automatically.  If X is not opened, foreign key constraints
744     * will not be enforced. [#15358]
745     */
746    public void testAutoOpenRelatedEntity()
747        throws DatabaseException {
748
749        PrimaryIndex<Integer,RelatedY> priY;
750        PrimaryIndex<Integer,RelatedX> priX;
751
752        /* Opening X should create (and open) Y and enforce constraints. */
753        open();
754        priX = store.getPrimaryIndex(Integer.class, RelatedX.class);
755        PersistTestUtils.assertDbExists
756            (true, env, STORE_NAME, RelatedY.class.getName(), null);
757        try {
758            priX.put(new RelatedX());
759            fail();
760        } catch (DatabaseException e) {
761            assertTrue
762                ("" + e.getMessage(), (e.getMessage().indexOf
763                  ("foreign key not allowed: it is not present") >= 0) ||
764                 (e.getMessage().indexOf("DB_FOREIGN_CONFLICT") >= 0));
765        }
766        priY = store.getPrimaryIndex(Integer.class, RelatedY.class);
767        priY.put(new RelatedY());
768        priX.put(new RelatedX());
769        close();
770
771        /* Delete should cascade even when X is not opened explicitly. */
772        open();
773        priY = store.getPrimaryIndex(Integer.class, RelatedY.class);
774        assertEquals(1, priY.count());
775        priY.delete(88);
776        assertEquals(0, priY.count());
777        priX = store.getPrimaryIndex(Integer.class, RelatedX.class);
778        assertEquals(0, priX.count()); /* Failed prior to [#15358] fix. */
779        close();
780    }
781
782    @Entity
783    static class RelatedX {
784
785        @PrimaryKey
786        int key = 99;
787
788        @SecondaryKey(relate=ONE_TO_ONE,
789                      relatedEntity=RelatedY.class,
790                      onRelatedEntityDelete=CASCADE)
791        int key2 = 88;
792
793        RelatedX() {
794        }
795    }
796
797    @Entity
798    static class RelatedY {
799
800        @PrimaryKey
801        int key = 88;
802
803        RelatedY() {
804        }
805    }
806
807    public void testSecondaryBulkLoad1()
808        throws DatabaseException {
809
810        doSecondaryBulkLoad(true);
811    }
812
813    public void testSecondaryBulkLoad2()
814        throws DatabaseException {
815
816        doSecondaryBulkLoad(false);
817    }
818
819    private void doSecondaryBulkLoad(boolean closeAndOpenNormally)
820        throws DatabaseException {
821
822        PrimaryIndex<Integer,RelatedX> priX;
823        PrimaryIndex<Integer,RelatedY> priY;
824        SecondaryIndex<Integer,Integer,RelatedX> secX;
825
826        /* Open priX with SecondaryBulkLoad=true. */
827        StoreConfig config = new StoreConfig();
828        config.setAllowCreate(true);
829        config.setSecondaryBulkLoad(true);
830        open(config);
831
832        /* Getting priX should not create the secondary index. */
833        priX = store.getPrimaryIndex(Integer.class, RelatedX.class);
834        PersistTestUtils.assertDbExists
835            (false, env, STORE_NAME, RelatedX.class.getName(), "key2");
836
837        /* We can put records that violate the secondary key constraint. */
838        priX.put(new RelatedX());
839
840        if (closeAndOpenNormally) {
841            /* Open normally and the secondary will be populated. */
842            close();
843            open();
844            try {
845                /* Before adding the foreign key, constraint is violated. */
846                priX = store.getPrimaryIndex(Integer.class, RelatedX.class);
847            } catch (DatabaseException e) {
848                assertTrue(e.toString(),
849                           e.toString().contains("foreign key not allowed"));
850            }
851            /* Add the foreign key to avoid the constraint error. */
852            priY = store.getPrimaryIndex(Integer.class, RelatedY.class);
853            priY.put(new RelatedY());
854            priX = store.getPrimaryIndex(Integer.class, RelatedX.class);
855            PersistTestUtils.assertDbExists
856                (true, env, STORE_NAME, RelatedX.class.getName(), "key2");
857            secX = store.getSecondaryIndex(priX, Integer.class, "key2");
858        } else {
859            /* Get secondary index explicitly and it will be populated. */
860            try {
861                /* Before adding the foreign key, constraint is violated. */
862                secX = store.getSecondaryIndex(priX, Integer.class, "key2");
863            } catch (DatabaseException e) {
864                assertTrue(e.toString(),
865                           e.toString().contains("foreign key not allowed"));
866            }
867            /* Add the foreign key. */
868            priY = store.getPrimaryIndex(Integer.class, RelatedY.class);
869            priY.put(new RelatedY());
870            secX = store.getSecondaryIndex(priX, Integer.class, "key2");
871            PersistTestUtils.assertDbExists
872                (true, env, STORE_NAME, RelatedX.class.getName(), "key2");
873        }
874
875        RelatedX x = secX.get(88);
876        assertNotNull(x);
877        close();
878    }
879}
880