package com.sleepycat.je.tree; import java.nio.ByteBuffer; import java.util.Map; import com.sleepycat.je.DatabaseEntry; import com.sleepycat.je.DatabaseException; import com.sleepycat.je.cleaner.UtilizationTracker; import com.sleepycat.je.dbi.DatabaseId; import com.sleepycat.je.dbi.DatabaseImpl; import com.sleepycat.je.dbi.EnvironmentImpl; import com.sleepycat.je.dbi.INList; 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.LogReadable; import com.sleepycat.je.log.LogUtils; import com.sleepycat.je.log.LoggableObject; import com.sleepycat.je.log.entry.DeletedDupLNLogEntry; import com.sleepycat.je.log.entry.LNLogEntry; import com.sleepycat.je.txn.Locker; import com.sleepycat.je.txn.Txn; import com.sleepycat.je.txn.WriteLockInfo; import com.sleepycat.je.utilint.DbLsn; import de.ovgu.cide.jakutil.*; /** * An LN represents a Leaf Node in the JE tree. */ public class LN extends Node implements LoggableObject, LogReadable { private static final String BEGIN_TAG="<ln>"; private static final String END_TAG="</ln>"; private byte[] data; /** * Create an empty LN, to be filled in from the log. */ public LN(){ super(false); this.data=null; } /** * Create a new LN from a byte array. */ public LN( byte[] data){ super(true); if (data == null) { this.data=null; } else { init(data,0,data.length); } } /** * Create a new LN from a DatabaseEntry. */ public LN( DatabaseEntry dbt){ super(true); byte[] data=dbt.getData(); if (data == null) { this.data=null; } else if (dbt.getPartial()) { init(data,dbt.getOffset(),dbt.getPartialOffset() + dbt.getSize(),dbt.getPartialOffset(),dbt.getSize()); } else { init(data,dbt.getOffset(),dbt.getSize()); } } private void init( byte[] data, int off, int len, int doff, int dlen){ if (len == 0) { this.data=LogUtils.ZERO_LENGTH_BYTE_ARRAY; } else { this.data=new byte[len]; System.arraycopy(data,off,this.data,doff,dlen); } } private void init( byte[] data, int off, int len){ init(data,off,len,0,len); } public byte[] getData(){ return data; } public byte[] copyData(){ int len=data.length; byte[] ret=new byte[len]; System.arraycopy(data,0,ret,0,len); return ret; } public boolean isDeleted(){ return (data == null); } void makeDeleted(){ data=null; } boolean isValidForDelete(){ return false; } /** * A LN can never be a child in the search chain. */ protected boolean isSoughtNode( long nid, boolean updateGeneration){ return false; } /** * A LN can never be the ancestor of another node. */ protected boolean canBeAncestor( boolean targetContainsDuplicates){ return false; } /** * Delete this LN's data and log the new version. */ public long delete( DatabaseImpl database, byte[] lnKey, byte[] dupKey, long oldLsn, Locker locker) throws DatabaseException { makeDeleted(); EnvironmentImpl env=database.getDbEnvironment(); long newLsn=DbLsn.NULL_LSN; if (dupKey != null) { LogEntryType entryType; long logAbortLsn; boolean logAbortKnownDeleted; Txn logTxn; if (locker.isTransactional()) { entryType=LogEntryType.LOG_DEL_DUPLN_TRANSACTIONAL; WriteLockInfo info=locker.getWriteLockInfo(getNodeId()); logAbortLsn=info.getAbortLsn(); logAbortKnownDeleted=info.getAbortKnownDeleted(); logTxn=locker.getTxnLocker(); } else { entryType=LogEntryType.LOG_DEL_DUPLN; logAbortLsn=DbLsn.NULL_LSN; logAbortKnownDeleted=true; logTxn=null; } if (oldLsn == logAbortLsn) { oldLsn=DbLsn.NULL_LSN; } DeletedDupLNLogEntry logEntry=new DeletedDupLNLogEntry(entryType,this,database.getId(),dupKey,lnKey,logAbortLsn,logAbortKnownDeleted,logTxn); LogManager logManager=env.getLogManager(); newLsn=logManager.log(logEntry,false,oldLsn); } else { newLsn=log(env,database.getId(),lnKey,oldLsn,locker); } return newLsn; } /** * Modify the LN's data and log the new version. */ public long modify( byte[] newData, DatabaseImpl database, byte[] lnKey, long oldLsn, Locker locker) throws DatabaseException { data=newData; EnvironmentImpl env=database.getDbEnvironment(); long newLsn=log(env,database.getId(),lnKey,oldLsn,locker); return newLsn; } /** * Add yourself to the dirty list if you're dirty. LNs are never dirty. */ void addToDirtyMap( Map dirtyMap){ } /** * Add yourself to the in memory list if you're a type of node that should * belong. */ void rebuildINList( INList inList){ } /** * No need to do anything, stop the search. */ void accountForSubtreeRemoval( INList inList, UtilizationTracker tracker){ } public String beginTag(){ return BEGIN_TAG; } public String endTag(){ return END_TAG; } public String dumpString( int nSpaces, boolean dumpTags){ StringBuffer self=new StringBuffer(); if (dumpTags) { self.append(TreeUtils.indent(nSpaces)); self.append(beginTag()); self.append('\n'); } self.append(super.dumpString(nSpaces + 2,true)); self.append('\n'); if (data != null) { self.append(TreeUtils.indent(nSpaces + 2)); self.append("<data>"); self.append(TreeUtils.dumpByteArray(data)); self.append("</data>"); self.append('\n'); } if (dumpTags) { self.append(TreeUtils.indent(nSpaces)); self.append(endTag()); } return self.toString(); } /** * Log a provisional, non-txnal version of a ln. * @param env the environment. * @param dbId database id of this node. (Not stored in LN) * @param key key of this node. (Not stored in LN) * @param oldLsn is the LSN of the previous version or null. */ public long logProvisional( EnvironmentImpl env, DatabaseId dbId, byte[] key, long oldLsn) throws DatabaseException { return log(env,dbId,key,oldLsn,null,true); } /** * Log this LN. Whether its logged as * a transactional entry or not depends on the type of locker. * @param env the environment. * @param dbId database id of this node. (Not stored in LN) * @param key key of this node. (Not stored in LN) * @param oldLsn is the LSN of the previous version or null. * @param locker owning locker. */ public long log( EnvironmentImpl env, DatabaseId dbId, byte[] key, long oldLsn, Locker locker) throws DatabaseException { return log(env,dbId,key,oldLsn,locker,false); } /** * Log this LN. Whether its logged as a transactional entry or not depends * on the type of locker. * @param env the environment. * @param dbId database id of this node. (Not stored in LN) * @param key key of this node. (Not stored in LN) * @param oldLsn is the LSN of the previous version or null. * @param locker owning locker. */ private long log( EnvironmentImpl env, DatabaseId dbId, byte[] key, long oldLsn, Locker locker, boolean isProvisional) throws DatabaseException { LogEntryType entryType; long logAbortLsn; boolean logAbortKnownDeleted; Txn logTxn; if (locker != null && locker.isTransactional()) { entryType=getTransactionalLogType(); WriteLockInfo info=locker.getWriteLockInfo(getNodeId()); logAbortLsn=info.getAbortLsn(); logAbortKnownDeleted=info.getAbortKnownDeleted(); logTxn=locker.getTxnLocker(); assert logTxn != null; } else { entryType=getLogType(); logAbortLsn=DbLsn.NULL_LSN; logAbortKnownDeleted=false; logTxn=null; } if (oldLsn == logAbortLsn) { oldLsn=DbLsn.NULL_LSN; } LNLogEntry logEntry=new LNLogEntry(entryType,this,dbId,key,logAbortLsn,logAbortKnownDeleted,logTxn); LogManager logManager=env.getLogManager(); return logManager.log(logEntry,isProvisional,oldLsn); } /** * Log type for transactional entries */ protected LogEntryType getTransactionalLogType(){ return LogEntryType.LOG_LN_TRANSACTIONAL; } /** * @see LoggableObject#countAsObsoleteWhenLogged */ public boolean countAsObsoleteWhenLogged(){ return false; } /** * @see LoggableObject#getLogType */ public LogEntryType getLogType(){ return LogEntryType.LOG_LN; } /** * @see LoggableObject#getLogSize */ public int getLogSize(){ int size=super.getLogSize(); size+=LogUtils.getBooleanLogSize(); if (!isDeleted()) { size+=LogUtils.getByteArrayLogSize(data); } return size; } /** * @see LoggableObject#writeToLog */ public void writeToLog( ByteBuffer logBuffer){ super.writeToLog(logBuffer); boolean dataExists=!isDeleted(); LogUtils.writeBoolean(logBuffer,dataExists); if (dataExists) { LogUtils.writeByteArray(logBuffer,data); } } /** * @see LogReadable#readFromLog */ public void readFromLog( ByteBuffer itemBuffer, byte entryTypeVersion) throws LogException { super.readFromLog(itemBuffer,entryTypeVersion); boolean dataExists=LogUtils.readBoolean(itemBuffer); if (dataExists) { data=LogUtils.readByteArray(itemBuffer); } } /** * @see LogReadable#dumpLog */ public void dumpLog( StringBuffer sb, boolean verbose){ sb.append(beginTag()); super.dumpLog(sb,verbose); if (data != null) { sb.append("<data>"); sb.append(TreeUtils.dumpByteArray(data)); sb.append("</data>"); } dumpLogAdditional(sb,verbose); sb.append(endTag()); } /** * Never called. * @see LogReadable#logEntryIsTransactional. */ public boolean logEntryIsTransactional(){ return false; } /** * Never called. * @see LogReadable#getTransactionId */ public long getTransactionId(){ return 0; } protected void dumpLogAdditional( StringBuffer sb, boolean verbose){ } }