package com.sleepycat.je.tree; import java.nio.ByteBuffer; import java.util.Comparator; import com.sleepycat.je.DatabaseException; import com.sleepycat.je.cleaner.Cleaner; import com.sleepycat.je.dbi.DatabaseId; import com.sleepycat.je.dbi.DatabaseImpl; import com.sleepycat.je.dbi.DbConfigManager; import com.sleepycat.je.dbi.MemoryBudget; import com.sleepycat.je.log.LogEntryType; import com.sleepycat.je.log.LogException; import com.sleepycat.je.log.LogManager; import com.sleepycat.je.log.LogUtils; import com.sleepycat.je.txn.LockResult; import com.sleepycat.je.txn.Locker; import de.ovgu.cide.jakutil.*; /** * An DIN represents an Duplicate Internal Node in the JE tree. */ public final class DIN extends IN { private static final String BEGIN_TAG="<din>"; private static final String END_TAG="</din>"; /** * Full key for this set of duplicates. */ private byte[] dupKey; /** * Reference to DupCountLN which stores the count. */ private ChildReference dupCountLNRef; /** * Create an empty DIN, with no node id, to be filled in from the log. */ public DIN(){ super(); dupCountLNRef=new ChildReference(); init(null,Key.EMPTY_KEY,0,0); } /** * Create a new DIN. */ public DIN( DatabaseImpl db, byte[] identifierKey, int capacity, byte[] dupKey, ChildReference dupCountLNRef, int level){ super(db,identifierKey,capacity,level); this.dupKey=dupKey; this.dupCountLNRef=dupCountLNRef; } protected int generateLevel( DatabaseId dbId, int newLevel){ return newLevel; } /** * Create a new DIN. Need this because we can't call newInstance() * without getting a 0 node. */ protected IN createNewInstance( byte[] identifierKey, int maxEntries, int level){ return new DIN(getDatabase(),identifierKey,maxEntries,dupKey,dupCountLNRef,level); } /** * Return the key for this duplicate set. */ public byte[] getDupKey(){ return dupKey; } /** * Get the key (dupe or identifier) in child that is used to locate * it in 'this' node. */ public byte[] getChildKey( IN child) throws DatabaseException { return child.getIdentifierKey(); } public byte[] selectKey( byte[] mainTreeKey, byte[] dupTreeKey){ return dupTreeKey; } /** * Return the key for navigating through the duplicate tree. */ public byte[] getDupTreeKey(){ return getIdentifierKey(); } /** * Return the key for navigating through the main tree. */ public byte[] getMainTreeKey(){ return dupKey; } public ChildReference getDupCountLNRef(){ return dupCountLNRef; } public DupCountLN getDupCountLN() throws DatabaseException { return (DupCountLN)dupCountLNRef.fetchTarget(getDatabase(),this); } /** * Assign the Dup Count LN. */ void setDupCountLN( ChildReference dupCountLNRef){ this.dupCountLNRef=dupCountLNRef; } /** * Assign the Dup Count LN node. Does not dirty the DIN. */ public void updateDupCountLN( Node target){ new DIN_updateDupCountLN(this,target).execute(); } /** * Update Dup Count LN. */ public void updateDupCountLNRefAndNullTarget( long newLsn){ new DIN_updateDupCountLNRefAndNullTarget(this,newLsn).execute(); } /** * Update dup count LSN. */ public void updateDupCountLNRef( long newLsn){ setDirty(true); dupCountLNRef.setLsn(newLsn); } /** * @return true if this node is a duplicate-bearing node type, false * if otherwise. */ public boolean containsDuplicates(){ return true; } public boolean isDbRoot(){ return false; } /** * Return the comparator function to be used for DINs. This is * the user defined duplicate comparison function, if defined. */ public final Comparator getKeyComparator(){ return getDatabase().getDuplicateComparator(); } /** * Increment or decrement the DupCountLN, log the updated LN, and update * the lock result. * Preconditions: This DIN is latched and the DupCountLN is write locked. * Postconditions: Same as preconditions. */ public void incrementDuplicateCount( LockResult lockResult, byte[] key, Locker locker, boolean increment) throws DatabaseException { long oldLsn=dupCountLNRef.getLsn(); lockResult.setAbortLsn(oldLsn,dupCountLNRef.isKnownDeleted()); DupCountLN dupCountLN=getDupCountLN(); if (increment) { dupCountLN.incDupCount(); } else { dupCountLN.decDupCount(); assert dupCountLN.getDupCount() >= 0; } DatabaseImpl db=getDatabase(); long newCountLSN=dupCountLN.log(db.getDbEnvironment(),db.getId(),key,oldLsn,locker); updateDupCountLNRef(newCountLSN); } boolean matchLNByNodeId( TreeLocation location, long nodeId) throws DatabaseException { for (int i=0; i < getNEntries(); i++) { Node n=fetchTarget(i); if (n != null) { boolean ret=n.matchLNByNodeId(location,nodeId); if (ret) { return true; } } } return false; } void accumulateStats( TreeWalkerStatsAccumulator acc){ acc.processDIN(this,new Long(getNodeId()),getLevel()); } /** * @see IN#getLogType */ public LogEntryType getLogType(){ return LogEntryType.LOG_DIN; } /** * Handles lazy migration of DupCountLNs prior to logging a DIN. See * BIN.logInternal for more information. */ protected long logInternal( LogManager logManager, boolean allowDeltas, boolean isProvisional, boolean proactiveMigration, IN parent) throws DatabaseException { if (dupCountLNRef != null) { Cleaner cleaner=getDatabase().getDbEnvironment().getCleaner(); cleaner.lazyMigrateDupCountLN(this,dupCountLNRef,proactiveMigration); } return super.logInternal(logManager,allowDeltas,isProvisional,proactiveMigration,parent); } /** * @see IN#getLogSize */ public int getLogSize(){ int size=super.getLogSize(); size+=LogUtils.getByteArrayLogSize(dupKey); size+=LogUtils.getBooleanLogSize(); if (dupCountLNRef != null) { size+=dupCountLNRef.getLogSize(); } return size; } /** * @see IN#writeToLog */ public void writeToLog( ByteBuffer logBuffer){ super.writeToLog(logBuffer); LogUtils.writeByteArray(logBuffer,dupKey); boolean dupCountLNRefExists=(dupCountLNRef != null); LogUtils.writeBoolean(logBuffer,dupCountLNRefExists); if (dupCountLNRefExists) { dupCountLNRef.writeToLog(logBuffer); } } /** * @see IN#readFromLog */ public void readFromLog( ByteBuffer itemBuffer, byte entryTypeVersion) throws LogException { super.readFromLog(itemBuffer,entryTypeVersion); dupKey=LogUtils.readByteArray(itemBuffer); boolean dupCountLNRefExists=LogUtils.readBoolean(itemBuffer); if (dupCountLNRefExists) { dupCountLNRef.readFromLog(itemBuffer,entryTypeVersion); } else { dupCountLNRef=null; } } /** * DINS need to dump their dup key */ protected void dumpLogAdditional( StringBuffer sb){ super.dumpLogAdditional(sb); sb.append(Key.dumpString(dupKey,0)); if (dupCountLNRef != null) { dupCountLNRef.dumpLog(sb,true); } } public String beginTag(){ return BEGIN_TAG; } public String endTag(){ return END_TAG; } /** * For unit test support: * @return a string that dumps information about this DIN, without */ public String dumpString( int nSpaces, boolean dumpTags){ StringBuffer sb=new StringBuffer(); if (dumpTags) { sb.append(TreeUtils.indent(nSpaces)); sb.append(beginTag()); sb.append('\n'); } sb.append(TreeUtils.indent(nSpaces + 2)); sb.append("<dupkey>"); sb.append(dupKey == null ? "" : Key.dumpString(dupKey,0)); sb.append("</dupkey>"); sb.append('\n'); if (dupCountLNRef == null) { sb.append(TreeUtils.indent(nSpaces + 2)); sb.append("<dupCountLN/>"); } else { sb.append(dupCountLNRef.dumpString(nSpaces + 4,true)); } sb.append('\n'); sb.append(super.dumpString(nSpaces,false)); if (dumpTags) { sb.append(TreeUtils.indent(nSpaces)); sb.append(endTag()); } return sb.toString(); } public String toString(){ return dumpString(0,true); } public String shortClassName(){ return "DIN"; } @MethodObject static class DIN_updateDupCountLN { DIN_updateDupCountLN( DIN _this, Node target){ this._this=_this; this.target=target; } void execute(){ _this.dupCountLNRef.setTarget(target); } protected DIN _this; protected Node target; protected long oldSize; protected long newSize; } @MethodObject static class DIN_updateDupCountLNRefAndNullTarget { DIN_updateDupCountLNRefAndNullTarget( DIN _this, long newLsn){ this._this=_this; this.newLsn=newLsn; } void execute(){ _this.setDirty(true); this.hook614(); _this.dupCountLNRef.setTarget(null); _this.dupCountLNRef.setLsn(newLsn); } protected DIN _this; protected long newLsn; protected long oldSize; protected long newSize; protected void hook614(){ } } }