KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sleepycat > je > test > ForeignKeyTest


1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2002,2006 Oracle. All rights reserved.
5  *
6  * $Id: ForeignKeyTest.java,v 1.13 2006/10/30 21:14:50 bostic Exp $
7  */

8
9 package com.sleepycat.je.test;
10
11 import junit.framework.Test;
12
13 import com.sleepycat.je.Database;
14 import com.sleepycat.je.DatabaseConfig;
15 import com.sleepycat.je.DatabaseEntry;
16 import com.sleepycat.je.DatabaseException;
17 import com.sleepycat.je.ForeignKeyDeleteAction;
18 import com.sleepycat.je.ForeignKeyNullifier;
19 import com.sleepycat.je.ForeignMultiKeyNullifier;
20 import com.sleepycat.je.LockMode;
21 import com.sleepycat.je.OperationStatus;
22 import com.sleepycat.je.SecondaryConfig;
23 import com.sleepycat.je.SecondaryCursor;
24 import com.sleepycat.je.SecondaryDatabase;
25 import com.sleepycat.je.SecondaryKeyCreator;
26 import com.sleepycat.je.Transaction;
27 import com.sleepycat.je.util.TestUtils;
28
29 public class ForeignKeyTest extends MultiKeyTxnTestCase {
30
31     public static Test suite() {
32         return multiKeyTxnTestSuite(ForeignKeyTest.class, null, null);
33     }
34
35     public void testDupsNotAllowed()
36         throws DatabaseException {
37
38         Database priDb1 = openPrimary("pri1");
39         Database priDb2 = openPrimary("pri2", true /*duplicates*/);
40
41         try {
42             openSecondary(priDb1, "sec2", priDb2, ForeignKeyDeleteAction.ABORT);
43             fail();
44         } catch (IllegalArgumentException JavaDoc expected) {
45             String JavaDoc msg = expected.getMessage();
46             assertTrue
47                 (msg, msg.indexOf("Duplicates must not be allowed") >= 0);
48         }
49
50         priDb1.close();
51         priDb2.close();
52     }
53
54     public void testIllegalNullifier()
55         throws DatabaseException {
56
57         Database priDb1 = openPrimary("pri1");
58         Transaction txn = txnBegin();
59         MyKeyCreator myCreator = new MyKeyCreator();
60         SecondaryConfig config;
61
62         /* A nullifier is required with NULLIFY. */
63         config = new SecondaryConfig();
64         config.setForeignKeyDeleteAction(ForeignKeyDeleteAction.NULLIFY);
65         config.setKeyCreator(myCreator);
66         try {
67             env.openSecondaryDatabase(txn, "sec1", priDb1, config);
68             fail();
69         } catch (NullPointerException JavaDoc expected) { }
70
71         /* Both nullifiers are not allowed. */
72         config = new SecondaryConfig();
73         config.setForeignKeyDeleteAction(ForeignKeyDeleteAction.NULLIFY);
74         config.setKeyCreator(myCreator);
75         config.setForeignKeyNullifier(myCreator);
76         config.setForeignMultiKeyNullifier(myCreator);
77         try {
78             env.openSecondaryDatabase(txn, "sec1", priDb1, config);
79             fail();
80         } catch (IllegalArgumentException JavaDoc expected) { }
81
82         /* ForeignKeyNullifier is not allowed with MultiKeyCreator. */
83         config = new SecondaryConfig();
84         config.setForeignKeyDeleteAction(ForeignKeyDeleteAction.NULLIFY);
85         config.setMultiKeyCreator(new SimpleMultiKeyCreator(myCreator));
86         config.setForeignKeyNullifier(myCreator);
87         try {
88             env.openSecondaryDatabase(txn, "sec1", priDb1, config);
89             fail();
90         } catch (IllegalArgumentException JavaDoc expected) { }
91
92         txnCommit(txn);
93         priDb1.close();
94     }
95
96     public void testAbort()
97         throws DatabaseException {
98
99         doTest(ForeignKeyDeleteAction.ABORT);
100     }
101
102     public void testCascade()
103         throws DatabaseException {
104
105         doTest(ForeignKeyDeleteAction.CASCADE);
106     }
107
108     public void testNullify()
109         throws DatabaseException {
110
111         doTest(ForeignKeyDeleteAction.NULLIFY);
112     }
113
114     private void doTest(ForeignKeyDeleteAction onDelete)
115         throws DatabaseException {
116
117         Database priDb1 = openPrimary("pri1");
118         Database priDb2 = openPrimary("pri2");
119
120         SecondaryDatabase secDb1 = openSecondary(priDb1, "sec1", null, null);
121         SecondaryDatabase secDb2 = openSecondary(priDb2, "sec2", priDb1,
122                                                  onDelete);
123
124         OperationStatus status;
125         DatabaseEntry data = new DatabaseEntry();
126         DatabaseEntry key = new DatabaseEntry();
127         DatabaseEntry pkey = new DatabaseEntry();
128         Transaction txn = txnBegin();
129
130         /*
131          * pri1 has a record with primary key 1 and index key 3.
132          * pri2 has a record with primary key 2 and foreign key 1,
133          * which is the primary key of pri1.
134          * pri2 has another record with primary key 3 and foreign key 1,
135          * to enable testing cascade and nullify for secondary duplicates.
136          */

137
138         /* Add three records. */
139
140         status = priDb1.put(txn, entry(1), entry(3));
141         assertEquals(OperationStatus.SUCCESS, status);
142
143         status = priDb2.put(txn, entry(2), entry(1));
144         assertEquals(OperationStatus.SUCCESS, status);
145
146         status = priDb2.put(txn, entry(3), entry(1));
147         assertEquals(OperationStatus.SUCCESS, status);
148
149         /* Verify record data. */
150
151         status = priDb1.get(txn, entry(1), data, LockMode.DEFAULT);
152         assertEquals(OperationStatus.SUCCESS, status);
153         assertEquals(3, val(data));
154
155         status = secDb1.get(txn, entry(3), data, LockMode.DEFAULT);
156         assertEquals(OperationStatus.SUCCESS, status);
157         assertEquals(3, val(data));
158
159         status = priDb2.get(txn, entry(2), data, LockMode.DEFAULT);
160         assertEquals(OperationStatus.SUCCESS, status);
161         assertEquals(1, val(data));
162
163         status = priDb2.get(txn, entry(3), data, LockMode.DEFAULT);
164         assertEquals(OperationStatus.SUCCESS, status);
165         assertEquals(1, val(data));
166
167         SecondaryCursor cursor = secDb2.openSecondaryCursor(txn, null);
168         status = cursor.getFirst(key, pkey, data, LockMode.DEFAULT);
169         assertEquals(OperationStatus.SUCCESS, status);
170         assertEquals(1, val(key));
171         assertEquals(2, val(pkey));
172         assertEquals(1, val(data));
173         status = cursor.getNext(key, pkey, data, LockMode.DEFAULT);
174         assertEquals(OperationStatus.SUCCESS, status);
175         assertEquals(1, val(key));
176         assertEquals(3, val(pkey));
177         assertEquals(1, val(data));
178         status = cursor.getNext(key, pkey, data, LockMode.DEFAULT);
179         assertEquals(OperationStatus.NOTFOUND, status);
180         cursor.close();
181
182         txnCommit(txn);
183         txn = txnBegin();
184
185         /* Test delete action. */
186
187         if (onDelete == ForeignKeyDeleteAction.ABORT) {
188
189             /* Test that we abort trying to delete a referenced key. */
190
191             try {
192                 status = priDb1.delete(txn, entry(1));
193                 fail();
194             } catch (DatabaseException expected) {
195                 txnAbort(txn);
196                 txn = txnBegin();
197             }
198
199             /* Test that we can put a record into pri2 with a null foreign key
200              * value. */

201
202             status = priDb2.put(txn, entry(2), entry(0));
203             assertEquals(OperationStatus.SUCCESS, status);
204
205             status = priDb2.put(txn, entry(3), entry(0));
206             assertEquals(OperationStatus.SUCCESS, status);
207
208             /* The sec2 records should not be present since the key was set
209              * to null above. */

210
211             status = secDb2.get(txn, entry(1), data, LockMode.DEFAULT);
212             assertEquals(OperationStatus.NOTFOUND, status);
213
214             /* Test that now we can delete the record in pri1, since it is no
215              * longer referenced. */

216
217             status = priDb1.delete(txn, entry(1));
218             assertEquals(OperationStatus.SUCCESS, status);
219
220             status = priDb1.get(txn, entry(1), data, LockMode.DEFAULT);
221             assertEquals(OperationStatus.NOTFOUND, status);
222
223             status = secDb1.get(txn, entry(3), data, LockMode.DEFAULT);
224             assertEquals(OperationStatus.NOTFOUND, status);
225
226         } else if (onDelete == ForeignKeyDeleteAction.NULLIFY) {
227
228             /* Delete the referenced key. */
229
230             status = priDb1.delete(txn, entry(1));
231             assertEquals(OperationStatus.SUCCESS, status);
232
233             status = priDb1.get(txn, entry(1), data, LockMode.DEFAULT);
234             assertEquals(OperationStatus.NOTFOUND, status);
235
236             status = secDb1.get(txn, entry(3), data, LockMode.DEFAULT);
237             assertEquals(OperationStatus.NOTFOUND, status);
238
239             /* The pri2 records should still exist, but should have a zero/null
240              * secondary key since it was nullified. */

241
242             status = priDb2.get(txn, entry(2), data, LockMode.DEFAULT);
243             assertEquals(OperationStatus.SUCCESS, status);
244             assertEquals(0, val(data));
245
246             status = priDb2.get(txn, entry(3), data, LockMode.DEFAULT);
247             assertEquals(OperationStatus.SUCCESS, status);
248             assertEquals(0, val(data));
249             
250             status = secDb2.get(txn, entry(1), data, LockMode.DEFAULT);
251             assertEquals(OperationStatus.NOTFOUND, status);
252
253         } else if (onDelete == ForeignKeyDeleteAction.CASCADE) {
254
255             /* Delete the referenced key. */
256
257             status = priDb1.delete(txn, entry(1));
258             assertEquals(OperationStatus.SUCCESS, status);
259
260             status = priDb1.get(txn, entry(1), data, LockMode.DEFAULT);
261             assertEquals(OperationStatus.NOTFOUND, status);
262
263             status = secDb1.get(txn, entry(3), data, LockMode.DEFAULT);
264             assertEquals(OperationStatus.NOTFOUND, status);
265
266             /* The pri2 records should have deleted also. */
267
268             status = priDb2.get(txn, entry(2), data, LockMode.DEFAULT);
269             assertEquals(OperationStatus.NOTFOUND, status);
270
271             status = priDb2.get(txn, entry(3), data, LockMode.DEFAULT);
272             assertEquals(OperationStatus.NOTFOUND, status);
273             
274             status = secDb2.get(txn, entry(1), data, LockMode.DEFAULT);
275             assertEquals(OperationStatus.NOTFOUND, status);
276
277         } else {
278             throw new IllegalStateException JavaDoc();
279         }
280
281         /*
282          * Test that a foreign key value may not be used that is not present
283          * in the foreign db. Key 2 is not in pri1 in this case.
284          */

285         try {
286             status = priDb2.put(txn, entry(3), entry(2));
287             fail();
288         } catch (DatabaseException expected) { }
289
290         txnCommit(txn);
291         secDb1.close();
292         secDb2.close();
293         priDb1.close();
294         priDb2.close();
295     }
296
297     private Database openPrimary(String JavaDoc name)
298         throws DatabaseException {
299
300         return openPrimary(name, false);
301     }
302
303     private Database openPrimary(String JavaDoc name, boolean duplicates)
304         throws DatabaseException {
305
306         DatabaseConfig dbConfig = new DatabaseConfig();
307         dbConfig.setTransactional(isTransactional);
308         dbConfig.setAllowCreate(true);
309         dbConfig.setSortedDuplicates(duplicates);
310
311         Transaction txn = txnBegin();
312         try {
313             return env.openDatabase(txn, name, dbConfig);
314         } finally {
315             txnCommit(txn);
316         }
317     }
318
319     private SecondaryDatabase openSecondary(Database priDb, String JavaDoc dbName,
320                                             Database foreignDb,
321                                             ForeignKeyDeleteAction onDelete)
322         throws DatabaseException {
323
324         SecondaryConfig dbConfig = new SecondaryConfig();
325         dbConfig.setTransactional(isTransactional);
326         dbConfig.setAllowCreate(true);
327         dbConfig.setSortedDuplicates(true);
328
329         MyKeyCreator keyCreator = new MyKeyCreator();
330         if (useMultiKey) {
331             dbConfig.setMultiKeyCreator(new SimpleMultiKeyCreator(keyCreator));
332         } else {
333             dbConfig.setKeyCreator(keyCreator);
334         }
335
336         if (foreignDb != null) {
337
338             if (useMultiKey) {
339                 dbConfig.setForeignMultiKeyNullifier(keyCreator);
340             } else {
341                 dbConfig.setForeignKeyNullifier(keyCreator);
342             }
343             dbConfig.setForeignKeyDatabase(foreignDb);
344             dbConfig.setForeignKeyDeleteAction(onDelete);
345         }
346
347         Transaction txn = txnBegin();
348         try {
349             return env.openSecondaryDatabase(txn, dbName, priDb, dbConfig);
350         } finally {
351             txnCommit(txn);
352         }
353     }
354
355     static private DatabaseEntry entry(int val) {
356
357         return new DatabaseEntry(TestUtils.getTestArray(val));
358     }
359
360     static private int val(DatabaseEntry entry) {
361
362         return TestUtils.getTestVal(entry.getData());
363     }
364
365     private class MyKeyCreator implements SecondaryKeyCreator,
366                                           ForeignMultiKeyNullifier,
367                                           ForeignKeyNullifier {
368
369         /* SecondaryKeyCreator */
370         public boolean createSecondaryKey(SecondaryDatabase secondary,
371                                           DatabaseEntry key,
372                                           DatabaseEntry data,
373                                           DatabaseEntry result)
374             throws DatabaseException {
375
376             int val = val(data);
377             if (val != 0) {
378                 result.setData(TestUtils.getTestArray(val));
379                 return true;
380             } else {
381                 return false;
382             }
383         }
384
385         /* ForeignMultiKeyNullifier */
386         public boolean nullifyForeignKey(SecondaryDatabase secondary,
387                                          DatabaseEntry key,
388                                          DatabaseEntry data,
389                                          DatabaseEntry secKey)
390             throws DatabaseException {
391
392             DatabaseEntry entry = new DatabaseEntry();
393             assertTrue(createSecondaryKey(secondary, null, data, entry));
394             assertEquals(entry, secKey);
395
396             return nullifyForeignKey(secondary, data);
397         }
398
399         /* ForeignKeyNullifier */
400         public boolean nullifyForeignKey(SecondaryDatabase secondary,
401                                          DatabaseEntry data)
402             throws DatabaseException {
403
404             int val = val(data);
405             if (val != 0) {
406                 data.setData(TestUtils.getTestArray(0));
407                 return true;
408             } else {
409                 return false;
410             }
411         }
412     }
413 }
414
Popular Tags