### Eclipse Workspace Patch 1.0 #P apache-trunk Index: hbase-common/src/main/java/org/apache/hadoop/hbase/KeyOnlyCell.java =================================================================== --- hbase-common/src/main/java/org/apache/hadoop/hbase/KeyOnlyCell.java (revision 0) +++ hbase-common/src/main/java/org/apache/hadoop/hbase/KeyOnlyCell.java (revision 0) @@ -0,0 +1,161 @@ +/** + * Copyright The Apache Software Foundation + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package org.apache.hadoop.hbase; + +import org.apache.hadoop.classification.InterfaceAudience; +import org.apache.hadoop.hbase.util.Bytes; + +/** + * KeyOnlyCell represents a cell which doesn't have the value bytes, it could be + * used when we want to create/fake a cell as the specified key bytes. e.g. + * PrefixTreeSeeker#seekToOrBeforeUsingPositionAtOrAfter + */ +@InterfaceAudience.Private +public class KeyOnlyCell implements Cell { + private byte[] keyOnlyBytes = null; + private int offset = 0; + private int length = 0; + + public KeyOnlyCell(final byte[] bytes) { + this(bytes, 0, bytes.length); + } + + public KeyOnlyCell(final byte[] bytes, final int offset, final int length) { + this.keyOnlyBytes = bytes; + this.offset = offset; + this.length = length; + } + + public int getKeyOffset() { + return this.offset; + } + + public int getKeyLength() { + return this.length; + } + + @Override + public byte[] getRowArray() { + return keyOnlyBytes; + } + + @Override + public int getRowOffset() { + return getKeyOffset() + Bytes.SIZEOF_SHORT; + } + + @Override + public short getRowLength() { + return Bytes.toShort(this.keyOnlyBytes, getKeyOffset()); + } + + @Override + public byte[] getFamilyArray() { + return keyOnlyBytes; + } + + @Override + public int getFamilyOffset() { + return getFamilyOffset(getRowLength()); + } + + int getFamilyOffset(int rlength) { + return getKeyOffset() + Bytes.SIZEOF_SHORT + rlength + + Bytes.SIZEOF_BYTE; + } + + @Override + public byte getFamilyLength() { + return getFamilyLength(getFamilyOffset()); + } + + public byte getFamilyLength(int foffset) { + return this.keyOnlyBytes[foffset - 1]; + } + + @Override + public byte[] getQualifierArray() { + return keyOnlyBytes; + } + + @Override + public int getQualifierOffset() { + return getQualifierOffset(getFamilyOffset()); + } + + /** + * @return Qualifier offset + */ + public int getQualifierOffset(int foffset) { + return foffset + getFamilyLength(foffset); + } + + @Override + public int getQualifierLength() { + return getQualifierLength(getRowLength(), getFamilyLength()); + } + + public int getQualifierLength(int rlength, int flength) { + return getKeyLength() + - (KeyValue.KEY_INFRASTRUCTURE_SIZE + rlength + flength); + } + + @Override + public long getTimestamp() { + return getTimestamp(getKeyLength()); + } + + long getTimestamp(final int keylength) { + int tsOffset = getTimestampOffset(keylength); + return Bytes.toLong(this.keyOnlyBytes, tsOffset); + } + + public int getTimestampOffset(final int keylength) { + return getKeyOffset() + keylength - KeyValue.TIMESTAMP_TYPE_SIZE; + } + + @Override + public byte getTypeByte() { + return getType(getKeyLength()); + } + + byte getType(final int keylength) { + return this.keyOnlyBytes[this.offset + keylength - 1]; + } + + @Override + public long getMvccVersion() { + return 0; + } + + @Override + public byte[] getValueArray() { + return this.keyOnlyBytes; + } + + @Override + public int getValueOffset() { + return getKeyOffset() + getKeyLength(); + } + + @Override + public int getValueLength() { + return 0; + } +} Index: hbase-prefix-tree/src/main/java/org/apache/hadoop/hbase/codec/prefixtree/PrefixTreeSeeker.java =================================================================== --- hbase-prefix-tree/src/main/java/org/apache/hadoop/hbase/codec/prefixtree/PrefixTreeSeeker.java (revision 1476730) +++ hbase-prefix-tree/src/main/java/org/apache/hadoop/hbase/codec/prefixtree/PrefixTreeSeeker.java (working copy) @@ -23,6 +23,7 @@ import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.hbase.Cell; import org.apache.hadoop.hbase.CellUtil; +import org.apache.hadoop.hbase.KeyOnlyCell; import org.apache.hadoop.hbase.KeyValue; import org.apache.hadoop.hbase.KeyValueUtil; import org.apache.hadoop.hbase.codec.prefixtree.decode.DecoderFactory; @@ -44,6 +45,8 @@ protected ByteBuffer block; protected boolean includeMvccVersion; protected PrefixTreeArraySearcher ptSearcher; + private boolean needResetCurKV = false; + private KeyValue cur = null; public PrefixTreeSeeker(boolean includeMvccVersion) { this.includeMvccVersion = includeMvccVersion; @@ -91,10 +94,15 @@ */ @Override public KeyValue getKeyValue() { - if (ptSearcher.current() == null) { - return null; + if (needResetCurKV) { + if (ptSearcher.current() != null) { + cur = KeyValueUtil.copyToNewKeyValue(ptSearcher.current()); + } else { + cur = null; + } + needResetCurKV = false; } - return KeyValueUtil.copyToNewKeyValue(ptSearcher.current()); + return cur; } /** @@ -114,16 +122,25 @@ @Override public void rewind() { ptSearcher.positionAtFirstCell(); + expireCurrentKeyValue(); } @Override public boolean next() { - return ptSearcher.advance(); + try { + return ptSearcher.advance(); + } finally { + expireCurrentKeyValue(); + } } // @Override public boolean advance() { + try { return ptSearcher.advance(); + } finally { + expireCurrentKeyValue(); + } } @@ -149,12 +166,16 @@ @Override public int seekToKeyInBlock(byte[] keyOnlyBytes, int offset, int length, boolean forceBeforeOnExactMatch) { - if (USE_POSITION_BEFORE) { - return seekToOrBeforeUsingPositionAtOrBefore(keyOnlyBytes, offset, length, - forceBeforeOnExactMatch); - }else{ - return seekToOrBeforeUsingPositionAtOrAfter(keyOnlyBytes, offset, length, - forceBeforeOnExactMatch); + try { + if (USE_POSITION_BEFORE) { + return seekToOrBeforeUsingPositionAtOrBefore(keyOnlyBytes, offset, + length, forceBeforeOnExactMatch); + } else { + return seekToOrBeforeUsingPositionAtOrAfter(keyOnlyBytes, offset, + length, forceBeforeOnExactMatch); + } + } finally { + expireCurrentKeyValue(); } } @@ -167,10 +188,9 @@ protected int seekToOrBeforeUsingPositionAtOrBefore(byte[] keyOnlyBytes, int offset, int length, boolean seekBefore){ - // this does a deep copy of the key byte[] because the CellSearcher interface wants a Cell - KeyValue kv = KeyValue.createKeyValueFromKey(keyOnlyBytes, offset, length); + Cell cell = new KeyOnlyCell(keyOnlyBytes, offset, length); - CellScannerPosition position = ptSearcher.seekForwardToOrBefore(kv); + CellScannerPosition position = ptSearcher.seekForwardToOrBefore(cell); if(CellScannerPosition.AT == position){ if (seekBefore) { @@ -186,11 +206,10 @@ protected int seekToOrBeforeUsingPositionAtOrAfter(byte[] keyOnlyBytes, int offset, int length, boolean seekBefore){ - // this does a deep copy of the key byte[] because the CellSearcher interface wants a Cell - KeyValue kv = KeyValue.createKeyValueFromKey(keyOnlyBytes, offset, length); + Cell cell = new KeyOnlyCell(keyOnlyBytes, offset, length); //should probably switch this to use the seekForwardToOrBefore method - CellScannerPosition position = ptSearcher.seekForwardToOrAfter(kv); + CellScannerPosition position = ptSearcher.seekForwardToOrAfter(cell); if(CellScannerPosition.AT == position){ if (seekBefore) { @@ -218,4 +237,11 @@ throw new RuntimeException("unexpected CellScannerPosition:"+position); } + /** + * Expire the cache of this seeker's current KeyValue + */ + private void expireCurrentKeyValue() { + needResetCurKV = true; + } + } Index: hbase-common/src/test/java/org/apache/hadoop/hbase/TestKeyOnlyCell.java =================================================================== --- hbase-common/src/test/java/org/apache/hadoop/hbase/TestKeyOnlyCell.java (revision 0) +++ hbase-common/src/test/java/org/apache/hadoop/hbase/TestKeyOnlyCell.java (revision 0) @@ -0,0 +1,76 @@ +/** + * Copyright The Apache Software Foundation + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package org.apache.hadoop.hbase; + +import static org.junit.Assert.assertTrue; + +import org.apache.hadoop.hbase.KeyValue.Type; +import org.apache.hadoop.hbase.util.Bytes; +import org.junit.Test; + +public class TestKeyOnlyCell { + + @Test + public void testKeyComparison() throws Exception { + byte[] row = Bytes.toBytes("testRow"); + byte[] family = Bytes.toBytes("testCf"); + byte[] qualifier = Bytes.toBytes("testCq"); + long ts = 123456; + Type type= Type.Put; + byte[] value = Bytes.toBytes("testValue"); + + // Test general keyvalue + KeyValue testKV = new KeyValue(row, family, qualifier, ts, type, value); + KeyValue keyOnlyKV = KeyValue.createKeyValueFromKey(testKV.getBuffer(), + testKV.getKeyOffset(), testKV.getKeyLength()); + Cell keyOnlyCell = new KeyOnlyCell(testKV.getBuffer(), + testKV.getKeyOffset(), testKV.getKeyLength()); + verifyKey(testKV, keyOnlyKV, keyOnlyCell); + + // Test no-qualifier keyvalue + testKV = new KeyValue(row, family, null, ts, type, value); + keyOnlyKV = KeyValue.createKeyValueFromKey(testKV.getBuffer(), + testKV.getKeyOffset(), testKV.getKeyLength()); + keyOnlyCell = new KeyOnlyCell(testKV.getBuffer(), testKV.getKeyOffset(), + testKV.getKeyLength()); + verifyKey(testKV, keyOnlyKV, keyOnlyCell); + + // Test no-family keyvalue + testKV = new KeyValue(row, null, null, ts, type, value); + keyOnlyKV = KeyValue.createKeyValueFromKey(testKV.getBuffer(), + testKV.getKeyOffset(), testKV.getKeyLength()); + keyOnlyCell = new KeyOnlyCell(testKV.getBuffer(), testKV.getKeyOffset(), + testKV.getKeyLength()); + verifyKey(testKV, keyOnlyKV, keyOnlyCell); + } + + private void verifyKey(Cell expected, Cell actual1, Cell actual2) { + assertTrue(CellComparator.equalsRow(expected, actual1)); + assertTrue(CellComparator.equalsRow(expected, actual2)); + assertTrue(CellComparator.equalsFamily(expected, actual1)); + assertTrue(CellComparator.equalsFamily(expected, actual2)); + assertTrue(CellComparator.equalsQualifier(expected, actual1)); + assertTrue(CellComparator.equalsQualifier(expected, actual2)); + assertTrue(CellComparator.equalsTimestamp(expected, actual1)); + assertTrue(CellComparator.equalsTimestamp(expected, actual2)); + assertTrue(CellComparator.equalsType(expected, actual1)); + assertTrue(CellComparator.equalsType(expected, actual2)); + } + +}