@Test public void testCursorPutAndGet() throws Exception { k1.putLong(0, 14); v1.putLong(0, 15); k2.putLong(0, 16); v2.putLong(0, 17); try (Transaction tx = env.createWriteTransaction()) { try (Cursor cursor = db.openCursor(tx)) { cursor.put(k1, v1, 0); cursor.put(k2, v2, 0); } tx.commit(); } DirectBuffer k = new DirectBuffer(); DirectBuffer v = new DirectBuffer(); try (Transaction tx = env.createReadTransaction()) { try (Cursor cursor = db.openCursor(tx)) { cursor.position(k, v, GetOp.FIRST); assertThat(k.getLong(0), is(14L)); assertThat(v.getLong(0), is(15L)); cursor.position(k, v, GetOp.NEXT); assertThat(k.getLong(0), is(16L)); assertThat(v.getLong(0), is(17L)); } } }
@Test public void testCursorSeekRange() throws Exception { ByteBuffer byteBuffer = ByteBuffer.allocateDirect(8); k1.putLong(0, 18); v1.putLong(0, 19); k2.putLong(0, 20); v2.putLong(0, 21); try (Transaction tx = env.createWriteTransaction()) { try (Cursor cursor = db.openCursor(tx)) { cursor.put(k1, v1, 0); cursor.put(k2, v2, 0); } tx.commit(); } try (Transaction tx = env.createReadTransaction()) { try (Cursor cursor = db.openCursor(tx)) { DirectBuffer k = new DirectBuffer(byteBuffer); DirectBuffer v = new DirectBuffer(); k.putLong(0, 10); cursor.seekPosition(k, v, SeekOp.RANGE); assertThat(k.getLong(0), is(18L)); assertThat(v.getLong(0), is(19L)); k.wrap(byteBuffer); k.putLong(0, 19); cursor.seekPosition(k, v, SeekOp.RANGE); assertThat(k.getLong(0), is(20L)); assertThat(v.getLong(0), is(21L)); } } }
public static Database buildtertiary(Database std) { try { // configure new database instance io.deleteFile("tertiarydb"); DatabaseConfig dbConfig = new DatabaseConfig(); dbConfig.setType(DatabaseType.HASH); dbConfig.setAllowCreate(true); dbConfig.setUnsortedDuplicates(true); Database tdb = new Database("tertiarydb", null, dbConfig); // configure cursors and entries Cursor stdCurs = std.openCursor(null, null); Cursor tCurs = tdb.openCursor(null, null); DatabaseEntry stdKey = new DatabaseEntry(); DatabaseEntry stdData = new DatabaseEntry(); DatabaseEntry tKey = new DatabaseEntry(); DatabaseEntry tData = new DatabaseEntry(); // extract from linearly constructed database to populate <song>,<users> table, making // use of songs grouped by id String currUsers = ""; String prevID = "default"; boolean firstIter = true; while (stdCurs.getNext(stdKey, stdData, LockMode.DEFAULT) == OperationStatus.SUCCESS) { // get rating data from current row String[] currIDUser = (new String(stdKey.getData())).split(","); String currID = currIDUser[0].trim(); String currUser = currIDUser[1].trim(); String currRating = (new String(stdData.getData())); stdKey.setData(null); stdData.setData(null); if (currID.equals(prevID) || firstIter) { // concatenate new username with current string currUsers += "(" + currUser + "," + currRating + ")"; } else if (!firstIter) { // insert completed <usernames> into table under key <song id> tKey.setData(prevID.getBytes()); tData.setData(currUsers.substring(0, currUsers.length()).getBytes()); tCurs.put(tKey, tData); tKey.setData(null); tData.setData(null); // DEBUG: // System.out.println(prevID+","+currUsers.substring(0, currUsers.length()-1)); // start the new <usernames> for the next song (in currID) currUsers = "(" + currUser + "," + currRating + ")"; } prevID = currID; firstIter = false; } // repeat iteration for last song tKey.setData(prevID.getBytes()); tData.setData(currUsers.substring(0, currUsers.length()).getBytes()); tCurs.put(tKey, tData); tKey.setData(null); tData.setData(null); // DEBUG: // io.debugread(tdb); tCurs.close(); return tdb; } catch (Exception e) { System.out.println(" error creating <song>,<users> tertiary table\n"); System.out.println(e.getMessage()); } return null; // should never happen }
/** * Returns the next available element in the sequence and changes the sequence value by <code> * delta</code>. The value of <code>delta</code> must be greater than zero. If there are enough * cached values in the sequence handle then they will be returned. Otherwise the next value will * be fetched from the database and incremented (decremented) by enough to cover the <code>delta * </code> and the next batch of cached values. * * <p>This method is synchronized to protect updating of the cached value, since multiple threads * may share a single handle. Multiple handles for the same database/key may be used to increase * concurrency. * * <p>The <code>txn</code> handle must be null if the sequence handle was opened with a non-zero * cache size. * * <p>For maximum concurrency, a non-zero cache size should be specified prior to opening the * sequence handle, the <code>txn</code> handle should be <code>null</code>, and {@link * com.sleepycat.je.SequenceConfig#setAutoCommitNoSync SequenceConfig.setAutoCommitNoSync} should * be called to disable log flushes. * * @param txn For a transactional database, an explicit transaction may be specified, or null may * be specified to use auto-commit. For a non-transactional database, null must be specified. * @param delta the amount by which to increment or decrement the sequence * @return the next available element in the sequence * @throws SequenceOverflowException if the end of the sequence is reached and wrapping is not * configured. * @throws SequenceIntegrityException if the sequence record has been deleted. * @throws OperationFailureException if one of the <a * href="../je/OperationFailureException.html#writeFailures">Write Operation Failures</a> * occurs. * @throws EnvironmentFailureException if an unexpected, internal or environment-wide failure * occurs. * @throws IllegalArgumentException if the delta is less than or equal to zero, or larger than the * size of the sequence's range. */ public synchronized long get(Transaction txn, int delta) throws DatabaseException { /* Check parameters, being careful of overflow. */ if (delta <= 0) { throw new IllegalArgumentException("Sequence delta must be greater than zero"); } if (rangeMin > rangeMax - delta) { throw new IllegalArgumentException("Sequence delta is larger than the range"); } /* Status variables for tracing. */ boolean cached = true; boolean wrapped = false; /* * Determine whether we have exceeded the cache. The cache size is * always <= Integer.MAX_VALUE, so we don't have to worry about * overflow here as long as we subtract the two long values first. */ if ((increment && delta > ((cacheLast - cacheValue) + 1)) || (!increment && delta > ((cacheValue - cacheLast) + 1))) { cached = false; /* * We need to allocate delta or cacheSize values, whichever is * larger, by incrementing or decrementing the stored value by * adjust. */ int adjust = (delta > cacheSize) ? delta : cacheSize; /* Perform an auto-commit transaction to update the sequence. */ Locker locker = null; Cursor cursor = null; OperationStatus status = OperationStatus.NOTFOUND; try { locker = LockerFactory.getWritableLocker( db.getEnvironment(), txn, db.isTransactional(), false, // retainNonTxnLocks db.getDatabaseImpl().isReplicated(), // autoTxnIsReplicated autoCommitConfig); cursor = new Cursor(db, locker, null); /* Get the existing record. */ readDataRequired(cursor, LockMode.RMW); /* If we would have wrapped when not allowed, overflow. */ if (overflow) { throw new SequenceOverflowException("Sequence overflow " + storedValue); } /* * Handle wrapping. The range size can be larger than a long * can hold, so to avoid arithmetic overflow we use BigInteger * arithmetic. Since we are going to write, the BigInteger * overhead is acceptable. */ BigInteger availBig; if (increment) { /* Available amount: rangeMax - storedValue */ availBig = BigInteger.valueOf(rangeMax).subtract(BigInteger.valueOf(storedValue)); } else { /* Available amount: storedValue - rangeMin */ availBig = BigInteger.valueOf(storedValue).subtract(BigInteger.valueOf(rangeMin)); } if (availBig.compareTo(BigInteger.valueOf(adjust)) < 0) { /* If availBig < adjust then availBig fits in an int. */ int availInt = (int) availBig.longValue(); if (availInt < delta) { if (wrapAllowed) { /* Wrap to the opposite range end point. */ storedValue = increment ? rangeMin : rangeMax; wrapped = true; } else { /* Signal an overflow next time. */ overflow = true; adjust = 0; } } else { /* * If the delta fits in the cache available, don't wrap * just to allocate the full cacheSize; instead, * allocate as much as is available. */ adjust = availInt; } } /* Negate the adjustment for decrementing. */ if (!increment) { adjust = -adjust; } /* Set the stored value one past the cached amount. */ storedValue += adjust; /* Write the new stored value. */ cursor.put(key, makeData()); status = OperationStatus.SUCCESS; } finally { if (cursor != null) { cursor.close(); } if (locker != null) { locker.operationEnd(status); } } /* The cache now contains the range: [cacheValue, storedValue) */ cacheValue = storedValue - adjust; cacheLast = storedValue + (increment ? (-1) : 1); } /* Return the current value and increment/decrement it by delta. */ long retVal = cacheValue; if (increment) { cacheValue += delta; } else { cacheValue -= delta; } /* Increment stats. */ nGets += 1; if (cached) { nCachedGets += 1; } /* Trace this method at the FINEST level. */ if (logger.isLoggable(Level.FINEST)) { LoggerUtils.finest( logger, db.getEnvironment().getEnvironmentImpl(), "Sequence.get" + " value=" + retVal + " cached=" + cached + " wrapped=" + wrapped); } return retVal; }