/** * Perform a binary lookup on the list of KeyRange for the tightest slot such that the slotBound * of the current slot is higher or equal than the slotBound of our range. * * @return the index of the slot whose slot bound equals or are the tightest one that is smaller * than rangeBound of range, or slots.length if no bound can be found. */ public static int searchClosestKeyRangeWithUpperHigherThanPtr( List<KeyRange> slots, ImmutableBytesWritable ptr, int lower, Field field) { int upper = slots.size() - 1; int mid; BytesComparator comparator = ScanUtil.getComparator(field.getDataType().isFixedWidth(), field.getSortOrder()); while (lower <= upper) { mid = (lower + upper) / 2; int cmp = slots.get(mid).compareUpperToLowerBound(ptr, true, comparator); if (cmp < 0) { lower = mid + 1; } else if (cmp > 0) { upper = mid - 1; } else { return mid; } } mid = (lower + upper) / 2; if (mid == 0 && slots.get(mid).compareUpperToLowerBound(ptr, true, comparator) > 0) { return mid; } else { return ++mid; } }
public static ScanRanges create( RowKeySchema schema, List<List<KeyRange>> ranges, int[] slotSpan, KeyRange minMaxRange, Integer nBuckets, boolean useSkipScan, int rowTimestampColIndex) { int offset = nBuckets == null ? 0 : SaltingUtil.NUM_SALTING_BYTES; int nSlots = ranges.size(); if (nSlots == offset && minMaxRange == KeyRange.EVERYTHING_RANGE) { return EVERYTHING; } else if (minMaxRange == KeyRange.EMPTY_RANGE || (nSlots == 1 + offset && ranges.get(offset).size() == 1 && ranges.get(offset).get(0) == KeyRange.EMPTY_RANGE)) { return NOTHING; } TimeRange rowTimestampRange = getRowTimestampColumnRange(ranges, schema, rowTimestampColIndex); boolean isPointLookup = isPointLookup(schema, ranges, slotSpan, useSkipScan); if (isPointLookup) { // TODO: consider keeping original to use for serialization as it would be smaller? List<byte[]> keys = ScanRanges.getPointKeys(ranges, slotSpan, schema, nBuckets); List<KeyRange> keyRanges = Lists.newArrayListWithExpectedSize(keys.size()); KeyRange unsaltedMinMaxRange = minMaxRange; if (nBuckets != null && minMaxRange != KeyRange.EVERYTHING_RANGE) { unsaltedMinMaxRange = KeyRange.getKeyRange( stripPrefix(minMaxRange.getLowerRange(), offset), minMaxRange.lowerUnbound(), stripPrefix(minMaxRange.getUpperRange(), offset), minMaxRange.upperUnbound()); } // We have full keys here, so use field from our varbinary schema BytesComparator comparator = ScanUtil.getComparator(SchemaUtil.VAR_BINARY_SCHEMA.getField(0)); for (byte[] key : keys) { // Filter now based on unsalted minMaxRange and ignore the point key salt byte if (unsaltedMinMaxRange.compareLowerToUpperBound( key, offset, key.length - offset, true, comparator) <= 0 && unsaltedMinMaxRange.compareUpperToLowerBound( key, offset, key.length - offset, true, comparator) >= 0) { keyRanges.add(KeyRange.getKeyRange(key)); } } ranges = Collections.singletonList(keyRanges); useSkipScan = keyRanges.size() > 1; // Treat as binary if descending because we've got a separator byte at the end // which is not part of the value. if (keys.size() > 1 || SchemaUtil.getSeparatorByte(schema.rowKeyOrderOptimizable(), false, schema.getField(0)) == QueryConstants.DESC_SEPARATOR_BYTE) { schema = SchemaUtil.VAR_BINARY_SCHEMA; slotSpan = ScanUtil.SINGLE_COLUMN_SLOT_SPAN; } else { // Keep original schema and don't use skip scan as it's not necessary // when there's a single key. slotSpan = new int[] {schema.getMaxFields() - 1}; } } List<List<KeyRange>> sortedRanges = Lists.newArrayListWithExpectedSize(ranges.size()); for (int i = 0; i < ranges.size(); i++) { List<KeyRange> sorted = Lists.newArrayList(ranges.get(i)); Collections.sort(sorted, KeyRange.COMPARATOR); sortedRanges.add(ImmutableList.copyOf(sorted)); } // Don't set minMaxRange for point lookup because it causes issues during intersect // by going across region boundaries KeyRange scanRange = KeyRange.EVERYTHING_RANGE; // if (!isPointLookup && (nBuckets == null || !useSkipScanFilter)) { // if (! ( isPointLookup || (nBuckets != null && useSkipScanFilter) ) ) { // if (nBuckets == null || (nBuckets != null && (!isPointLookup || !useSkipScanFilter))) { if (nBuckets == null || !isPointLookup || !useSkipScan) { byte[] minKey = ScanUtil.getMinKey(schema, sortedRanges, slotSpan); byte[] maxKey = ScanUtil.getMaxKey(schema, sortedRanges, slotSpan); // If the maxKey has crossed the salt byte boundary, then we do not // have anything to filter at the upper end of the range if (ScanUtil.crossesPrefixBoundary(maxKey, ScanUtil.getPrefix(minKey, offset), offset)) { maxKey = KeyRange.UNBOUND; } // We won't filter anything at the low end of the range if we just have the salt byte if (minKey.length <= offset) { minKey = KeyRange.UNBOUND; } scanRange = KeyRange.getKeyRange(minKey, maxKey); } if (minMaxRange != KeyRange.EVERYTHING_RANGE) { minMaxRange = ScanUtil.convertToInclusiveExclusiveRange( minMaxRange, schema, new ImmutableBytesWritable()); scanRange = scanRange.intersect(minMaxRange); } if (scanRange == KeyRange.EMPTY_RANGE) { return NOTHING; } return new ScanRanges( schema, slotSpan, sortedRanges, scanRange, minMaxRange, useSkipScan, isPointLookup, nBuckets, rowTimestampRange); }