1 /**
2 * Copyright 2010 The Apache Software Foundation
3 *
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21 package org.apache.hadoop.hbase.regionserver;
22
23 import org.apache.hadoop.hbase.KeyValue;
24 import org.apache.hadoop.hbase.util.Bytes;
25
26 /**
27 * This class is responsible for the tracking and enforcement of Deletes
28 * during the course of a Scan operation.
29 *
30 * It only has to enforce Delete and DeleteColumn, since the
31 * DeleteFamily is handled at a higher level.
32 *
33 * <p>
34 * This class is utilized through three methods:
35 * <ul><li>{@link #add} when encountering a Delete or DeleteColumn
36 * <li>{@link #isDeleted} when checking if a Put KeyValue has been deleted
37 * <li>{@link #update} when reaching the end of a StoreFile or row for scans
38 * <p>
39 * This class is NOT thread-safe as queries are never multi-threaded
40 */
41 public class ScanDeleteTracker implements DeleteTracker {
42
43 private long familyStamp = -1L;
44 private byte [] deleteBuffer = null;
45 private int deleteOffset = 0;
46 private int deleteLength = 0;
47 private byte deleteType = 0;
48 private long deleteTimestamp = 0L;
49
50 /**
51 * Constructor for ScanDeleteTracker
52 */
53 public ScanDeleteTracker() {
54 super();
55 }
56
57 /**
58 * Add the specified KeyValue to the list of deletes to check against for
59 * this row operation.
60 * <p>
61 * This is called when a Delete is encountered in a StoreFile.
62 * @param buffer KeyValue buffer
63 * @param qualifierOffset column qualifier offset
64 * @param qualifierLength column qualifier length
65 * @param timestamp timestamp
66 * @param type delete type as byte
67 */
68 @Override
69 public void add(byte[] buffer, int qualifierOffset, int qualifierLength,
70 long timestamp, byte type) {
71 if (timestamp > familyStamp) {
72 if (type == KeyValue.Type.DeleteFamily.getCode()) {
73 familyStamp = timestamp;
74 return;
75 }
76
77 if (deleteBuffer != null && type < deleteType) {
78 // same column, so ignore less specific delete
79 if (Bytes.compareTo(deleteBuffer, deleteOffset, deleteLength,
80 buffer, qualifierOffset, qualifierLength) == 0){
81 return;
82 }
83 }
84 // new column, or more general delete type
85 deleteBuffer = buffer;
86 deleteOffset = qualifierOffset;
87 deleteLength = qualifierLength;
88 deleteType = type;
89 deleteTimestamp = timestamp;
90 }
91 // missing else is never called.
92 }
93
94 /**
95 * Check if the specified KeyValue buffer has been deleted by a previously
96 * seen delete.
97 *
98 * @param buffer KeyValue buffer
99 * @param qualifierOffset column qualifier offset
100 * @param qualifierLength column qualifier length
101 * @param timestamp timestamp
102 * @return true is the specified KeyValue is deleted, false if not
103 */
104 @Override
105 public boolean isDeleted(byte [] buffer, int qualifierOffset,
106 int qualifierLength, long timestamp) {
107 if (timestamp <= familyStamp) {
108 return true;
109 }
110
111 if (deleteBuffer != null) {
112 int ret = Bytes.compareTo(deleteBuffer, deleteOffset, deleteLength,
113 buffer, qualifierOffset, qualifierLength);
114
115 if (ret == 0) {
116 if (deleteType == KeyValue.Type.DeleteColumn.getCode()) {
117 return true;
118 }
119 // Delete (aka DeleteVersion)
120 // If the timestamp is the same, keep this one
121 if (timestamp == deleteTimestamp) {
122 return true;
123 }
124 // use assert or not?
125 assert timestamp < deleteTimestamp;
126
127 // different timestamp, let's clear the buffer.
128 deleteBuffer = null;
129 } else if(ret < 0){
130 // Next column case.
131 deleteBuffer = null;
132 } else {
133 //Should never happen, throw Exception
134 }
135 }
136
137 return false;
138 }
139
140 @Override
141 public boolean isEmpty() {
142 return deleteBuffer == null && familyStamp == 0;
143 }
144
145 @Override
146 // called between every row.
147 public void reset() {
148 familyStamp = 0L;
149 deleteBuffer = null;
150 }
151
152 @Override
153 // should not be called at all even (!)
154 public void update() {
155 this.reset();
156 }
157 }