@Override public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { tuple.getKey(ptr); int offset = accessor.getOffset(ptr.get(), ptr.getOffset()); // Null is represented in the last expression of a multi-part key // by the bytes not being present. if (offset < ptr.getOffset() + ptr.getLength()) { byte[] buffer = ptr.get(); int maxByteSize = ptr.getLength() - (offset - ptr.getOffset()); int fixedByteSize = -1; // FIXME: fixedByteSize <= maxByteSize ? fixedByteSize : 0 required because HBase passes bogus // keys to filter to position scan (HBASE-6562) if (fromType.isFixedWidth()) { fixedByteSize = getByteSize(); fixedByteSize = fixedByteSize <= maxByteSize ? fixedByteSize : 0; } int length = fixedByteSize >= 0 ? fixedByteSize : accessor.getLength(buffer, offset, maxByteSize); // In the middle of the key, an empty variable length byte array represents null if (length > 0) { if (type == fromType) { ptr.set(buffer, offset, length); } else { ptr.set(type.toBytes(type.toObject(buffer, offset, length, fromType))); } return true; } } return false; }
@Override public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { // Starting from the end of the byte, look for all single bytes at the end of the string // that is below SPACE_UTF8 (space and control characters) or above (control chars). if (!getStringExpression().evaluate(tuple, ptr)) { return false; } if (ptr.getLength() == 0) { ptr.set(ByteUtil.EMPTY_BYTE_ARRAY); return true; } byte[] string = ptr.get(); int offset = ptr.getOffset(); int length = ptr.getLength(); int i = offset + length - 1; for (; i >= offset; i--) { if ((string[i] & SINGLE_BYTE_MASK) == 0 && SPACE_UTF8 < string[i] && string[i] != 0x7f) { break; } } if (i == offset - 1) { ptr.set(ByteUtil.EMPTY_BYTE_ARRAY); return true; } ptr.set(string, offset, i - offset + 1); return true; }
@Override public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { BigDecimal result = null; DateNative dateNative = DateNative.getInstance(); for (int i = 0; i < children.size(); i++) { if (!children.get(i).evaluate(tuple, ptr) || ptr.getLength() == 0) { return false; } PDataType childType = children.get(i).getDataType(); boolean isDate = childType.isCoercibleTo(PDataType.DATE); BigDecimal bd = isDate ? BigDecimal.valueOf(dateNative.toLong(ptr)) : (BigDecimal) PDataType.DECIMAL.toObject(ptr, childType); if (result == null) { result = bd; } else { result = result.subtract(bd); /* * Special case for date subtraction - note that only first two expression may be dates. * We need to convert the date to a unit of "days" because that's what sql expects. */ if (isDate) { result = result.divide(BD_MILLIS_IN_DAY, PDataType.DEFAULT_MATH_CONTEXT); } } } ptr.set(PDataType.DECIMAL.toBytes(result)); return true; }
@Override public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { BigDecimal result = null; for (int i = 0; i < children.size(); i++) { Expression childExpr = children.get(i); if (!childExpr.evaluate(tuple, ptr)) { return false; } if (ptr.getLength() == 0) { return true; } PDataType childType = children.get(i).getDataType(); BigDecimal bd = (BigDecimal) PDataType.DECIMAL.toObject(ptr, childType); if (result == null) { result = bd; } else { result = result.divide(bd, PDataType.DEFAULT_MATH_CONTEXT); } } if (maxLength != null && scale != null) { result = NumberUtil.setDecimalWidthAndScale(result, maxLength, scale); } if (result == null) { throw new ValueTypeIncompatibleException(PDataType.DECIMAL, maxLength, scale); } ptr.set(PDataType.DECIMAL.toBytes(result)); return true; }
@Override public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { if (!children.get(0).evaluate(tuple, ptr)) { return false; } String timezone = Bytes.toString(ptr.get(), ptr.getOffset(), ptr.getLength()); if (!children.get(1).evaluate(tuple, ptr)) { return false; } if (!cachedTimeZones.containsKey(timezone)) { TimeZone tz = TimeZone.getTimeZone(timezone); if (!tz.getID().equals(timezone)) { throw new IllegalDataException("Invalid timezone " + timezone); } cachedTimeZones.put(timezone, tz); } Date date = (Date) PDate.INSTANCE.toObject(ptr, children.get(1).getSortOrder()); int offset = cachedTimeZones.get(timezone).getOffset(date.getTime()); ptr.set(PInteger.INSTANCE.toBytes(offset / MILLIS_TO_MINUTES)); return true; }
@Override public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { // TODO: multi-byte characters Expression offsetExpression = getOffsetExpression(); if (!offsetExpression.evaluate(tuple, ptr)) { return false; } LongNative longNative = (LongNative) offsetExpression.getDataType().getNative(); int offset = (int) longNative.toLong(ptr); int length = -1; if (hasLengthExpression) { Expression lengthExpression = getLengthExpression(); if (!lengthExpression.evaluate(tuple, ptr)) { return false; } longNative = (LongNative) lengthExpression.getDataType().getNative(); length = (int) longNative.toLong(ptr); if (length <= 0) { return false; } } if (!getStrExpression().evaluate(tuple, ptr)) { return false; } try { boolean isCharType = getStrExpression().getDataType() == PDataType.CHAR; int strlen = isCharType ? ptr.getLength() : StringUtil.calculateUTF8Length(ptr.get(), ptr.getOffset(), ptr.getLength()); // Account for 1 versus 0-based offset offset = offset - (offset <= 0 ? 0 : 1); if (offset < 0) { // Offset < 0 means get from end offset = strlen + offset; } if (offset < 0 || offset >= strlen) { return false; } int maxLength = strlen - offset; length = length == -1 ? maxLength : Math.min(length, maxLength); int byteOffset = isCharType ? offset : StringUtil.getByteLengthForUtf8SubStr(ptr.get(), ptr.getOffset(), offset); int byteLength = isCharType ? length : StringUtil.getByteLengthForUtf8SubStr( ptr.get(), ptr.getOffset() + byteOffset, length); ptr.set(ptr.get(), ptr.getOffset() + byteOffset, byteLength); return true; } catch (UnsupportedEncodingException e) { return false; } }
@Override public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { Expression expression = getExpression(); if (!expression.evaluate(tuple, ptr)) { return false; } PDataType type = expression.getDataType(); Object value = formatter.format(type.toObject(ptr, expression.getSortOrder())); byte[] b = getDataType().toBytes(value); ptr.set(b); return true; }
@Override public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { if (!getChildExpression().evaluate(tuple, ptr)) { return false; } // Update the digest value messageDigest.update(ptr.get(), ptr.getOffset(), ptr.getLength()); // Get the digest bytes (note this resets the messageDigest as well) ptr.set(messageDigest.digest()); return true; }
@Override public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { if (!super.evaluate(tuple, ptr)) { return false; } if (isConstantExpression()) { PDataType type = getDataType(); Object constantValue = ((LiteralExpression) children.get(0)).getValue(); if (type == PDataType.DECIMAL) { BigDecimal value = ((BigDecimal) constantValue) .multiply((BigDecimal) PDataType.DECIMAL.toObject(ptr, PDataType.LONG)); ptr.set(PDataType.DECIMAL.toBytes(value)); } else { long constantLongValue = ((Number) constantValue).longValue(); long value = constantLongValue * type.getCodec().decodeLong(ptr, null); ptr.set(new byte[type.getByteSize()]); type.getCodec().encodeLong(value, ptr); } } return true; }
@Override public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { if (!getStringExpression().evaluate(tuple, ptr)) { return false; } if (ptr.getLength() == 0) { ptr.set(ByteUtil.EMPTY_BYTE_ARRAY); return true; } byte[] string = ptr.get(); int offset = ptr.getOffset(); int length = ptr.getLength(); ColumnModifier columnModifier = getColumnModifier(); int end = StringUtil.getFirstNonBlankCharIdxFromEnd(string, offset, length, columnModifier); if (end == offset - 1) { ptr.set(ByteUtil.EMPTY_BYTE_ARRAY); return true; } int head = StringUtil.getFirstNonBlankCharIdxFromStart(string, offset, length, columnModifier); ptr.set(string, head, end - head + 1); return true; }
@Override public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { Expression arg = getChildren().get(0); if (!arg.evaluate(tuple, ptr)) { return false; } if (ptr.getLength() == 0) { return true; } long dateTime = inputCodec.decodeLong(ptr, arg.getSortOrder()); DateTime jodaDT = new DateTime(dateTime); int day = jodaDT.getDayOfYear(); PDataType returnDataType = getDataType(); byte[] byteValue = new byte[returnDataType.getByteSize()]; returnDataType.getCodec().encodeInt(day, byteValue, 0); ptr.set(byteValue); return true; }
@Override public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { // This serializes the Map. The format is as follows // Map size(VInt ie. 1 to 5 bytes) + // ( key length [VInt ie. 1 to 5 bytes] + key bytes + value [VInt ie. 1 to 5 bytes] )* buffer = new byte[countMapSerializationSize()]; int offset = 0; offset += ByteUtil.vintToBytes(buffer, offset, this.valueVsCount.size()); for (Entry<ImmutableBytesPtr, Integer> entry : this.valueVsCount.entrySet()) { ImmutableBytesPtr key = entry.getKey(); offset += ByteUtil.vintToBytes(buffer, offset, key.getLength()); System.arraycopy(key.get(), key.getOffset(), buffer, offset, key.getLength()); offset += key.getLength(); offset += ByteUtil.vintToBytes(buffer, offset, entry.getValue().intValue()); } ptr.set(buffer, 0, offset); return true; }
@Override public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { // Reset buffer so that it gets initialized with the current datatype of the column buffer = null; if (cachedResult == null) { columnExp = (ColumnExpression) exps.get(0); // Second exp will be a LiteralExpression of Boolean type indicating // whether the ordering to be ASC/DESC LiteralExpression isAscendingExpression = (LiteralExpression) exps.get(1); boolean isAscending = (Boolean) isAscendingExpression.getValue(); // Third expression will be LiteralExpression LiteralExpression percentileExp = (LiteralExpression) exps.get(2); float p = ((Number) percentileExp.getValue()).floatValue(); Map<Object, Integer> sorted = getSortedValueVsCount(isAscending, columnExp.getDataType()); int currValue = 0; Object result = null; // Here the Percentile_disc returns the cum_dist() that is greater or equal to the // Percentile (p) specified in the query. So the result set will be of that of the // datatype of the column being selected for (Entry<Object, Integer> entry : sorted.entrySet()) { result = entry.getKey(); Integer value = entry.getValue(); currValue += value; float cum_dist = (float) currValue / (float) totalCount; if (cum_dist >= p) { break; } } this.cachedResult = result; } if (buffer == null) { // Initialize based on the datatype // columnExp cannot be null buffer = new byte[columnExp.getDataType().getByteSize()]; } // Copy the result to the buffer. System.arraycopy( columnExp.getDataType().toBytes(this.cachedResult), 0, buffer, 0, buffer.length); ptr.set(buffer); return true; }
@Override public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { BigDecimal result = null; for (int i = 0; i < children.size(); i++) { if (!children.get(i).evaluate(tuple, ptr) || ptr.getLength() == 0) { return false; } PDataType childType = children.get(i).getDataType(); BigDecimal bd = (BigDecimal) PDataType.DECIMAL.toObject(ptr, childType); if (result == null) { result = bd; } else { result = result.divide(bd, PDataType.DEFAULT_MATH_CONTEXT); } } ptr.set(PDataType.DECIMAL.toBytes(result)); return true; }
@Override public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { BigDecimal finalResult = BigDecimal.ZERO; for (int i = 0; i < children.size(); i++) { if (!children.get(i).evaluate(tuple, ptr)) { return false; } if (ptr.getLength() == 0) { return true; } BigDecimal value; PDataType type = children.get(i).getDataType(); ColumnModifier columnModifier = children.get(i).getColumnModifier(); if (type == PDataType.TIMESTAMP || type == PDataType.UNSIGNED_TIMESTAMP) { value = (BigDecimal) (PDataType.DECIMAL.toObject(ptr, type, columnModifier)); } else if (type.isCoercibleTo(PDataType.DECIMAL)) { value = (((BigDecimal) PDataType.DECIMAL.toObject(ptr, columnModifier)) .multiply(QueryConstants.BD_MILLIS_IN_DAY)) .setScale(6, RoundingMode.HALF_UP); } else if (type.isCoercibleTo(PDataType.DOUBLE)) { value = ((BigDecimal.valueOf(type.getCodec().decodeDouble(ptr, columnModifier))) .multiply(QueryConstants.BD_MILLIS_IN_DAY)) .setScale(6, RoundingMode.HALF_UP); } else { value = BigDecimal.valueOf(type.getCodec().decodeLong(ptr, columnModifier)); } finalResult = finalResult.add(value); } Timestamp ts = DateUtil.getTimestamp(finalResult); byte[] resultPtr = new byte[getDataType().getByteSize()]; PDataType.TIMESTAMP.toBytes(ts, resultPtr, 0); ptr.set(resultPtr); return true; }
private static MutationState upsertSelect( PhoenixStatement statement, TableRef tableRef, RowProjector projector, ResultIterator iterator, int[] columnIndexes, int[] pkSlotIndexes) throws SQLException { try { PhoenixConnection connection = statement.getConnection(); ConnectionQueryServices services = connection.getQueryServices(); int maxSize = services .getProps() .getInt( QueryServices.MAX_MUTATION_SIZE_ATTRIB, QueryServicesOptions.DEFAULT_MAX_MUTATION_SIZE); int batchSize = Math.min(connection.getMutateBatchSize(), maxSize); boolean isAutoCommit = connection.getAutoCommit(); byte[][] values = new byte[columnIndexes.length][]; int rowCount = 0; Map<ImmutableBytesPtr, Map<PColumn, byte[]>> mutation = Maps.newHashMapWithExpectedSize(batchSize); PTable table = tableRef.getTable(); ResultSet rs = new PhoenixResultSet(iterator, projector, statement); ImmutableBytesWritable ptr = new ImmutableBytesWritable(); while (rs.next()) { for (int i = 0; i < values.length; i++) { PColumn column = table.getColumns().get(columnIndexes[i]); byte[] bytes = rs.getBytes(i + 1); ptr.set(bytes == null ? ByteUtil.EMPTY_BYTE_ARRAY : bytes); Object value = rs.getObject(i + 1); int rsPrecision = rs.getMetaData().getPrecision(i + 1); Integer precision = rsPrecision == 0 ? null : rsPrecision; int rsScale = rs.getMetaData().getScale(i + 1); Integer scale = rsScale == 0 ? null : rsScale; // We are guaranteed that the two column will have compatible types, // as we checked that before. if (!column .getDataType() .isSizeCompatible( ptr, value, column.getDataType(), precision, scale, column.getMaxLength(), column.getScale())) { throw new SQLExceptionInfo.Builder(SQLExceptionCode.DATA_EXCEEDS_MAX_CAPACITY) .setColumnName(column.getName().getString()) .setMessage("value=" + column.getDataType().toStringLiteral(ptr, null)) .build() .buildException(); } column .getDataType() .coerceBytes( ptr, value, column.getDataType(), precision, scale, SortOrder.getDefault(), column.getMaxLength(), column.getScale(), column.getSortOrder()); values[i] = ByteUtil.copyKeyBytesIfNecessary(ptr); } setValues(values, pkSlotIndexes, columnIndexes, table, mutation); rowCount++; // Commit a batch if auto commit is true and we're at our batch size if (isAutoCommit && rowCount % batchSize == 0) { MutationState state = new MutationState(tableRef, mutation, 0, maxSize, connection); connection.getMutationState().join(state); connection.commit(); mutation.clear(); } } // If auto commit is true, this last batch will be committed upon return return new MutationState( tableRef, mutation, rowCount / batchSize * batchSize, maxSize, connection); } finally { iterator.close(); } }
@edu.umd.cs.findbugs.annotations.SuppressWarnings( value = "QBA_QUESTIONABLE_BOOLEAN_ASSIGNMENT", justification = "Assignment designed to work this way.") private ReturnCode navigate( final byte[] currentKey, int offset, int length, Terminate terminate) { int nSlots = slots.size(); // First check to see if we're in-range until we reach our end key if (endKeyLength > 0) { if (Bytes.compareTo(currentKey, offset, length, endKey, 0, endKeyLength) < 0) { return ReturnCode.INCLUDE; } // If key range of last slot is a single key, we can increment our position // since we know we'll be past the current row after including it. if (slots.get(nSlots - 1).get(position[nSlots - 1]).isSingleKey()) { if (nextPosition(nSlots - 1) < 0) { // Current row will be included, but we have no more isDone = true; return ReturnCode.NEXT_ROW; } } else { // Reset the positions to zero from the next slot after the earliest ranged slot, since the // next key could be bigger at this ranged slot, and smaller than the current position of // less significant slots. int earliestRangeIndex = nSlots - 1; for (int i = 0; i < nSlots; i++) { if (!slots.get(i).get(position[i]).isSingleKey()) { earliestRangeIndex = i; break; } } Arrays.fill(position, earliestRangeIndex + 1, position.length, 0); } } endKeyLength = 0; // We could have included the previous if (isDone) { return ReturnCode.NEXT_ROW; } int i = 0; boolean seek = false; int earliestRangeIndex = nSlots - 1; ptr.set(currentKey, offset, length); schema.first(ptr, i, ValueBitSet.EMPTY_VALUE_BITSET); while (true) { // Increment to the next range while the upper bound of our current slot is less than our // current key while (position[i] < slots.get(i).size() && slots.get(i).get(position[i]).compareUpperToLowerBound(ptr) < 0) { position[i]++; } Arrays.fill(position, i + 1, position.length, 0); if (position[i] >= slots.get(i).size()) { // Our current key is bigger than the last range of the current slot. // If navigating after current key, backtrack and increment the key of the previous slot // values. // If navigating to current key, just return if (terminate == Terminate.AT) { return ReturnCode.SEEK_NEXT_USING_HINT; } if (i == 0) { isDone = true; return ReturnCode.NEXT_ROW; } // Increment key and backtrack until in range. We know at this point that we'll be // issuing a seek next hint. seek = true; Arrays.fill(position, i, position.length, 0); i--; // If we're positioned at a single key, no need to copy the current key and get the next key // . // Instead, just increment to the next key and continue. boolean incremented = false; while (i >= 0 && slots.get(i).get(position[i]).isSingleKey() && (incremented = true) && (position[i] = (position[i] + 1) % slots.get(i).size()) == 0) { i--; incremented = false; } if (i < 0) { isDone = true; return ReturnCode.NEXT_ROW; } if (incremented) { // Continue the loop after setting the start key, because our start key maybe smaller than // the current key, so we'll end up incrementing the start key until it's bigger than the // current key. setStartKey(); ptr.set(ptr.get(), offset, length); // Reinitialize iterator to be positioned at previous slot position schema.setAccessor(ptr, i, ValueBitSet.EMPTY_VALUE_BITSET); } else { int currentLength = setStartKey(ptr, offset, i + 1); // From here on, we use startKey as our buffer with offset reset to 0 // We've copied the part of the current key above that we need into startKey ptr.set(startKey, offset = 0, length = startKeyLength); // Reinitialize iterator to be positioned at previous slot position // TODO: a schema.previous would potentially be more efficient schema.setAccessor(ptr, i, ValueBitSet.EMPTY_VALUE_BITSET); // Do nextKey after setting the accessor b/c otherwise the null byte may have // been incremented causing us not to find it ByteUtil.nextKey(startKey, currentLength); } } else if (slots.get(i).get(position[i]).compareLowerToUpperBound(ptr) > 0) { // Our current key is less than the lower range of the current position in the current slot. // Seek to the lower range, since it's bigger than the current key setStartKey(ptr, offset, i); return ReturnCode.SEEK_NEXT_USING_HINT; } else { // We're in range, check the next slot if (!slots.get(i).get(position[i]).isSingleKey() && i < earliestRangeIndex) { earliestRangeIndex = i; } i++; // If we're past the last slot or we know we're seeking to the next (in // which case the previously updated slot was verified to be within the // range, so we don't need to check the rest of the slots. If we were // to check the rest of the slots, we'd get into trouble because we may // have a null byte that was incremented which screws up our schema.next call) if (i >= nSlots || seek) { break; } // If we run out of slots in our key, it means we have a partial key. In this // case, we seek to the next full key after this one. // TODO: test for this if (schema.next(ptr, i, offset + length, ValueBitSet.EMPTY_VALUE_BITSET) == null) { setStartKey(ptr, offset, i); return ReturnCode.SEEK_NEXT_USING_HINT; } } } if (seek) { return ReturnCode.SEEK_NEXT_USING_HINT; } // Else, we're in range for all slots and can include this row plus all rows // up to the upper range of our last slot. We do this for ranges and single keys // since we potentially have multiple key values for the same row key. setEndKey(ptr, offset, slots.size() - 1); return ReturnCode.INCLUDE; }
private boolean intersect( byte[] lowerInclusiveKey, byte[] upperExclusiveKey, List<List<KeyRange>> newSlots) { boolean lowerUnbound = (lowerInclusiveKey.length == 0); Arrays.fill(position, 0); isDone = false; int startPos = 0; int lastSlot = slots.size() - 1; if (!lowerUnbound) { // Find the position of the first slot of the lower range ptr.set(lowerInclusiveKey); schema.first(ptr, 0, ValueBitSet.EMPTY_VALUE_BITSET); startPos = ScanUtil.searchClosestKeyRangeWithUpperHigherThanPtr(slots.get(0), ptr, 0); // Lower range is past last upper range of first slot, so cannot possibly be in range if (startPos >= slots.get(0).size()) { return false; } } boolean upperUnbound = (upperExclusiveKey.length == 0); int endPos = slots.get(0).size() - 1; if (!upperUnbound) { // Find the position of the first slot of the upper range ptr.set(upperExclusiveKey); schema.first(ptr, 0, ValueBitSet.EMPTY_VALUE_BITSET); endPos = ScanUtil.searchClosestKeyRangeWithUpperHigherThanPtr(slots.get(0), ptr, startPos); // Upper range lower than first lower range of first slot, so cannot possibly be in range if (endPos == 0 && Bytes.compareTo(upperExclusiveKey, slots.get(0).get(0).getLowerRange()) <= 0) { return false; } // Past last position, so we can include everything from the start position if (endPos >= slots.get(0).size()) { upperUnbound = true; endPos = slots.get(0).size() - 1; } } if (!lowerUnbound) { position[0] = startPos; navigate(lowerInclusiveKey, 0, lowerInclusiveKey.length, Terminate.AFTER); if (filterAllRemaining()) { return false; } } if (upperUnbound) { if (newSlots != null) { newSlots.add(slots.get(0).subList(startPos, endPos + 1)); newSlots.addAll(slots.subList(1, slots.size())); } return true; } int[] lowerPosition = Arrays.copyOf(position, position.length); // Navigate to the upperExclusiveKey, but not past it ReturnCode endCode = navigate(upperExclusiveKey, 0, upperExclusiveKey.length, Terminate.AT); if (endCode == ReturnCode.INCLUDE) { setStartKey(); // If the upperExclusiveKey is equal to the start key, we've gone one position too far, since // our upper key is exclusive. In that case, go to the previous key if (Bytes.compareTo( startKey, 0, startKeyLength, upperExclusiveKey, 0, upperExclusiveKey.length) == 0 && (previousPosition(lastSlot) < 0 || position[0] < lowerPosition[0])) { // If by backing up one position we have an empty range, then return return false; } } else if (endCode == ReturnCode.SEEK_NEXT_USING_HINT) { // The upperExclusive key is smaller than the slots stored in the position. Check if it's the // same position // as the slots for lowerInclusive. If so, there is no intersection. if (Arrays.equals(lowerPosition, position)) { return false; } } // Copy inclusive all positions for (int i = 0; i <= lastSlot; i++) { List<KeyRange> newRanges = slots.get(i).subList(lowerPosition[i], Math.min(position[i] + 1, slots.get(i).size())); if (newRanges.isEmpty()) { return false; } if (newSlots != null) { newSlots.add(newRanges); } if (position[i] > lowerPosition[i]) { if (newSlots != null) { newSlots.addAll(slots.subList(i + 1, slots.size())); } break; } } return true; }
@Override public boolean getValue(byte[] family, byte[] qualifier, ImmutableBytesWritable ptr) { ptr.set(projectedValue); return true; }
@Override public void getKey(ImmutableBytesWritable ptr) { ptr.set(keyPtr.get(), keyPtr.getOffset(), keyPtr.getLength()); }
@Override public int newKey(ImmutableBytesWritable key, byte[][] values) { int i = 0; TrustedByteArrayOutputStream os = new TrustedByteArrayOutputStream(SchemaUtil.estimateKeyLength(this)); try { List<PColumn> columns = getPKColumns(); int nColumns = columns.size(); PColumn lastPKColumn = columns.get(nColumns - 1); while (i < values.length && i < nColumns) { PColumn column = columns.get(i); PDataType type = column.getDataType(); // This will throw if the value is null and the type doesn't allow null byte[] byteValue = values[i++]; if (byteValue == null) { byteValue = ByteUtil.EMPTY_BYTE_ARRAY; } // An empty byte array return value means null. Do this, // since a type may have muliple representations of null. // For example, VARCHAR treats both null and an empty string // as null. This way we don't need to leak that part of the // implementation outside of PDataType by checking the value // here. if (byteValue.length == 0 && !column.isNullable()) { throw new ConstraintViolationException( name.getString() + "." + column.getName().getString() + " may not be null"); } Integer byteSize = column.getByteSize(); if (type.isFixedWidth()) { // TODO: handle multi-byte characters if (byteValue.length != byteSize) { throw new ConstraintViolationException( name.getString() + "." + column.getName().getString() + " must be " + byteSize + " bytes (" + SchemaUtil.toString(type, byteValue) + ")"); } } else if (byteSize != null && byteValue.length > byteSize) { throw new ConstraintViolationException( name.getString() + "." + column.getName().getString() + " may not exceed " + byteSize + " bytes (" + SchemaUtil.toString(type, byteValue) + ")"); } os.write(byteValue, 0, byteValue.length); // Separate variable length column values in key with zero byte if (!type.isFixedWidth() && column != lastPKColumn) { os.write(SEPARATOR_BYTE); } } // If some non null pk values aren't set, then throw if (i < nColumns) { PColumn column = columns.get(i); PDataType type = column.getDataType(); if (type.isFixedWidth() || !column.isNullable()) { throw new ConstraintViolationException( name.getString() + "." + column.getName().getString() + " may not be null"); } // Separate variable length column values in key with zero byte if (column != lastPKColumn) { os.write(SEPARATOR_BYTE); } } key.set(os.getBuffer(), 0, os.size()); return i; } finally { try { os.close(); } catch (IOException e) { throw new RuntimeException(e); // Impossible } } }
@Override public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { // Literal always evaluates, even when it returns null ptr.set(byteValue); return true; }