HBASE-8753 Provide new delete flag which can delete all cells under a column-family which have designated timestamp (Honghua)

git-svn-id: https://svn.apache.org/repos/asf/hbase/trunk@1500780 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Zhihong Yu 2013-07-08 14:52:00 +00:00
parent fba901fe9c
commit 0f33c00f1f
12 changed files with 293 additions and 60 deletions

View File

@ -211,6 +211,25 @@ public class Delete extends Mutation implements Comparable<Row> {
return this;
}
/**
* Delete all columns of the specified family with a timestamp equal to
* the specified timestamp.
* @param family family name
* @param timestamp version timestamp
* @return this for invocation chaining
*/
public Delete deleteFamilyVersion(byte [] family, long timestamp) {
List<? extends Cell> list = familyMap.get(family);
if(list == null) {
list = new ArrayList<Cell>();
}
((List<KeyValue>)list).add(new KeyValue(row, family, null, timestamp,
KeyValue.Type.DeleteFamilyVersion));
familyMap.put(family, list);
return this;
}
/**
* Delete all versions of the specified column.
* @param family family name

View File

@ -548,6 +548,8 @@ public final class ProtobufUtil {
delete.deleteColumn(family, qualifier, ts);
} else if (deleteType == DeleteType.DELETE_MULTIPLE_VERSIONS) {
delete.deleteColumns(family, qualifier, ts);
} else if (deleteType == DeleteType.DELETE_FAMILY_VERSION) {
delete.deleteFamilyVersion(family, ts);
} else {
delete.deleteFamily(family, ts);
}
@ -1187,7 +1189,9 @@ public final class ProtobufUtil {
return DeleteType.DELETE_MULTIPLE_VERSIONS;
case DeleteFamily:
return DeleteType.DELETE_FAMILY;
default:
case DeleteFamilyVersion:
return DeleteType.DELETE_FAMILY_VERSION;
default:
throw new IOException("Unknown delete type: " + type);
}
}

View File

@ -205,6 +205,7 @@ public class KeyValue implements Cell, HeapSize, Cloneable {
Put((byte)4),
Delete((byte)8),
DeleteFamilyVersion((byte)10),
DeleteColumn((byte)12),
DeleteFamily((byte)14),
@ -1371,6 +1372,13 @@ public class KeyValue implements Cell, HeapSize, Cloneable {
return getType() == Type.DeleteFamily.getCode();
}
/**
* @return True if this KV is a delete family-version type.
*/
public boolean isDeleteFamilyVersion() {
return getType() == Type.DeleteFamilyVersion.getCode();
}
/**
*
* @return True if this KV is a delete family or column type.

View File

@ -6600,11 +6600,13 @@ public final class ClientProtos {
DELETE_ONE_VERSION(0, 0),
DELETE_MULTIPLE_VERSIONS(1, 1),
DELETE_FAMILY(2, 2),
DELETE_FAMILY_VERSION(3, 3),
;
public static final int DELETE_ONE_VERSION_VALUE = 0;
public static final int DELETE_MULTIPLE_VERSIONS_VALUE = 1;
public static final int DELETE_FAMILY_VALUE = 2;
public static final int DELETE_FAMILY_VERSION_VALUE = 3;
public final int getNumber() { return value; }
@ -6614,6 +6616,7 @@ public final class ClientProtos {
case 0: return DELETE_ONE_VERSION;
case 1: return DELETE_MULTIPLE_VERSIONS;
case 2: return DELETE_FAMILY;
case 3: return DELETE_FAMILY_VERSION;
default: return null;
}
}
@ -6644,7 +6647,7 @@ public final class ClientProtos {
}
private static final DeleteType[] VALUES = {
DELETE_ONE_VERSION, DELETE_MULTIPLE_VERSIONS, DELETE_FAMILY,
DELETE_ONE_VERSION, DELETE_MULTIPLE_VERSIONS, DELETE_FAMILY, DELETE_FAMILY_VERSION,
};
public static DeleteType valueOf(
@ -21576,7 +21579,7 @@ public final class ClientProtos {
"\177\n\tCondition\022\013\n\003row\030\001 \002(\014\022\016\n\006family\030\002 \002(",
"\014\022\021\n\tqualifier\030\003 \002(\014\022!\n\013compareType\030\004 \002(" +
"\0162\014.CompareType\022\037\n\ncomparator\030\005 \002(\0132\013.Co" +
"mparator\"\365\005\n\rMutationProto\022\013\n\003row\030\001 \001(\014\022" +
"mparator\"\220\006\n\rMutationProto\022\013\n\003row\030\001 \001(\014\022" +
"/\n\nmutateType\030\002 \001(\0162\033.MutationProto.Muta" +
"tionType\022/\n\013columnValue\030\003 \003(\0132\032.Mutation" +
"Proto.ColumnValue\022\021\n\ttimestamp\030\004 \001(\004\022!\n\t" +
@ -21593,61 +21596,61 @@ public final class ClientProtos {
"\014\n\010SKIP_WAL\020\001\022\r\n\tASYNC_WAL\020\002\022\014\n\010SYNC_WAL" +
"\020\003\022\r\n\tFSYNC_WAL\020\004\">\n\014MutationType\022\n\n\006APP" +
"END\020\000\022\r\n\tINCREMENT\020\001\022\007\n\003PUT\020\002\022\n\n\006DELETE\020" +
"\003\"U\n\nDeleteType\022\026\n\022DELETE_ONE_VERSION\020\000\022",
"\003\"p\n\nDeleteType\022\026\n\022DELETE_ONE_VERSION\020\000\022",
"\034\n\030DELETE_MULTIPLE_VERSIONS\020\001\022\021\n\rDELETE_" +
"FAMILY\020\002\"r\n\rMutateRequest\022 \n\006region\030\001 \002(" +
"\0132\020.RegionSpecifier\022 \n\010mutation\030\002 \002(\0132\016." +
"MutationProto\022\035\n\tcondition\030\003 \001(\0132\n.Condi" +
"tion\"<\n\016MutateResponse\022\027\n\006result\030\001 \001(\0132\007" +
".Result\022\021\n\tprocessed\030\002 \001(\010\"\362\002\n\004Scan\022\027\n\006c" +
"olumn\030\001 \003(\0132\007.Column\022!\n\tattribute\030\002 \003(\0132" +
"\016.NameBytesPair\022\020\n\010startRow\030\003 \001(\014\022\017\n\007sto" +
"pRow\030\004 \001(\014\022\027\n\006filter\030\005 \001(\0132\007.Filter\022\035\n\tt" +
"imeRange\030\006 \001(\0132\n.TimeRange\022\026\n\013maxVersion",
"s\030\007 \001(\r:\0011\022\031\n\013cacheBlocks\030\010 \001(\010:\004true\022\021\n" +
"\tbatchSize\030\t \001(\r\022\025\n\rmaxResultSize\030\n \001(\004\022" +
"\022\n\nstoreLimit\030\013 \001(\r\022\023\n\013storeOffset\030\014 \001(\r" +
"\022\"\n\032loadColumnFamiliesOnDemand\030\r \001(\010\022\024\n\014" +
"cachingCount\030\016 \001(\r\022\023\n\013prefetching\030\017 \001(\010\"" +
"\230\001\n\013ScanRequest\022 \n\006region\030\001 \001(\0132\020.Region" +
"Specifier\022\023\n\004scan\030\002 \001(\0132\005.Scan\022\021\n\tscanne" +
"rId\030\003 \001(\004\022\024\n\014numberOfRows\030\004 \001(\r\022\024\n\014close" +
"Scanner\030\005 \001(\010\022\023\n\013nextCallSeq\030\006 \001(\004\"l\n\014Sc" +
"anResponse\022\'\n\016resultCellMeta\030\001 \001(\0132\017.Res",
"ultCellMeta\022\021\n\tscannerId\030\002 \001(\004\022\023\n\013moreRe" +
"sults\030\003 \001(\010\022\013\n\003ttl\030\004 \001(\r\"%\n\016ResultCellMe" +
"ta\022\023\n\013cellsLength\030\001 \003(\r\"\260\001\n\024BulkLoadHFil" +
"eRequest\022 \n\006region\030\001 \002(\0132\020.RegionSpecifi" +
"er\0224\n\nfamilyPath\030\002 \003(\0132 .BulkLoadHFileRe" +
"quest.FamilyPath\022\024\n\014assignSeqNum\030\003 \001(\010\032*" +
"\n\nFamilyPath\022\016\n\006family\030\001 \002(\014\022\014\n\004path\030\002 \002" +
"(\t\"\'\n\025BulkLoadHFileResponse\022\016\n\006loaded\030\001 " +
"\002(\010\"_\n\026CoprocessorServiceCall\022\013\n\003row\030\001 \002" +
"(\014\022\023\n\013serviceName\030\002 \002(\t\022\022\n\nmethodName\030\003 ",
"\002(\t\022\017\n\007request\030\004 \002(\014\"d\n\031CoprocessorServi" +
"ceRequest\022 \n\006region\030\001 \002(\0132\020.RegionSpecif" +
"ier\022%\n\004call\030\002 \002(\0132\027.CoprocessorServiceCa" +
"ll\"]\n\032CoprocessorServiceResponse\022 \n\006regi" +
"on\030\001 \002(\0132\020.RegionSpecifier\022\035\n\005value\030\002 \002(" +
"\0132\016.NameBytesPair\"B\n\013MultiAction\022 \n\010muta" +
"tion\030\001 \001(\0132\016.MutationProto\022\021\n\003get\030\002 \001(\0132" +
"\004.Get\"I\n\014ActionResult\022\026\n\005value\030\001 \001(\0132\007.R" +
"esult\022!\n\texception\030\002 \001(\0132\016.NameBytesPair" +
"\"^\n\014MultiRequest\022 \n\006region\030\001 \002(\0132\020.Regio",
"nSpecifier\022\034\n\006action\030\002 \003(\0132\014.MultiAction" +
"\022\016\n\006atomic\030\003 \001(\010\".\n\rMultiResponse\022\035\n\006res" +
"ult\030\001 \003(\0132\r.ActionResult2\342\002\n\rClientServi" +
"ce\022 \n\003get\022\013.GetRequest\032\014.GetResponse\022/\n\010" +
"multiGet\022\020.MultiGetRequest\032\021.MultiGetRes" +
"ponse\022)\n\006mutate\022\016.MutateRequest\032\017.Mutate" +
"Response\022#\n\004scan\022\014.ScanRequest\032\r.ScanRes" +
"ponse\022>\n\rbulkLoadHFile\022\025.BulkLoadHFileRe" +
"quest\032\026.BulkLoadHFileResponse\022F\n\013execSer" +
"vice\022\032.CoprocessorServiceRequest\032\033.Copro",
"cessorServiceResponse\022&\n\005multi\022\r.MultiRe" +
"quest\032\016.MultiResponseBB\n*org.apache.hado" +
"op.hbase.protobuf.generatedB\014ClientProto" +
"sH\001\210\001\001\240\001\001"
"FAMILY\020\002\022\031\n\025DELETE_FAMILY_VERSION\020\003\"r\n\rM" +
"utateRequest\022 \n\006region\030\001 \002(\0132\020.RegionSpe" +
"cifier\022 \n\010mutation\030\002 \002(\0132\016.MutationProto" +
"\022\035\n\tcondition\030\003 \001(\0132\n.Condition\"<\n\016Mutat" +
"eResponse\022\027\n\006result\030\001 \001(\0132\007.Result\022\021\n\tpr" +
"ocessed\030\002 \001(\010\"\362\002\n\004Scan\022\027\n\006column\030\001 \003(\0132\007" +
".Column\022!\n\tattribute\030\002 \003(\0132\016.NameBytesPa" +
"ir\022\020\n\010startRow\030\003 \001(\014\022\017\n\007stopRow\030\004 \001(\014\022\027\n" +
"\006filter\030\005 \001(\0132\007.Filter\022\035\n\ttimeRange\030\006 \001(",
"\0132\n.TimeRange\022\026\n\013maxVersions\030\007 \001(\r:\0011\022\031\n" +
"\013cacheBlocks\030\010 \001(\010:\004true\022\021\n\tbatchSize\030\t " +
"\001(\r\022\025\n\rmaxResultSize\030\n \001(\004\022\022\n\nstoreLimit" +
"\030\013 \001(\r\022\023\n\013storeOffset\030\014 \001(\r\022\"\n\032loadColum" +
"nFamiliesOnDemand\030\r \001(\010\022\024\n\014cachingCount\030" +
"\016 \001(\r\022\023\n\013prefetching\030\017 \001(\010\"\230\001\n\013ScanReque" +
"st\022 \n\006region\030\001 \001(\0132\020.RegionSpecifier\022\023\n\004" +
"scan\030\002 \001(\0132\005.Scan\022\021\n\tscannerId\030\003 \001(\004\022\024\n\014" +
"numberOfRows\030\004 \001(\r\022\024\n\014closeScanner\030\005 \001(\010" +
"\022\023\n\013nextCallSeq\030\006 \001(\004\"l\n\014ScanResponse\022\'\n",
"\016resultCellMeta\030\001 \001(\0132\017.ResultCellMeta\022\021" +
"\n\tscannerId\030\002 \001(\004\022\023\n\013moreResults\030\003 \001(\010\022\013" +
"\n\003ttl\030\004 \001(\r\"%\n\016ResultCellMeta\022\023\n\013cellsLe" +
"ngth\030\001 \003(\r\"\260\001\n\024BulkLoadHFileRequest\022 \n\006r" +
"egion\030\001 \002(\0132\020.RegionSpecifier\0224\n\nfamilyP" +
"ath\030\002 \003(\0132 .BulkLoadHFileRequest.FamilyP" +
"ath\022\024\n\014assignSeqNum\030\003 \001(\010\032*\n\nFamilyPath\022" +
"\016\n\006family\030\001 \002(\014\022\014\n\004path\030\002 \002(\t\"\'\n\025BulkLoa" +
"dHFileResponse\022\016\n\006loaded\030\001 \002(\010\"_\n\026Coproc" +
"essorServiceCall\022\013\n\003row\030\001 \002(\014\022\023\n\013service",
"Name\030\002 \002(\t\022\022\n\nmethodName\030\003 \002(\t\022\017\n\007reques" +
"t\030\004 \002(\014\"d\n\031CoprocessorServiceRequest\022 \n\006" +
"region\030\001 \002(\0132\020.RegionSpecifier\022%\n\004call\030\002" +
" \002(\0132\027.CoprocessorServiceCall\"]\n\032Coproce" +
"ssorServiceResponse\022 \n\006region\030\001 \002(\0132\020.Re" +
"gionSpecifier\022\035\n\005value\030\002 \002(\0132\016.NameBytes" +
"Pair\"B\n\013MultiAction\022 \n\010mutation\030\001 \001(\0132\016." +
"MutationProto\022\021\n\003get\030\002 \001(\0132\004.Get\"I\n\014Acti" +
"onResult\022\026\n\005value\030\001 \001(\0132\007.Result\022!\n\texce" +
"ption\030\002 \001(\0132\016.NameBytesPair\"^\n\014MultiRequ",
"est\022 \n\006region\030\001 \002(\0132\020.RegionSpecifier\022\034\n" +
"\006action\030\002 \003(\0132\014.MultiAction\022\016\n\006atomic\030\003 " +
"\001(\010\".\n\rMultiResponse\022\035\n\006result\030\001 \003(\0132\r.A" +
"ctionResult2\342\002\n\rClientService\022 \n\003get\022\013.G" +
"etRequest\032\014.GetResponse\022/\n\010multiGet\022\020.Mu" +
"ltiGetRequest\032\021.MultiGetResponse\022)\n\006muta" +
"te\022\016.MutateRequest\032\017.MutateResponse\022#\n\004s" +
"can\022\014.ScanRequest\032\r.ScanResponse\022>\n\rbulk" +
"LoadHFile\022\025.BulkLoadHFileRequest\032\026.BulkL" +
"oadHFileResponse\022F\n\013execService\022\032.Coproc",
"essorServiceRequest\032\033.CoprocessorService" +
"Response\022&\n\005multi\022\r.MultiRequest\032\016.Multi" +
"ResponseBB\n*org.apache.hadoop.hbase.prot" +
"obuf.generatedB\014ClientProtosH\001\210\001\001\240\001\001"
};
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {

View File

@ -175,6 +175,7 @@ message MutationProto {
DELETE_ONE_VERSION = 0;
DELETE_MULTIPLE_VERSIONS = 1;
DELETE_FAMILY = 2;
DELETE_FAMILY_VERSION = 3;
}
message ColumnValue {

View File

@ -104,6 +104,7 @@ public interface DeleteTracker {
*/
public static enum DeleteResult {
FAMILY_DELETED, // The KeyValue is deleted by a delete family.
FAMILY_VERSION_DELETED, // The KeyValue is deleted by a delete family version.
COLUMN_DELETED, // The KeyValue is deleted by a delete column.
VERSION_DELETED, // The KeyValue is deleted by a version delete.
NOT_DELETED

View File

@ -19,6 +19,9 @@
package org.apache.hadoop.hbase.regionserver;
import java.util.SortedSet;
import java.util.TreeSet;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.util.Bytes;
@ -43,6 +46,7 @@ public class ScanDeleteTracker implements DeleteTracker {
private boolean hasFamilyStamp = false;
private long familyStamp = 0L;
private SortedSet<Long> familyVersionStamps = new TreeSet<Long>();
private byte [] deleteBuffer = null;
private int deleteOffset = 0;
private int deleteLength = 0;
@ -75,6 +79,9 @@ public class ScanDeleteTracker implements DeleteTracker {
hasFamilyStamp = true;
familyStamp = timestamp;
return;
} else if (type == KeyValue.Type.DeleteFamilyVersion.getCode()) {
familyVersionStamps.add(timestamp);
return;
}
if (deleteBuffer != null && type < deleteType) {
@ -111,6 +118,10 @@ public class ScanDeleteTracker implements DeleteTracker {
return DeleteResult.FAMILY_DELETED;
}
if (familyVersionStamps.contains(Long.valueOf(timestamp))) {
return DeleteResult.FAMILY_VERSION_DELETED;
}
if (deleteBuffer != null) {
int ret = Bytes.compareTo(deleteBuffer, deleteOffset, deleteLength,
buffer, qualifierOffset, qualifierLength);
@ -146,7 +157,8 @@ public class ScanDeleteTracker implements DeleteTracker {
@Override
public boolean isEmpty() {
return deleteBuffer == null && !hasFamilyStamp;
return deleteBuffer == null && !hasFamilyStamp &&
familyVersionStamps.isEmpty();
}
@Override
@ -154,6 +166,7 @@ public class ScanDeleteTracker implements DeleteTracker {
public void reset() {
hasFamilyStamp = false;
familyStamp = 0L;
familyVersionStamps.clear();
deleteBuffer = null;
}

View File

@ -352,6 +352,7 @@ public class ScanQueryMatcher {
case COLUMN_DELETED:
return columns.getNextRowOrNextColumn(bytes, offset, qualLength);
case VERSION_DELETED:
case FAMILY_VERSION_DELETED:
return MatchCode.SKIP;
case NOT_DELETED:
break;

View File

@ -950,7 +950,7 @@ public class StoreFile {
private void appendDeleteFamilyBloomFilter(final KeyValue kv)
throws IOException {
if (!kv.isDeleteFamily()) {
if (!kv.isDeleteFamily() && !kv.isDeleteFamilyVersion()) {
return;
}

View File

@ -292,7 +292,7 @@ public class StoreFileScanner implements KeyValueScanner {
kv.getRowOffset(), kv.getRowLength(), kv.getBuffer(),
kv.getQualifierOffset(), kv.getQualifierLength());
} else if (this.matcher != null && !matcher.hasNullColumnInQuery() &&
kv.isDeleteFamily()) {
(kv.isDeleteFamily() || kv.isDeleteFamilyVersion())) {
// if there is no such delete family kv in the store file,
// then no need to seek.
haveToSeek = reader.passesDeleteFamilyBloomFilter(kv.getBuffer(),

View File

@ -1722,6 +1722,160 @@ public class TestFromClientSide {
}
@Test
public void testDeleteFamilyVersion() throws Exception {
HBaseAdmin admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
byte [] TABLE = Bytes.toBytes("testDeleteFamilyVersion");
byte [][] QUALIFIERS = makeNAscii(QUALIFIER, 1);
byte [][] VALUES = makeN(VALUE, 5);
long [] ts = {1000, 2000, 3000, 4000, 5000};
HTable ht = TEST_UTIL.createTable(TABLE, FAMILY, 5);
Put put = new Put(ROW);
for (int q = 0; q < 1; q++)
for (int t = 0; t < 5; t++)
put.add(FAMILY, QUALIFIERS[q], ts[t], VALUES[t]);
ht.put(put);
admin.flush(TABLE);
Delete delete = new Delete(ROW);
delete.deleteFamilyVersion(FAMILY, ts[1]); // delete version '2000'
delete.deleteFamilyVersion(FAMILY, ts[3]); // delete version '4000'
ht.delete(delete);
admin.flush(TABLE);
for (int i = 0; i < 1; i++) {
Get get = new Get(ROW);
get.addColumn(FAMILY, QUALIFIERS[i]);
get.setMaxVersions(Integer.MAX_VALUE);
Result result = ht.get(get);
// verify version '1000'/'3000'/'5000' remains for all columns
assertNResult(result, ROW, FAMILY, QUALIFIERS[i],
new long [] {ts[0], ts[2], ts[4]},
new byte[][] {VALUES[0], VALUES[2], VALUES[4]},
0, 2);
}
ht.close();
admin.close();
}
@Test
public void testDeleteFamilyVersionWithOtherDeletes() throws Exception {
byte [] TABLE = Bytes.toBytes("testDeleteFamilyVersionWithOtherDeletes");
byte [][] QUALIFIERS = makeNAscii(QUALIFIER, 5);
byte [][] VALUES = makeN(VALUE, 5);
long [] ts = {1000, 2000, 3000, 4000, 5000};
HBaseAdmin admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
HTable ht = TEST_UTIL.createTable(TABLE, FAMILY, 5);
Put put = null;
Result result = null;
Get get = null;
Delete delete = null;
// 1. put on ROW
put = new Put(ROW);
for (int q = 0; q < 5; q++)
for (int t = 0; t < 5; t++)
put.add(FAMILY, QUALIFIERS[q], ts[t], VALUES[t]);
ht.put(put);
admin.flush(TABLE);
// 2. put on ROWS[0]
byte [] ROW2 = Bytes.toBytes("myRowForTest");
put = new Put(ROW2);
for (int q = 0; q < 5; q++)
for (int t = 0; t < 5; t++)
put.add(FAMILY, QUALIFIERS[q], ts[t], VALUES[t]);
ht.put(put);
admin.flush(TABLE);
// 3. delete on ROW
delete = new Delete(ROW);
// delete version <= 2000 of all columns
// note: deleteFamily must be the first since it will mask
// the subsequent other type deletes!
delete.deleteFamily(FAMILY, ts[1]);
// delete version '4000' of all columns
delete.deleteFamilyVersion(FAMILY, ts[3]);
// delete version <= 3000 of column 0
delete.deleteColumns(FAMILY, QUALIFIERS[0], ts[2]);
// delete version <= 5000 of column 2
delete.deleteColumns(FAMILY, QUALIFIERS[2], ts[4]);
// delete version 5000 of column 4
delete.deleteColumn(FAMILY, QUALIFIERS[4], ts[4]);
ht.delete(delete);
admin.flush(TABLE);
// 4. delete on ROWS[0]
delete = new Delete(ROW2);
delete.deleteFamilyVersion(FAMILY, ts[1]); // delete version '2000'
delete.deleteFamilyVersion(FAMILY, ts[3]); // delete version '4000'
ht.delete(delete);
admin.flush(TABLE);
// 5. check ROW
get = new Get(ROW);
get.addColumn(FAMILY, QUALIFIERS[0]);
get.setMaxVersions(Integer.MAX_VALUE);
result = ht.get(get);
assertNResult(result, ROW, FAMILY, QUALIFIERS[0],
new long [] {ts[4]},
new byte[][] {VALUES[4]},
0, 0);
get = new Get(ROW);
get.addColumn(FAMILY, QUALIFIERS[1]);
get.setMaxVersions(Integer.MAX_VALUE);
result = ht.get(get);
assertNResult(result, ROW, FAMILY, QUALIFIERS[1],
new long [] {ts[2], ts[4]},
new byte[][] {VALUES[2], VALUES[4]},
0, 1);
get = new Get(ROW);
get.addColumn(FAMILY, QUALIFIERS[2]);
get.setMaxVersions(Integer.MAX_VALUE);
result = ht.get(get);
assertEquals(0, result.size());
get = new Get(ROW);
get.addColumn(FAMILY, QUALIFIERS[3]);
get.setMaxVersions(Integer.MAX_VALUE);
result = ht.get(get);
assertNResult(result, ROW, FAMILY, QUALIFIERS[3],
new long [] {ts[2], ts[4]},
new byte[][] {VALUES[2], VALUES[4]},
0, 1);
get = new Get(ROW);
get.addColumn(FAMILY, QUALIFIERS[4]);
get.setMaxVersions(Integer.MAX_VALUE);
result = ht.get(get);
assertNResult(result, ROW, FAMILY, QUALIFIERS[4],
new long [] {ts[2]},
new byte[][] {VALUES[2]},
0, 0);
// 6. check ROWS[0]
for (int i = 0; i < 5; i++) {
get = new Get(ROW2);
get.addColumn(FAMILY, QUALIFIERS[i]);
get.setMaxVersions(Integer.MAX_VALUE);
result = ht.get(get);
// verify version '1000'/'3000'/'5000' remains for all columns
assertNResult(result, ROW2, FAMILY, QUALIFIERS[i],
new long [] {ts[0], ts[2], ts[4]},
new byte[][] {VALUES[0], VALUES[2], VALUES[4]},
0, 2);
}
ht.close();
admin.close();
}
@Test
public void testDeletes() throws Exception {
byte [] TABLE = Bytes.toBytes("testDeletes");

View File

@ -67,6 +67,35 @@ public class TestScanDeleteTracker extends HBaseTestCase {
assertEquals(DeleteResult.FAMILY_DELETED, ret);
}
public void testDeletedBy_DeleteFamilyVersion() {
byte [] qualifier1 = Bytes.toBytes("qualifier1");
byte [] qualifier2 = Bytes.toBytes("qualifier2");
byte [] qualifier3 = Bytes.toBytes("qualifier3");
byte [] qualifier4 = Bytes.toBytes("qualifier4");
deleteType = KeyValue.Type.DeleteFamilyVersion.getCode();
sdt.add(null, 0, 0, timestamp, deleteType);
DeleteResult ret = sdt.isDeleted(qualifier1, 0, qualifier1.length, timestamp);
assertEquals(DeleteResult.FAMILY_VERSION_DELETED, ret);
ret = sdt.isDeleted(qualifier2, 0, qualifier2.length, timestamp);
assertEquals(DeleteResult.FAMILY_VERSION_DELETED, ret);
ret = sdt.isDeleted(qualifier3, 0, qualifier3.length, timestamp);
assertEquals(DeleteResult.FAMILY_VERSION_DELETED, ret);
ret = sdt.isDeleted(qualifier4, 0, qualifier4.length, timestamp);
assertEquals(DeleteResult.FAMILY_VERSION_DELETED, ret);
ret = sdt.isDeleted(qualifier1, 0, qualifier1.length, timestamp + 3);
assertEquals(DeleteResult.NOT_DELETED, ret);
ret = sdt.isDeleted(qualifier2, 0, qualifier2.length, timestamp - 2);
assertEquals(DeleteResult.NOT_DELETED, ret);
ret = sdt.isDeleted(qualifier3, 0, qualifier3.length, timestamp - 5);
assertEquals(DeleteResult.NOT_DELETED, ret);
ret = sdt.isDeleted(qualifier4, 0, qualifier4.length, timestamp + 8);
assertEquals(DeleteResult.NOT_DELETED, ret);
}
public void testDelete_DeleteColumn() {
byte [] qualifier = Bytes.toBytes("qualifier");
deleteType = KeyValue.Type.Delete.getCode();