Skip to content

Commit cd0b29c

Browse files
authored
HBASE-28626 MultiRowRangeFilter deserialization fails in org.apache.h… (#5951)
Signed-off-by: Ankit Singhal <ankit@apache.org>
1 parent c4a7606 commit cd0b29c

File tree

2 files changed

+97
-4
lines changed

2 files changed

+97
-4
lines changed

hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/model/ScannerModel.java

Lines changed: 82 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@
2121
import java.io.IOException;
2222
import java.io.Serializable;
2323
import java.util.ArrayList;
24+
import java.util.Arrays;
2425
import java.util.Base64;
2526
import java.util.List;
2627
import java.util.Map;
2728
import java.util.NavigableSet;
29+
import java.util.Objects;
2830
import javax.xml.bind.annotation.XmlAttribute;
2931
import javax.xml.bind.annotation.XmlElement;
3032
import javax.xml.bind.annotation.XmlRootElement;
@@ -203,6 +205,79 @@ public ByteArrayComparable build() {
203205

204206
}
205207

208+
/**
209+
* This DTO omits the pseudo-getters in MultiRowRangeFilter.RowRange which break Jackson
210+
* deserialization. It also avoids adding those as dummy JSON elements.
211+
*/
212+
static class RowRangeModel {
213+
214+
protected byte[] startRow;
215+
216+
protected boolean startRowInclusive = true;
217+
218+
protected byte[] stopRow;
219+
220+
protected boolean stopRowInclusive = false;
221+
222+
public RowRangeModel() {
223+
}
224+
225+
public RowRangeModel(MultiRowRangeFilter.RowRange rr) {
226+
this.startRow = rr.getStartRow();
227+
this.startRowInclusive = rr.isStartRowInclusive();
228+
this.stopRow = rr.getStopRow();
229+
this.stopRowInclusive = rr.isStopRowInclusive();
230+
}
231+
232+
public MultiRowRangeFilter.RowRange build() {
233+
return new MultiRowRangeFilter.RowRange(startRow, startRowInclusive, stopRow,
234+
stopRowInclusive);
235+
}
236+
237+
public byte[] getStartRow() {
238+
return startRow;
239+
}
240+
241+
public byte[] getStopRow() {
242+
return stopRow;
243+
}
244+
245+
/** Returns if start row is inclusive. */
246+
public boolean isStartRowInclusive() {
247+
return startRowInclusive;
248+
}
249+
250+
/** Returns if stop row is inclusive. */
251+
public boolean isStopRowInclusive() {
252+
return stopRowInclusive;
253+
}
254+
255+
@Override
256+
public int hashCode() {
257+
final int prime = 31;
258+
int result = 1;
259+
result = prime * result + Arrays.hashCode(startRow);
260+
result = prime * result + Arrays.hashCode(stopRow);
261+
result = prime * result + Objects.hash(startRowInclusive, stopRowInclusive);
262+
return result;
263+
}
264+
265+
@Override
266+
public boolean equals(Object obj) {
267+
if (this == obj) {
268+
return true;
269+
}
270+
if (!(obj instanceof RowRangeModel)) {
271+
return false;
272+
}
273+
RowRangeModel other = (RowRangeModel) obj;
274+
return Arrays.equals(startRow, other.startRow)
275+
&& startRowInclusive == other.startRowInclusive && Arrays.equals(stopRow, other.stopRow)
276+
&& stopRowInclusive == other.stopRowInclusive;
277+
}
278+
279+
}
280+
206281
// A grab bag of fields, would have been a union if this were C.
207282
// These are null by default and will only be serialized if set (non null).
208283
@XmlAttribute
@@ -242,7 +317,7 @@ public ByteArrayComparable build() {
242317
@XmlElement
243318
public List<String> prefixes;
244319
@XmlElement
245-
private List<RowRange> ranges;
320+
private List<RowRangeModel> ranges;
246321
@XmlElement
247322
public List<Long> timestamps;
248323

@@ -333,8 +408,7 @@ public FilterModel(Filter filter) {
333408
case MultiRowRangeFilter:
334409
this.ranges = new ArrayList<>();
335410
for (RowRange range : ((MultiRowRangeFilter) filter).getRowRanges()) {
336-
this.ranges.add(new RowRange(range.getStartRow(), range.isStartRowInclusive(),
337-
range.getStopRow(), range.isStopRowInclusive()));
411+
this.ranges.add(new RowRangeModel(range));
338412
}
339413
break;
340414
case PageFilter:
@@ -438,7 +512,11 @@ public Filter build() {
438512
}
439513
break;
440514
case MultiRowRangeFilter: {
441-
filter = new MultiRowRangeFilter(ranges);
515+
ArrayList<MultiRowRangeFilter.RowRange> rowRanges = new ArrayList<>(ranges.size());
516+
for (RowRangeModel rangeModel : ranges) {
517+
rowRanges.add(rangeModel.build());
518+
}
519+
filter = new MultiRowRangeFilter(rowRanges);
442520
}
443521
break;
444522
case PageFilter:

hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestScannersWithFilters.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import org.apache.hadoop.hbase.filter.FilterList.Operator;
5353
import org.apache.hadoop.hbase.filter.FirstKeyOnlyFilter;
5454
import org.apache.hadoop.hbase.filter.InclusiveStopFilter;
55+
import org.apache.hadoop.hbase.filter.MultiRowRangeFilter;
5556
import org.apache.hadoop.hbase.filter.PageFilter;
5657
import org.apache.hadoop.hbase.filter.PrefixFilter;
5758
import org.apache.hadoop.hbase.filter.QualifierFilter;
@@ -963,4 +964,18 @@ public void testFirstKeyOnlyFilter() throws Exception {
963964
new KeyValue(ROWS_TWO[3], FAMILIES[0], QUALIFIERS_TWO[0], VALUES[1]) };
964965
verifyScanFull(s, kvs);
965966
}
967+
968+
@Test
969+
public void testMultiRowRangeFilter() throws Exception {
970+
long expectedRows = 2;
971+
long expectedKeys = colsPerRow;
972+
List<MultiRowRangeFilter.RowRange> ranges = new ArrayList<>();
973+
// Both return only the third element, as the second one is deleted during initialization.
974+
ranges.add(new MultiRowRangeFilter.RowRange(ROWS_ONE[1], true, ROWS_ONE[2], true));
975+
ranges.add(new MultiRowRangeFilter.RowRange(ROWS_TWO[0], false, ROWS_TWO[3], false));
976+
977+
Scan s = new Scan();
978+
s.setFilter(new MultiRowRangeFilter(ranges));
979+
verifyScan(s, expectedRows, expectedKeys);
980+
}
966981
}

0 commit comments

Comments
 (0)