001: /*-
002: * See the file LICENSE for redistribution information.
003: *
004: * Copyright (c) 2002,2008 Oracle. All rights reserved.
005: *
006: * $Id: ChildReference.java,v 1.101.2.4 2008/01/07 15:14:16 cwl Exp $
007: */
008:
009: package com.sleepycat.je.tree;
010:
011: import java.nio.ByteBuffer;
012:
013: import com.sleepycat.je.DatabaseException;
014: import com.sleepycat.je.dbi.DatabaseImpl;
015: import com.sleepycat.je.dbi.EnvironmentImpl;
016: import com.sleepycat.je.log.LogFileNotFoundException;
017: import com.sleepycat.je.log.LogUtils;
018: import com.sleepycat.je.log.Loggable;
019: import com.sleepycat.je.utilint.DbLsn;
020:
021: /**
022: * A ChildReference is a reference in the tree from parent to child. It
023: * contains a node reference, key, and LSN.
024: */
025: public class ChildReference implements Loggable {
026:
027: private Node target;
028: private long lsn;
029: private byte[] key;
030:
031: /*
032: * The state byte holds knownDeleted state in bit 0 and dirty state in bit
033: * 1. Bit flags are used here because of the desire to keep the child
034: * reference compact. State is persistent because knownDeleted is
035: * persistent, but the dirty bit is cleared when read in from the log.
036: *
037: * -- KnownDeleted is a way of indicating that the reference is invalid
038: * without logging new data. This happens in aborts and recoveries. If
039: * knownDeleted is true, this entry is surely deleted. If knownDeleted is
040: * false, this entry may or may not be deleted. Future space optimizations:
041: * store as a separate bit array in the BIN, or subclass ChildReference and
042: * make a special reference only used by BINs and not by INs.
043: *
044: * -- Dirty is true if the LSN or key has been changed since the last time
045: * the ownign node was logged. This supports the calculation of BIN deltas.
046: */
047: private byte state;
048: private static final byte KNOWN_DELETED_BIT = 0x1;
049: private static final byte DIRTY_BIT = 0x2;
050: private static final byte CLEAR_DIRTY_BIT = ~0x2;
051: private static final byte MIGRATE_BIT = 0x4;
052: private static final byte CLEAR_MIGRATE_BIT = ~0x4;
053: private static final byte PENDING_DELETED_BIT = 0x8;
054:
055: /**
056: * Construct an empty child reference, for reading from the log.
057: */
058: ChildReference() {
059: init(null, Key.EMPTY_KEY, DbLsn.NULL_LSN, 0);
060: }
061:
062: /**
063: * Construct a ChildReference for inserting a new entry.
064: */
065: public ChildReference(Node target, byte[] key, long lsn) {
066: init(target, key, lsn, DIRTY_BIT);
067: }
068:
069: /**
070: * Construct a ChildReference for inserting an existing entry.
071: */
072: public ChildReference(Node target, byte[] key, long lsn,
073: byte existingState) {
074: init(target, key, lsn, existingState | DIRTY_BIT);
075: }
076:
077: private void init(Node target, byte[] key, long lsn, int state) {
078: this .target = target;
079: this .key = key;
080: this .lsn = lsn;
081: this .state = (byte) state;
082: }
083:
084: /**
085: * Return the key for this ChildReference.
086: */
087: public byte[] getKey() {
088: return key;
089: }
090:
091: /**
092: * Set the key for this ChildReference.
093: */
094: public void setKey(byte[] key) {
095: this .key = key;
096: setDirty();
097: }
098:
099: /**
100: * Fetch the target object that this ChildReference refers to. If the
101: * object is already in VM, then just return the reference to it. If the
102: * object is not in VM, then read the object from the log. If the object
103: * has been faulted in and the in arg is supplied, then the total memory
104: * size cache in the IN is invalidated.
105: *
106: * @param database The database that this ChildReference resides in.
107: * @param in The IN that this ChildReference lives in. If
108: * the target is fetched (i.e. it is null on entry), then the
109: * total in memory count is invalidated in the IN. May be null.
110: * For example, the root is a ChildReference and there is no parent IN
111: * when the rootIN is fetched in.
112: * @return the Node object representing the target node in the tree, or
113: * null if there is no target of this ChildReference, or null if a
114: * pendingDelete or knownDeleted entry has been cleaned.
115: */
116: public Node fetchTarget(DatabaseImpl database, IN in)
117: throws DatabaseException {
118:
119: if (target == null) {
120: /* fault object in from log */
121: if (lsn == DbLsn.NULL_LSN) {
122: if (!isKnownDeleted()) {
123: throw new DatabaseException(IN.makeFetchErrorMsg(
124: "NULL_LSN without KnownDeleted", in, lsn,
125: state));
126: }
127: /* Ignore a NULL_LSN (return null) if KnownDeleted is set. */
128: } else {
129: try {
130: EnvironmentImpl env = database.getDbEnvironment();
131: Node node = (Node) env.getLogManager().get(lsn);
132: node.postFetchInit(database, lsn);
133: target = node;
134: if (in != null) {
135: in.updateMemorySize(null, target);
136: }
137: } catch (LogFileNotFoundException LNFE) {
138: if (!isKnownDeleted() && !isPendingDeleted()) {
139: throw new DatabaseException(IN
140: .makeFetchErrorMsg(LNFE.toString(), in,
141: lsn, state), LNFE);
142: }
143: /* Ignore. Cleaner got to it, so just return null. */
144: } catch (Exception e) {
145: throw new DatabaseException(IN.makeFetchErrorMsg(e
146: .toString(), in, lsn, state), e);
147: }
148: }
149: }
150:
151: return target;
152: }
153:
154: /*
155: * Return the state byte for this ChildReference.
156: */
157: byte getState() {
158: return state;
159: }
160:
161: /**
162: * Return the target for this ChildReference.
163: */
164: public Node getTarget() {
165: return target;
166: }
167:
168: /**
169: * Sets the target for this ChildReference. No need to make dirty, that
170: * state only applies to key and LSN.
171: */
172: public void setTarget(Node target) {
173: this .target = target;
174: }
175:
176: /**
177: * Clear the target for this ChildReference. No need to make dirty, that
178: * state only applies to key and LSN. This method is public because it's
179: * safe and used by RecoveryManager. This can't corrupt the tree.
180: */
181: public void clearTarget() {
182: this .target = null;
183: }
184:
185: /**
186: * Return the LSN for this ChildReference.
187: *
188: * @return the LSN for this ChildReference.
189: */
190: public long getLsn() {
191: return lsn;
192: }
193:
194: /**
195: * Sets the target LSN for this ChildReference.
196: *
197: * @param the target LSN.
198: */
199: public void setLsn(long lsn) {
200: this .lsn = lsn;
201: setDirty();
202: }
203:
204: /**
205: * Do deferredWrite optional logging check.
206: */
207: void updateLsnAfterOptionalLog(DatabaseImpl dbImpl, long lsn) {
208: if ((lsn == DbLsn.NULL_LSN) && dbImpl.isDeferredWrite()) {
209: /*
210: * Don't update the lsn -- we don't want to overwrite a
211: * non-null lsn.
212: */
213: setDirty();
214: } else {
215: setLsn(lsn);
216: }
217: }
218:
219: private void setDirty() {
220: state |= DIRTY_BIT;
221: }
222:
223: /**
224: * @return true if the entry has been deleted, although the transaction the
225: * performed the deletion may not be committed.
226: */
227: private boolean isPendingDeleted() {
228: return ((state & PENDING_DELETED_BIT) != 0);
229: }
230:
231: /**
232: * @return true if entry is deleted for sure.
233: */
234: public boolean isKnownDeleted() {
235: return ((state & KNOWN_DELETED_BIT) != 0);
236: }
237:
238: /**
239: * @return true if the object is dirty.
240: */
241: private boolean isDirty() {
242: return ((state & DIRTY_BIT) != 0);
243: }
244:
245: /**
246: * Get the entry migrate status.
247: */
248: public boolean getMigrate() {
249: return (state & MIGRATE_BIT) != 0;
250: }
251:
252: /**
253: * Set the entry migrate status.
254: */
255: public void setMigrate(boolean migrate) {
256: if (migrate) {
257: state |= MIGRATE_BIT;
258: } else {
259: state &= CLEAR_MIGRATE_BIT;
260: }
261: }
262:
263: /*
264: * Support for logging.
265: */
266:
267: /**
268: * @see Loggable#getLogSize
269: */
270: public int getLogSize() {
271: return LogUtils.getByteArrayLogSize(key) + // key
272: LogUtils.getLongLogSize() + // LSN
273: 1; // state
274: }
275:
276: /**
277: * @see Loggable#writeToLog
278: */
279: public void writeToLog(ByteBuffer logBuffer) {
280: LogUtils.writeByteArray(logBuffer, key); // key
281: LogUtils.writeLong(logBuffer, lsn);
282: logBuffer.put(state); // state
283: state &= CLEAR_DIRTY_BIT;
284: }
285:
286: /**
287: * @see Loggable#readFromLog
288: */
289: public void readFromLog(ByteBuffer itemBuffer, byte entryTypeVersion) {
290: key = LogUtils.readByteArray(itemBuffer); // key
291: lsn = LogUtils.readLong(itemBuffer); // LSN
292: state = itemBuffer.get(); // state
293: state &= CLEAR_DIRTY_BIT;
294: }
295:
296: /**
297: * @see Loggable#dumpLog
298: */
299: public void dumpLog(StringBuffer sb, boolean verbose) {
300: sb.append("<ref knownDeleted=\"").append(isKnownDeleted());
301: sb.append("\" pendingDeleted=\"").append(isPendingDeleted());
302: sb.append("\">");
303: sb.append(Key.dumpString(key, 0));
304: sb.append(DbLsn.toString(lsn));
305: sb.append("</ref>");
306: }
307:
308: /**
309: * @see Loggable#getTransactionId
310: */
311: public long getTransactionId() {
312: return 0;
313: }
314:
315: /*
316: * Dumping
317: */
318: String dumpString(int nspaces, boolean dumpTags) {
319: StringBuffer sb = new StringBuffer();
320: if (lsn == DbLsn.NULL_LSN) {
321: sb.append(TreeUtils.indent(nspaces));
322: sb.append("<lsn/>");
323: } else {
324: sb.append(DbLsn.dumpString(lsn, nspaces));
325: }
326: sb.append('\n');
327: if (key == null) {
328: sb.append(TreeUtils.indent(nspaces));
329: sb.append("<key/>");
330: } else {
331: sb.append(Key.dumpString(key, nspaces));
332: }
333: sb.append('\n');
334: if (target == null) {
335: sb.append(TreeUtils.indent(nspaces));
336: sb.append("<target/>");
337: } else {
338: sb.append(target.dumpString(nspaces, true));
339: }
340: sb.append('\n');
341: sb.append(TreeUtils.indent(nspaces));
342: sb.append("<knownDeleted val=\"");
343: sb.append(isKnownDeleted()).append("\"/>");
344: sb.append("<pendingDeleted val=\"");
345: sb.append(isPendingDeleted()).append("\"/>");
346: sb.append("<dirty val=\"").append(isDirty()).append("\"/>");
347: return sb.toString();
348: }
349:
350: public String toString() {
351: return dumpString(0, false);
352: }
353: }
|