private static byte[] getKey( RowKeySchema schema, List<List<KeyRange>> slots, int[] slotSpan, Bound bound) { if (slots.isEmpty()) { return KeyRange.UNBOUND; } int[] position = new int[slots.size()]; int maxLength = 0; for (int i = 0; i < position.length; i++) { position[i] = bound == Bound.LOWER ? 0 : slots.get(i).size() - 1; KeyRange range = slots.get(i).get(position[i]); Field field = schema.getField(i + slotSpan[i]); int keyLength = range.getRange(bound).length; if (!field.getDataType().isFixedWidth()) { keyLength++; if (range.isUnbound(bound) && !range.isInclusive(bound) && field.getSortOrder() == SortOrder.DESC) { keyLength++; } } maxLength += keyLength; } byte[] key = new byte[maxLength]; int length = setKey(schema, slots, slotSpan, position, bound, key, 0, 0, position.length); if (length == 0) { return KeyRange.UNBOUND; } if (length == maxLength) { return key; } byte[] keyCopy = new byte[length]; System.arraycopy(key, 0, keyCopy, 0, length); return keyCopy; }
private static int getTerminatorCount(RowKeySchema schema) { int nTerminators = 0; for (int i = 0; i < schema.getFieldCount(); i++) { Field field = schema.getField(i); // We won't have a terminator on the last PK column // unless it is variable length and exclusive, but // having the extra byte irregardless won't hurt anything if (!field.getDataType().isFixedWidth()) { nTerminators++; } } return nTerminators; }
public static PName padTenantIdIfNecessary( RowKeySchema schema, boolean isSalted, PName tenantId) { int pkPos = isSalted ? 1 : 0; String tenantIdStr = tenantId.getString(); Field field = schema.getField(pkPos); PDataType dataType = field.getDataType(); boolean isFixedWidth = dataType.isFixedWidth(); Integer maxLength = field.getMaxLength(); if (isFixedWidth && maxLength != null) { if (tenantIdStr.length() < maxLength) { tenantIdStr = (String) dataType.pad(tenantIdStr, maxLength); return PNameFactory.newName(tenantIdStr); } } return tenantId; }
private static byte[] nextKey(byte[] key, RowKeySchema schema, ImmutableBytesWritable ptr) { int pos = 0; int maxOffset = schema.iterator(key, ptr); while (schema.next(ptr, pos, maxOffset) != null) { pos++; } Field field = schema.getField(pos - 1); if (!field.getDataType().isFixedWidth()) { byte[] newLowerRange = new byte[key.length + 1]; System.arraycopy(key, 0, newLowerRange, 0, key.length); newLowerRange[key.length] = SchemaUtil.getSeparatorByte(schema.rowKeyOrderOptimizable(), key.length == 0, field); key = newLowerRange; } else { key = Arrays.copyOf(key, key.length); } ByteUtil.nextKey(key, key.length); return key; }
public static TimeRange getDescTimeRange( KeyRange lowestKeyRange, KeyRange highestKeyRange, Field f) throws IOException { boolean lowerUnbound = lowestKeyRange.lowerUnbound(); boolean lowerInclusive = lowestKeyRange.isLowerInclusive(); boolean upperUnbound = highestKeyRange.upperUnbound(); boolean upperInclusive = highestKeyRange.isUpperInclusive(); long low = lowerUnbound ? -1 : f.getDataType() .getCodec() .decodeLong(lowestKeyRange.getLowerRange(), 0, SortOrder.DESC); long high = upperUnbound ? -1 : f.getDataType() .getCodec() .decodeLong(highestKeyRange.getUpperRange(), 0, SortOrder.DESC); long newHigh; long newLow; if (!lowerUnbound && !upperUnbound) { newHigh = lowerInclusive ? safelyIncrement(low) : low; newLow = upperInclusive ? high : safelyIncrement(high); return new TimeRange(newLow, newHigh); } else if (!lowerUnbound && upperUnbound) { newHigh = lowerInclusive ? safelyIncrement(low) : low; newLow = 0; return new TimeRange(newLow, newHigh); } else if (lowerUnbound && !upperUnbound) { newLow = upperInclusive ? high : safelyIncrement(high); newHigh = HConstants.LATEST_TIMESTAMP; return new TimeRange(newLow, newHigh); } else { newLow = 0; newHigh = HConstants.LATEST_TIMESTAMP; return new TimeRange(newLow, newHigh); } }
/** * 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; } }
private static TimeRange getAscTimeRange(KeyRange lowestRange, KeyRange highestRange, Field f) throws IOException { long low; long high; if (lowestRange.lowerUnbound()) { low = 0; } else { long lowerRange = f.getDataType().getCodec().decodeLong(lowestRange.getLowerRange(), 0, SortOrder.ASC); low = lowestRange.isLowerInclusive() ? lowerRange : safelyIncrement(lowerRange); } if (highestRange.upperUnbound()) { high = HConstants.LATEST_TIMESTAMP; } else { long upperRange = f.getDataType().getCodec().decodeLong(highestRange.getUpperRange(), 0, SortOrder.ASC); if (highestRange.isUpperInclusive()) { high = safelyIncrement(upperRange); } else { high = upperRange; } } return new TimeRange(low, high); }
private static TimeRange getRowTimestampColumnRange( List<List<KeyRange>> ranges, RowKeySchema schema, int rowTimestampColPos) { try { if (rowTimestampColPos != -1) { if (ranges != null && ranges.size() > rowTimestampColPos) { List<KeyRange> rowTimestampColRange = ranges.get(rowTimestampColPos); List<KeyRange> sortedRange = new ArrayList<>(rowTimestampColRange); Collections.sort(sortedRange, KeyRange.COMPARATOR); // ranges.set(rowTimestampColPos, sortedRange); //TODO: do I really need to do this? Field f = schema.getField(rowTimestampColPos); SortOrder order = f.getSortOrder(); KeyRange lowestRange = rowTimestampColRange.get(0); KeyRange highestRange = rowTimestampColRange.get(rowTimestampColRange.size() - 1); if (order == SortOrder.DESC) { return getDescTimeRange(lowestRange, highestRange, f); } return getAscTimeRange(lowestRange, highestRange, f); } } } catch (IOException e) { Throwables.propagate(e); } return null; }
public static BytesComparator getComparator(Field field) { return getComparator(field.getDataType().isFixedWidth(), field.getSortOrder()); }
public static int setKey( RowKeySchema schema, List<List<KeyRange>> slots, int[] slotSpan, int[] position, Bound bound, byte[] key, int byteOffset, int slotStartIndex, int slotEndIndex, int schemaStartIndex) { int offset = byteOffset; boolean lastInclusiveUpperSingleKey = false; boolean anyInclusiveUpperRangeKey = false; // The index used for slots should be incremented by 1, // but the index for the field it represents in the schema // should be incremented by 1 + value in the current slotSpan index // slotSpan stores the number of columns beyond one that the range spans Field field = null; int i = slotStartIndex, fieldIndex = ScanUtil.getRowKeyPosition(slotSpan, slotStartIndex); for (i = slotStartIndex; i < slotEndIndex; i++) { // Build up the key by appending the bound of each key range // from the current position of each slot. KeyRange range = slots.get(i).get(position[i]); // Use last slot in a multi-span column to determine if fixed width field = schema.getField(fieldIndex + slotSpan[i]); boolean isFixedWidth = field.getDataType().isFixedWidth(); fieldIndex += slotSpan[i] + 1; /* * If the current slot is unbound then stop if: * 1) setting the upper bound. There's no value in * continuing because nothing will be filtered. * 2) setting the lower bound when the type is fixed length * for the same reason. However, if the type is variable width * continue building the key because null values will be filtered * since our separator byte will be appended and incremented. */ if (range.isUnbound(bound) && (bound == Bound.UPPER || isFixedWidth)) { break; } byte[] bytes = range.getRange(bound); System.arraycopy(bytes, 0, key, offset, bytes.length); offset += bytes.length; /* * We must add a terminator to a variable length key even for the last PK column if * the lower key is non inclusive or the upper key is inclusive. Otherwise, we'd be * incrementing the key value itself, and thus bumping it up too much. */ boolean inclusiveUpper = range.isInclusive(bound) && bound == Bound.UPPER; boolean exclusiveLower = !range.isInclusive(bound) && bound == Bound.LOWER; // If we are setting the upper bound of using inclusive single key, we remember // to increment the key if we exit the loop after this iteration. // // We remember to increment the last slot if we are setting the upper bound with an // inclusive range key. // // We cannot combine the two flags together in case for single-inclusive key followed // by the range-exclusive key. In that case, we do not need to increment the end at the // end. But if we combine the two flag, the single inclusive key in the middle of the // key slots would cause the flag to become true. lastInclusiveUpperSingleKey = range.isSingleKey() && inclusiveUpper; anyInclusiveUpperRangeKey |= !range.isSingleKey() && inclusiveUpper; // A null or empty byte array is always represented as a zero byte byte sepByte = SchemaUtil.getSeparatorByte(schema.rowKeyOrderOptimizable(), bytes.length == 0, field); if (!isFixedWidth && (fieldIndex < schema.getMaxFields() || inclusiveUpper || exclusiveLower || sepByte == QueryConstants.DESC_SEPARATOR_BYTE)) { key[offset++] = sepByte; // Set lastInclusiveUpperSingleKey back to false if this is the last pk column // as we don't want to increment the null byte in this case lastInclusiveUpperSingleKey &= i < schema.getMaxFields() - 1; } // If we are setting the lower bound with an exclusive range key, we need to bump the // slot up for each key part. For an upper bound, we bump up an inclusive key, but // only after the last key part. if (exclusiveLower) { if (!ByteUtil.nextKey(key, offset)) { // Special case for not being able to increment. // In this case we return a negative byteOffset to // remove this part from the key being formed. Since the // key has overflowed, this means that we should not // have an end key specified. return -byteOffset; } // We're filtering on values being non null here, but we still need the 0xFF // terminator, since DESC keys ignore the last byte as it's expected to be // the terminator. Without this, we'd ignore the separator byte that was // just added and incremented. if (!isFixedWidth && bytes.length == 0 && SchemaUtil.getSeparatorByte(schema.rowKeyOrderOptimizable(), false, field) == QueryConstants.DESC_SEPARATOR_BYTE) { key[offset++] = QueryConstants.DESC_SEPARATOR_BYTE; } } } if (lastInclusiveUpperSingleKey || anyInclusiveUpperRangeKey) { if (!ByteUtil.nextKey(key, offset)) { // Special case for not being able to increment. // In this case we return a negative byteOffset to // remove this part from the key being formed. Since the // key has overflowed, this means that we should not // have an end key specified. return -byteOffset; } } // Remove trailing separator bytes, since the columns may have been added // after the table has data, in which case there won't be a separator // byte. if (bound == Bound.LOWER) { while (--i >= schemaStartIndex && offset > byteOffset && !(field = schema.getField(--fieldIndex)).getDataType().isFixedWidth() && field.getSortOrder() == SortOrder.ASC && key[offset - 1] == QueryConstants.SEPARATOR_BYTE) { offset--; fieldIndex -= slotSpan[i]; } } return offset - byteOffset; }
public static byte getSeparatorByte( boolean rowKeyOrderOptimizable, boolean isNullValue, Field f) { return getSeparatorByte(rowKeyOrderOptimizable, isNullValue, f.getSortOrder()); }