1 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 ); 40 41 try { 42 openSecondary(priDb1, "sec2", priDb2, ForeignKeyDeleteAction.ABORT); 43 fail(); 44 } catch (IllegalArgumentException expected) { 45 String 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 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 expected) { } 70 71 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 expected) { } 81 82 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 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 137 138 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 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 186 187 if (onDelete == ForeignKeyDeleteAction.ABORT) { 188 189 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 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 210 211 status = secDb2.get(txn, entry(1), data, LockMode.DEFAULT); 212 assertEquals(OperationStatus.NOTFOUND, status); 213 214 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 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 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 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 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 (); 279 } 280 281 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 name) 298 throws DatabaseException { 299 300 return openPrimary(name, false); 301 } 302 303 private Database openPrimary(String 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 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 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 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 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 |