KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2002,2006 Oracle. All rights reserved.
5  *
6  * $Id: JoinTest.java,v 1.14 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.Cursor;
14 import com.sleepycat.je.Database;
15 import com.sleepycat.je.DatabaseConfig;
16 import com.sleepycat.je.DatabaseEntry;
17 import com.sleepycat.je.DatabaseException;
18 import com.sleepycat.je.DbInternal;
19 import com.sleepycat.je.EnvironmentConfig;
20 import com.sleepycat.je.JoinConfig;
21 import com.sleepycat.je.JoinCursor;
22 import com.sleepycat.je.LockMode;
23 import com.sleepycat.je.OperationStatus;
24 import com.sleepycat.je.SecondaryConfig;
25 import com.sleepycat.je.SecondaryCursor;
26 import com.sleepycat.je.SecondaryDatabase;
27 import com.sleepycat.je.SecondaryKeyCreator;
28 import com.sleepycat.je.Transaction;
29 import com.sleepycat.je.util.TestUtils;
30
31 public class JoinTest extends MultiKeyTxnTestCase {
32
33     /*
34      * DATA sets are pairs of arrays for each record. The first array is
35      * the record data and has three values in the 0/1/2 positions for the
36      * secondary key values with key IDs 0/1/2. second array contains a single
37      * value which is the primary key.
38      *
39      * JOIN sets are also pairs of arrays. The first array in each pair has 3
40      * values for setting the input cursors. Entries 0/1/2 in that array are
41      * for secondary keys 0/1/2. The second array is the set of primary keys
42      * that are expected to match in the join operation.
43      *
44      * A zero value for an index key means "don't index", so zero values are
45      * never used for join index keys since we wouldn't be able to successfully
46      * position the input cursor.
47      *
48      * These values are all stored as bytes, not ints, in the actual records,
49      * so all values must be within the range of a signed byte.
50      */

51     private static final int[][][] ALL = {
52         /* Data set #1 - single match possible per record. */
53         {
54             {1, 1, 1}, {11},
55             {2, 2, 2}, {12},
56             {3, 3, 3}, {13},
57         }, {
58             {1, 1, 1}, {11},
59             {2, 2, 2}, {12},
60             {3, 3, 3}, {13},
61             {1, 2, 3}, {},
62             {1, 1, 2}, {},
63             {3, 2, 2}, {},
64         },
65         /* Data set #2 - no match possible when all indices are not present
66          * (when some are zero). */

67         {
68             {1, 1, 0}, {11},
69             {2, 0, 2}, {12},
70             {0, 3, 3}, {13},
71             {3, 2, 1}, {14},
72         }, {
73             {1, 1, 1}, {},
74             {2, 2, 2}, {},
75             {3, 3, 3}, {},
76         },
77         /* Data set #3 - one match in the presence of non-matching records
78          * (with missing/zero index keys). */

79         {
80             {1, 0, 0}, {11},
81             {1, 1, 0}, {12},
82             {1, 1, 1}, {13},
83             {0, 0, 0}, {14},
84         }, {
85             {1, 1, 1}, {13},
86         },
87         /* Data set #4 - one match in the presence of non-matching records
88          * (with non-matching but non-zero values). */

89         {
90             {1, 2, 3}, {11},
91             {1, 1, 3}, {12},
92             {1, 1, 1}, {13},
93             {3, 2, 1}, {14},
94         }, {
95             {1, 1, 1}, {13},
96         },
97         /* Data set #5 - two matches in the presence of non-matching records.
98          */

99         {
100             {1, 2, 3}, {11},
101             {1, 1, 3}, {12},
102             {1, 1, 1}, {13},
103             {1, 2, 3}, {14},
104         }, {
105             {1, 2, 3}, {11, 14},
106         },
107         /* Data set #6 - three matches in the presence of non-matching records.
108          * Also used to verify that cursors are sorted by count: 2, 1, 0 */

109         {
110             {1, 2, 3}, {11},
111             {1, 1, 3}, {12},
112             {1, 1, 1}, {13},
113             {1, 2, 3}, {14},
114             {1, 1, 1}, {15},
115             {1, 0, 0}, {16},
116             {1, 1, 0}, {17},
117             {1, 1, 1}, {18},
118             {0, 0, 0}, {19},
119             {3, 2, 1}, {20},
120         }, {
121             {1, 1, 1}, {13, 15, 18},
122         },
123         /* Data set #7 - three matches by themselves. */
124         {
125             {1, 2, 3}, {11},
126             {1, 2, 3}, {12},
127             {1, 2, 3}, {13},
128         }, {
129             {1, 2, 3}, {11, 12, 13},
130         },
131     };
132
133     /* Used for testing the cursors are sorted by count. */
134     private static final int CURSOR_ORDER_SET = 6;
135     private static final int[] CURSOR_ORDER = {2, 1, 0};
136
137     private static EnvironmentConfig envConfig = TestUtils.initEnvConfig();
138     static {
139         envConfig.setAllowCreate(true);
140     }
141
142     private static JoinConfig joinConfigNoSort = new JoinConfig();
143     static {
144         joinConfigNoSort.setNoSort(true);
145     }
146
147     public static Test suite() {
148         return multiKeyTxnTestSuite(JoinTest.class, envConfig, null);
149     }
150
151     public void testJoin()
152         throws DatabaseException {
153
154         for (int i = 0; i < ALL.length; i += 2) {
155             doJoin(ALL[i], ALL[i + 1], (i / 2) + 1);
156         }
157     }
158
159     private void doJoin(int[][] dataSet, int[][] joinSet, int setNum)
160         throws DatabaseException {
161
162         String JavaDoc name = "Set#" + setNum;
163         Database priDb = openPrimary("pri");
164         SecondaryDatabase secDb0 = openSecondary(priDb, "sec0", true, 0);
165         SecondaryDatabase secDb1 = openSecondary(priDb, "sec1", true, 1);
166         SecondaryDatabase secDb2 = openSecondary(priDb, "sec2", true, 2);
167
168         OperationStatus status;
169         DatabaseEntry key = new DatabaseEntry();
170         DatabaseEntry data = new DatabaseEntry();
171         Transaction txn;
172         txn = txnBegin();
173
174         for (int i = 0; i < dataSet.length; i += 2) {
175             int[] vals = dataSet[i];
176             setData(data, vals[0], vals[1], vals[2]);
177             setKey(key, dataSet[i + 1][0]);
178             status = priDb.put(txn, key, data);
179             assertEquals(name, OperationStatus.SUCCESS, status);
180         }
181
182         txnCommit(txn);
183         txn = txnBeginCursor();
184
185         SecondaryCursor c0 = secDb0.openSecondaryCursor(txn, null);
186         SecondaryCursor c1 = secDb1.openSecondaryCursor(txn, null);
187         SecondaryCursor c2 = secDb2.openSecondaryCursor(txn, null);
188         SecondaryCursor[] cursors = {c0, c1, c2};
189
190         for (int i = 0; i < joinSet.length; i += 2) {
191             int[] indexKeys = joinSet[i];
192             int[] priKeys = joinSet[i + 1];
193             String JavaDoc prefix = name + " row=" + i;
194             for (int k = 0; k < 3; k += 1) {
195                 String JavaDoc msg = prefix + " k=" + k + " ikey=" + indexKeys[k];
196                 setKey(key, indexKeys[k]);
197                 status = cursors[k].getSearchKey(key, data,
198                                                  LockMode.DEFAULT);
199                 assertEquals(msg, OperationStatus.SUCCESS, status);
200             }
201             for (int j = 0; j < 2; j += 1) {
202                 boolean withData = (j == 0);
203                 JoinConfig config = (j == 0) ? null : joinConfigNoSort;
204                 JoinCursor jc = priDb.join(cursors, config);
205                 assertSame(priDb, jc.getDatabase());
206                 for (int k = 0; k < priKeys.length; k += 1) {
207                     String JavaDoc msg = prefix + " k=" + k + " pkey=" + priKeys[k];
208                     if (withData) {
209                         status = jc.getNext(key, data, LockMode.DEFAULT);
210                     } else {
211                         status = jc.getNext(key, LockMode.DEFAULT);
212                     }
213                     assertEquals(msg, OperationStatus.SUCCESS, status);
214                     assertEquals(msg, priKeys[k], (int) key.getData()[0]);
215                     if (withData) {
216                         boolean dataFound = false;
217                         for (int m = 0; m < dataSet.length; m += 2) {
218                             int[] vals = dataSet[m];
219                             int priKey = dataSet[m + 1][0];
220                             if (priKey == priKeys[k]) {
221                                 for (int n = 0; n < 3; n += 1) {
222                                     assertEquals(msg, vals[n],
223                                                  (int) data.getData()[n]);
224                                     dataFound = true;
225                                 }
226                             }
227                         }
228                         assertTrue(msg, dataFound);
229                     }
230                 }
231                 String JavaDoc msg = prefix + " no more expected";
232                 if (withData) {
233                     status = jc.getNext(key, data, LockMode.DEFAULT);
234                 } else {
235                     status = jc.getNext(key, LockMode.DEFAULT);
236                 }
237                 assertEquals(msg, OperationStatus.NOTFOUND, status);
238
239                 Cursor[] sorted = DbInternal.getSortedCursors(jc);
240                 assertEquals(CURSOR_ORDER.length, sorted.length);
241                 if (config == joinConfigNoSort) {
242                     Database db0 = sorted[0].getDatabase();
243                     Database db1 = sorted[1].getDatabase();
244                     Database db2 = sorted[2].getDatabase();
245                     assertSame(db0, secDb0);
246                     assertSame(db1, secDb1);
247                     assertSame(db2, secDb2);
248                 } else if (setNum == CURSOR_ORDER_SET) {
249                     Database db0 = sorted[CURSOR_ORDER[0]].getDatabase();
250                     Database db1 = sorted[CURSOR_ORDER[1]].getDatabase();
251                     Database db2 = sorted[CURSOR_ORDER[2]].getDatabase();
252                     assertSame(db0, secDb0);
253                     assertSame(db1, secDb1);
254                     assertSame(db2, secDb2);
255                 }
256                 jc.close();
257             }
258         }
259
260         c0.close();
261         c1.close();
262         c2.close();
263         txnCommit(txn);
264
265         secDb0.close();
266         secDb1.close();
267         secDb2.close();
268         priDb.close();
269
270         /* Remove dbs since we reuse them multiple times in a single case. */
271         txn = txnBegin();
272         env.removeDatabase(txn, "pri");
273         env.removeDatabase(txn, "sec0");
274         env.removeDatabase(txn, "sec1");
275         env.removeDatabase(txn, "sec2");
276         txnCommit(txn);
277     }
278
279     /**
280      * Checks that a join operation does not block writers from inserting
281      * duplicates with the same main key as the search key. Writers were being
282      * blocked before we changed join() to use READ_UNCOMMITTED when getting
283      * the duplicate count for each cursor. [#11833]
284      */

285     public void testWriteDuringJoin()
286         throws DatabaseException {
287
288         Database priDb = openPrimary("pri");
289         SecondaryDatabase secDb0 = openSecondary(priDb, "sec0", true, 0);
290         SecondaryDatabase secDb1 = openSecondary(priDb, "sec1", true, 1);
291         SecondaryDatabase secDb2 = openSecondary(priDb, "sec2", true, 2);
292
293         OperationStatus status;
294         DatabaseEntry key = new DatabaseEntry();
295         DatabaseEntry data = new DatabaseEntry();
296         Transaction txn;
297         txn = txnBegin();
298
299         setKey(key, 13);
300         setData(data, 1, 1, 1);
301         status = priDb.put(txn, key, data);
302         assertEquals(OperationStatus.SUCCESS, status);
303         setKey(key, 14);
304         setData(data, 1, 1, 1);
305         status = priDb.put(txn, key, data);
306         assertEquals(OperationStatus.SUCCESS, status);
307
308         txnCommit(txn);
309         txn = txnBeginCursor();
310
311         SecondaryCursor c0 = secDb0.openSecondaryCursor(txn, null);
312         SecondaryCursor c1 = secDb1.openSecondaryCursor(txn, null);
313         SecondaryCursor c2 = secDb2.openSecondaryCursor(txn, null);
314         SecondaryCursor[] cursors = {c0, c1, c2};
315
316         for (int i = 0; i < 3; i += 1) {
317             setKey(key, 1);
318             status = cursors[i].getSearchKey(key, data, LockMode.DEFAULT);
319             assertEquals(OperationStatus.SUCCESS, status);
320         }
321
322         /* join() will get the cursor counts. */
323         JoinCursor jc = priDb.join(cursors, null);
324
325         /*
326          * After calling join(), try inserting dups for the same main key.
327          * Before the fix to use READ_UNCOMMITTED, this would cause a deadlock.
328          */

329         Transaction writerTxn = txnBegin();
330         setKey(key, 12);
331         setData(data, 1, 1, 1);
332         status = priDb.put(writerTxn, key, data);
333         assertEquals(OperationStatus.SUCCESS, status);
334
335         /* The join should retrieve two records, 13 and 14. */
336         status = jc.getNext(key, data, LockMode.DEFAULT);
337         assertEquals(OperationStatus.SUCCESS, status);
338         assertEquals(13, (int) key.getData()[0]);
339         status = jc.getNext(key, data, LockMode.DEFAULT);
340         assertEquals(OperationStatus.SUCCESS, status);
341         assertEquals(14, (int) key.getData()[0]);
342         status = jc.getNext(key, data, LockMode.DEFAULT);
343         assertEquals(OperationStatus.NOTFOUND, status);
344
345         /* Try writing again after calling getNext(). */
346         setKey(key, 11);
347         setData(data, 1, 1, 1);
348         status = priDb.put(writerTxn, key, data);
349         assertEquals(OperationStatus.SUCCESS, status);
350         txnCommit(writerTxn);
351
352         jc.close();
353
354         c0.close();
355         c1.close();
356         c2.close();
357         txnCommit(txn);
358
359         secDb0.close();
360         secDb1.close();
361         secDb2.close();
362         priDb.close();
363     }
364
365     private Database openPrimary(String JavaDoc name)
366         throws DatabaseException {
367
368         DatabaseConfig dbConfig = new DatabaseConfig();
369         dbConfig.setTransactional(isTransactional);
370         dbConfig.setAllowCreate(true);
371
372         Transaction txn = txnBegin();
373         try {
374             return env.openDatabase(txn, name, dbConfig);
375         } finally {
376             txnCommit(txn);
377         }
378     }
379
380     private SecondaryDatabase openSecondary(Database priDb, String JavaDoc dbName,
381                                             boolean dups, int keyId)
382         throws DatabaseException {
383
384         SecondaryConfig dbConfig = new SecondaryConfig();
385         dbConfig.setTransactional(isTransactional);
386         dbConfig.setAllowCreate(true);
387         dbConfig.setSortedDuplicates(dups);
388         if (useMultiKey) {
389             dbConfig.setMultiKeyCreator
390                 (new SimpleMultiKeyCreator(new MyKeyCreator(keyId)));
391         } else {
392             dbConfig.setKeyCreator(new MyKeyCreator(keyId));
393         }
394
395         Transaction txn = txnBegin();
396         try {
397             return env.openSecondaryDatabase(txn, dbName, priDb, dbConfig);
398         } finally {
399             txnCommit(txn);
400         }
401     }
402
403     private static void setKey(DatabaseEntry key, int priKey) {
404
405         byte[] a = new byte[1];
406         a[0] = (byte) priKey;
407         key.setData(a);
408     }
409
410     private static void setData(DatabaseEntry data,
411                                 int key1, int key2, int key3) {
412
413         byte[] a = new byte[4];
414         a[0] = (byte) key1;
415         a[1] = (byte) key2;
416         a[2] = (byte) key3;
417         data.setData(a);
418     }
419
420     private static class MyKeyCreator implements SecondaryKeyCreator {
421
422         private int keyId;
423
424         MyKeyCreator(int keyId) {
425
426             this.keyId = keyId;
427         }
428
429         public boolean createSecondaryKey(SecondaryDatabase secondary,
430                                           DatabaseEntry key,
431                                           DatabaseEntry data,
432                                           DatabaseEntry result)
433             throws DatabaseException {
434
435             byte val = data.getData()[keyId];
436             if (val != 0) {
437                 result.setData(new byte[] { val });
438                 return true;
439             } else {
440                 return false;
441             }
442         }
443     }
444 }
445
Popular Tags