public static boolean intersectScanRange( Scan scan, byte[] startKey, byte[] stopKey, boolean useSkipScan) { boolean mayHaveRows = false; int offset = 0; if (ScanUtil.isLocalIndex(scan)) { offset = startKey.length != 0 ? startKey.length : stopKey.length; } byte[] existingStartKey = scan.getStartRow(); byte[] existingStopKey = scan.getStopRow(); if (existingStartKey.length > 0) { if (startKey.length == 0 || Bytes.compareTo(existingStartKey, startKey) > 0) { startKey = existingStartKey; } } else { mayHaveRows = true; } if (existingStopKey.length > 0) { if (stopKey.length == 0 || Bytes.compareTo(existingStopKey, stopKey) < 0) { stopKey = existingStopKey; } } else { mayHaveRows = true; } scan.setStartRow(startKey); scan.setStopRow(stopKey); if (offset > 0 && useSkipScan) { byte[] temp = null; if (startKey.length != 0) { temp = new byte[startKey.length - offset]; System.arraycopy(startKey, offset, temp, 0, startKey.length - offset); startKey = temp; } if (stopKey.length != 0) { temp = new byte[stopKey.length - offset]; System.arraycopy(stopKey, offset, temp, 0, stopKey.length - offset); stopKey = temp; } } mayHaveRows = mayHaveRows || Bytes.compareTo(scan.getStartRow(), scan.getStopRow()) < 0; // If the scan is using skip scan filter, intersect and replace the filter. if (mayHaveRows && useSkipScan) { Filter filter = scan.getFilter(); if (filter instanceof SkipScanFilter) { SkipScanFilter oldFilter = (SkipScanFilter) filter; SkipScanFilter newFilter = oldFilter.intersect(startKey, stopKey); if (newFilter == null) { return false; } // Intersect found: replace skip scan with intersected one scan.setFilter(newFilter); } else if (filter instanceof FilterList) { FilterList oldList = (FilterList) filter; FilterList newList = new FilterList(FilterList.Operator.MUST_PASS_ALL); for (Filter f : oldList.getFilters()) { if (f instanceof SkipScanFilter) { SkipScanFilter newFilter = ((SkipScanFilter) f).intersect(startKey, stopKey); if (newFilter == null) { return false; } newList.addFilter(newFilter); } else { newList.addFilter(f); } } scan.setFilter(newList); } } return mayHaveRows; }
public Scan intersectScan( Scan scan, final byte[] originalStartKey, final byte[] originalStopKey, final int keyOffset, boolean crossesRegionBoundary) { byte[] startKey = originalStartKey; byte[] stopKey = originalStopKey; if (stopKey.length > 0 && Bytes.compareTo(startKey, stopKey) >= 0) { return null; } boolean mayHaveRows = false; // Keep the keys as they are if we have a point lookup, as we've already resolved the // salt bytes in that case. final int scanKeyOffset = this.isSalted && !this.isPointLookup ? SaltingUtil.NUM_SALTING_BYTES : 0; assert (scanKeyOffset == 0 || keyOffset == 0); // Total offset for startKey/stopKey. Either 1 for salted tables or the prefix length // of the current region for local indexes. We'll never have a case where a table is // both salted and local. final int totalKeyOffset = scanKeyOffset + keyOffset; byte[] prefixBytes = ByteUtil.EMPTY_BYTE_ARRAY; if (totalKeyOffset > 0) { prefixBytes = ScanUtil.getPrefix(startKey, totalKeyOffset); /* * If our startKey to stopKey crosses a region boundary consider everything after the startKey as our scan * is always done within a single region. This prevents us from having to prefix the key prior to knowing * whether or not there may be an intersection. We can't calculate whether or not we've crossed a region * boundary for local indexes, because we don't know the key offset of the next region, but only for the * current one (which is the one passed in). If the next prefix happened to be a subset of the previous * prefix, then this wouldn't detect that we crossed a region boundary. */ if (crossesRegionBoundary) { stopKey = ByteUtil.EMPTY_BYTE_ARRAY; } } int scanStartKeyOffset = scanKeyOffset; byte[] scanStartKey = scan == null ? ByteUtil.EMPTY_BYTE_ARRAY : scan.getStartRow(); // Compare ignoring key prefix and salt byte if (scanStartKey.length > 0) { if (startKey.length > 0 && Bytes.compareTo( scanStartKey, scanKeyOffset, scanStartKey.length - scanKeyOffset, startKey, totalKeyOffset, startKey.length - totalKeyOffset) < 0) { scanStartKey = startKey; scanStartKeyOffset = totalKeyOffset; } } else { scanStartKey = startKey; scanStartKeyOffset = totalKeyOffset; mayHaveRows = true; } int scanStopKeyOffset = scanKeyOffset; byte[] scanStopKey = scan == null ? ByteUtil.EMPTY_BYTE_ARRAY : scan.getStopRow(); if (scanStopKey.length > 0) { if (stopKey.length > 0 && Bytes.compareTo( scanStopKey, scanKeyOffset, scanStopKey.length - scanKeyOffset, stopKey, totalKeyOffset, stopKey.length - totalKeyOffset) > 0) { scanStopKey = stopKey; scanStopKeyOffset = totalKeyOffset; } } else { scanStopKey = stopKey; scanStopKeyOffset = totalKeyOffset; mayHaveRows = true; } mayHaveRows = mayHaveRows || Bytes.compareTo( scanStartKey, scanStartKeyOffset, scanStartKey.length - scanStartKeyOffset, scanStopKey, scanStopKeyOffset, scanStopKey.length - scanStopKeyOffset) < 0; if (!mayHaveRows) { return null; } if (originalStopKey.length != 0 && scanStopKey.length == 0) { scanStopKey = originalStopKey; } Filter newFilter = null; // If the scan is using skip scan filter, intersect and replace the filter. if (scan == null || this.useSkipScanFilter()) { byte[] skipScanStartKey = scanStartKey; byte[] skipScanStopKey = scanStopKey; // If we have a keyOffset and we've used the startKey/stopKey that // were passed in (which have the prefix) for the above range check, // we need to remove the prefix before running our intersect method. // TODO: we could use skipScanFilter.setOffset(keyOffset) if both // the startKey and stopKey were used above *and* our intersect // method honored the skipScanFilter.offset variable. if (scanKeyOffset > 0) { if (skipScanStartKey != originalStartKey) { // original already has correct salt byte skipScanStartKey = replaceSaltByte(skipScanStartKey, prefixBytes); } if (skipScanStopKey != originalStopKey) { skipScanStopKey = replaceSaltByte(skipScanStopKey, prefixBytes); } } else if (keyOffset > 0) { if (skipScanStartKey == originalStartKey) { skipScanStartKey = stripPrefix(skipScanStartKey, keyOffset); } if (skipScanStopKey == originalStopKey) { skipScanStopKey = stripPrefix(skipScanStopKey, keyOffset); } } if (scan == null) { return filter.hasIntersect(skipScanStartKey, skipScanStopKey) ? HAS_INTERSECTION : null; } Filter filter = scan.getFilter(); SkipScanFilter newSkipScanFilter = null; if (filter instanceof SkipScanFilter) { SkipScanFilter oldSkipScanFilter = (SkipScanFilter) filter; newFilter = newSkipScanFilter = oldSkipScanFilter.intersect(skipScanStartKey, skipScanStopKey); if (newFilter == null) { return null; } } else if (filter instanceof FilterList) { FilterList oldList = (FilterList) filter; FilterList newList = new FilterList(FilterList.Operator.MUST_PASS_ALL); newFilter = newList; for (Filter f : oldList.getFilters()) { if (f instanceof SkipScanFilter) { newSkipScanFilter = ((SkipScanFilter) f).intersect(skipScanStartKey, skipScanStopKey); if (newSkipScanFilter == null) { return null; } newList.addFilter(newSkipScanFilter); } else { newList.addFilter(f); } } } // TODO: it seems that our SkipScanFilter or HBase runs into problems if we don't // have an enclosing range when we do a point lookup. if (isPointLookup) { scanStartKey = ScanUtil.getMinKey(schema, newSkipScanFilter.getSlots(), slotSpan); scanStopKey = ScanUtil.getMaxKey(schema, newSkipScanFilter.getSlots(), slotSpan); } } if (newFilter == null) { newFilter = scan.getFilter(); } Scan newScan = ScanUtil.newScan(scan); newScan.setFilter(newFilter); // If we have an offset (salted table or local index), we need to make sure to // prefix our scan start/stop row by the prefix of the startKey or stopKey that // were passed in. Our scan either doesn't have the prefix or has a placeholder // for it. if (totalKeyOffset > 0) { if (scanStartKey != originalStartKey) { scanStartKey = prefixKey(scanStartKey, scanKeyOffset, prefixBytes, keyOffset); } if (scanStopKey != originalStopKey) { scanStopKey = prefixKey(scanStopKey, scanKeyOffset, prefixBytes, keyOffset); } } // Don't let the stopRow of the scan go beyond the originalStopKey if (originalStopKey.length > 0 && Bytes.compareTo(scanStopKey, originalStopKey) > 0) { scanStopKey = originalStopKey; } if (scanStopKey.length > 0 && Bytes.compareTo(scanStartKey, scanStopKey) >= 0) { return null; } newScan.setAttribute(SCAN_ACTUAL_START_ROW, scanStartKey); newScan.setStartRow(scanStartKey); newScan.setStopRow(scanStopKey); if (keyOffset > 0) { newScan.setAttribute(STARTKEY_OFFSET, Bytes.toBytes(keyOffset)); } return newScan; }