@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; }
@Test public void testNegativeCompareNegativeValue() throws Exception { String query = "SELECT string_key FROM HBASE_NATIVE WHERE uint_key > 100000"; String url = PHOENIX_JDBC_URL + ";" + PhoenixRuntime.CURRENT_SCN_ATTRIB + "=" + (ts + 7); // Run query at timestamp 7 Properties props = new Properties(TEST_PROPERTIES); PhoenixConnection conn = DriverManager.getConnection(url, props).unwrap(PhoenixConnection.class); HTableInterface hTable = conn.getQueryServices() .getTable(SchemaUtil.getTableNameAsBytes(HBASE_NATIVE_SCHEMA_NAME, HBASE_NATIVE)); List<Row> mutations = new ArrayList<Row>(); byte[] family = Bytes.toBytes("1"); byte[] uintCol = Bytes.toBytes("UINT_COL"); byte[] ulongCol = Bytes.toBytes("ULONG_COL"); byte[] key; Put put; // Need to use native APIs because the Phoenix APIs wouldn't let you insert a // negative number for an unsigned type key = ByteUtil.concat(Bytes.toBytes(-10), Bytes.toBytes(100L), Bytes.toBytes("e")); put = new Put(key); // Insert at later timestamp than other queries in this test are using, so that // we don't affect them put.add(family, uintCol, ts + 6, Bytes.toBytes(10)); put.add(family, ulongCol, ts + 6, Bytes.toBytes(100L)); put.add(family, QueryConstants.EMPTY_COLUMN_BYTES, ts + 6, ByteUtil.EMPTY_BYTE_ARRAY); mutations.add(put); hTable.batch(mutations); // Demonstrates weakness of HBase Bytes serialization. Negative numbers // show up as bigger than positive numbers PreparedStatement statement = conn.prepareStatement(query); ResultSet rs = statement.executeQuery(); assertTrue(rs.next()); assertEquals("e", rs.getString(1)); assertFalse(rs.next()); }
@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 static void initTableValues() throws Exception { ConnectionQueryServices services = driver.getConnectionQueryServices(getUrl(), TEST_PROPERTIES); HTableInterface hTable = services.getTable(SchemaUtil.getTableNameAsBytes(HBASE_NATIVE_SCHEMA_NAME, HBASE_NATIVE)); try { // Insert rows using standard HBase mechanism with standard HBase "types" List<Row> mutations = new ArrayList<Row>(); byte[] family = Bytes.toBytes("1"); byte[] uintCol = Bytes.toBytes("UINT_COL"); byte[] ulongCol = Bytes.toBytes("ULONG_COL"); byte[] key, bKey; Put put; key = ByteUtil.concat(Bytes.toBytes(10), Bytes.toBytes(100L), Bytes.toBytes("a")); put = new Put(key); put.add(family, uintCol, ts - 2, Bytes.toBytes(5)); put.add(family, ulongCol, ts - 2, Bytes.toBytes(50L)); mutations.add(put); put = new Put(key); put.add(family, uintCol, ts, Bytes.toBytes(10)); put.add(family, ulongCol, ts, Bytes.toBytes(100L)); mutations.add(put); bKey = key = ByteUtil.concat(Bytes.toBytes(20), Bytes.toBytes(200L), Bytes.toBytes("b")); put = new Put(key); put.add(family, uintCol, ts - 4, Bytes.toBytes(5000)); put.add(family, ulongCol, ts - 4, Bytes.toBytes(50000L)); mutations.add(put); @SuppressWarnings( "deprecation") // FIXME: Remove when unintentionally deprecated method is fixed // (HBASE-7870). // FIXME: the version of the Delete constructor without the lock args was introduced // in 0.94.4, thus if we try to use it here we can no longer use the 0.94.2 version // of the client. Delete del = new Delete(key, ts - 2, null); mutations.add(del); put = new Put(key); put.add(family, uintCol, ts, Bytes.toBytes(2000)); put.add(family, ulongCol, ts, Bytes.toBytes(20000L)); mutations.add(put); key = ByteUtil.concat(Bytes.toBytes(30), Bytes.toBytes(300L), Bytes.toBytes("c")); put = new Put(key); put.add(family, uintCol, ts, Bytes.toBytes(3000)); put.add(family, ulongCol, ts, Bytes.toBytes(30000L)); mutations.add(put); key = ByteUtil.concat(Bytes.toBytes(40), Bytes.toBytes(400L), Bytes.toBytes("d")); put = new Put(key); put.add(family, uintCol, ts, Bytes.toBytes(4000)); put.add(family, ulongCol, ts, Bytes.toBytes(40000L)); mutations.add(put); hTable.batch(mutations); Result r = hTable.get(new Get(bKey)); assertFalse(r.isEmpty()); } finally { hTable.close(); } // Create Phoenix table after HBase table was created through the native APIs // The timestamp of the table creation must be later than the timestamp of the data ensureTableCreated(getUrl(), HBASE_NATIVE, null, ts + 1); }