/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.facebook.presto.block; import com.facebook.presto.spi.block.Block; import com.facebook.presto.spi.block.BlockBuilderStatus; import com.facebook.presto.spi.block.InterleavedBlock; import com.facebook.presto.spi.block.InterleavedBlockBuilder; import com.facebook.presto.spi.type.Type; import com.facebook.presto.spi.type.VarcharType; import com.google.common.collect.ImmutableList; import com.google.common.primitives.Ints; import com.google.common.primitives.Longs; import io.airlift.slice.Slice; import org.testng.annotations.Test; import java.lang.reflect.Array; import java.util.List; import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Collectors; import java.util.stream.IntStream; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.VarcharType.VARCHAR; import static com.facebook.presto.spi.type.VarcharType.createUnboundedVarcharType; import static java.lang.Math.toIntExact; import static java.lang.String.format; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; public class TestInterleavedBlock extends AbstractTestBlock { private static final int[] SIZE_0 = new int[] {16, 0, 13, 1, 2, 11, 4, 7}; private static final int SIZE_1 = 8; private static final int POSITION_COUNT = SIZE_0.length; private static final int COLUMN_COUNT = 2; private static final ImmutableList<Type> TYPES = ImmutableList.of(VARCHAR, BIGINT); @Test public void test() { Slice[] expectedValues = createExpectedValues(); assertValues(expectedValues); assertValues((Slice[]) alternatingNullValues(expectedValues)); } @Test public void testCopyPositions() { Slice[] expectedValues = createExpectedValues(); InterleavedBlockBuilder blockBuilder = createBlockBuilderWithValues(expectedValues); assertBlockFilteredPositions(expectedValues, blockBuilder, Ints.asList(0, 1, 4, 5, 6, 7, 14, 15)); assertBlockFilteredPositions(expectedValues, blockBuilder.build(), Ints.asList(0, 1, 4, 5, 6, 7, 14, 15)); assertBlockFilteredPositions(expectedValues, blockBuilder.build(), Ints.asList(2, 3, 4, 5, 8, 9, 12, 13)); } @Test private void testGetSizeInBytes() { int numEntries = 2000; VarcharType unboundedVarcharType = createUnboundedVarcharType(); InterleavedBlockBuilder blockBuilder = new InterleavedBlockBuilder(ImmutableList.of(unboundedVarcharType, BIGINT), new BlockBuilderStatus(), numEntries); for (int i = 0; i < numEntries; i += 2) { unboundedVarcharType.writeString(blockBuilder, String.valueOf(ThreadLocalRandom.current().nextLong())); BIGINT.writeLong(blockBuilder, ThreadLocalRandom.current().nextLong()); } InterleavedBlock block = blockBuilder.build(); List<Block> splitQuarter = splitBlock(block, 4); int sizeInBytes = block.getSizeInBytes(); int quarter1size = splitQuarter.get(0).getSizeInBytes(); int quarter2size = splitQuarter.get(1).getSizeInBytes(); int quarter3size = splitQuarter.get(2).getSizeInBytes(); int quarter4size = splitQuarter.get(3).getSizeInBytes(); double expectedQuarterSizeMin = sizeInBytes * 0.2; double expectedQuarterSizeMax = sizeInBytes * 0.3; assertTrue(quarter1size > expectedQuarterSizeMin && quarter1size < expectedQuarterSizeMax, format("quarter1size is %s, should be between %s and %s", quarter1size, expectedQuarterSizeMin, expectedQuarterSizeMax)); assertTrue(quarter2size > expectedQuarterSizeMin && quarter2size < expectedQuarterSizeMax, format("quarter2size is %s, should be between %s and %s", quarter2size, expectedQuarterSizeMin, expectedQuarterSizeMax)); assertTrue(quarter3size > expectedQuarterSizeMin && quarter3size < expectedQuarterSizeMax, format("quarter3size is %s, should be between %s and %s", quarter3size, expectedQuarterSizeMin, expectedQuarterSizeMax)); assertTrue(quarter4size > expectedQuarterSizeMin && quarter4size < expectedQuarterSizeMax, format("quarter4size is %s, should be between %s and %s", quarter4size, expectedQuarterSizeMin, expectedQuarterSizeMax)); assertEquals(quarter1size + quarter2size + quarter3size + quarter4size, sizeInBytes); } private static InterleavedBlockBuilder createBlockBuilderWithValues(Slice[] expectedValues) { InterleavedBlockBuilder blockBuilder = new InterleavedBlockBuilder(TYPES, new BlockBuilderStatus(), expectedValues.length); int valueIndex = 0; while (valueIndex < expectedValues.length) { for (Type type : TYPES) { Class<?> javaType = type.getJavaType(); Slice expectedValue = expectedValues[valueIndex]; if (expectedValue == null) { blockBuilder.appendNull(); } else if (javaType == boolean.class) { type.writeBoolean(blockBuilder, expectedValue.getByte(0) != 0); } else if (javaType == long.class) { type.writeLong(blockBuilder, expectedValue.getLong(0)); } else if (javaType == double.class) { type.writeDouble(blockBuilder, expectedValue.getDouble(0)); } else { blockBuilder.writeBytes(expectedValue, 0, expectedValue.length()).closeEntry(); } valueIndex++; } } return blockBuilder; } private void assertValues(Slice[] expectedValues) { InterleavedBlockBuilder blockBuilder = createBlockBuilderWithValues(expectedValues); assertBlock(blockBuilder, expectedValues); assertBlock(blockBuilder.build(), expectedValues); } private static Slice[] createExpectedValues() { Slice[] expectedValues = new Slice[POSITION_COUNT * 2]; for (int position = 0; position < POSITION_COUNT; position++) { expectedValues[position * 2] = createExpectedValue(SIZE_0[position]); expectedValues[position * 2 + 1] = createExpectedValue(SIZE_1); } return expectedValues; } protected static Object[] alternatingNullValues(Object[] objects) { Object[] objectsWithNulls = (Object[]) Array.newInstance(objects.getClass().getComponentType(), objects.length * 2 + 2); for (int i = 0; i < objects.length; i += 2) { objectsWithNulls[i * 2] = null; objectsWithNulls[i * 2 + 1] = null; objectsWithNulls[i * 2 + 2] = objects[i]; objectsWithNulls[i * 2 + 3] = objects[i + 1]; } objectsWithNulls[objectsWithNulls.length - 2] = null; objectsWithNulls[objectsWithNulls.length - 1] = null; return objectsWithNulls; } @Override protected <T> void assertBlockPosition(Block block, int position, T expectedValue) { assertInterleavedPosition(TYPES, block, position, expectedValue); Type type = TYPES.get(position % TYPES.size()); assertInterleavedPosition(ImmutableList.of(type), block.getSingleValueBlock(position), 0, expectedValue); int alignedPosition = position - position % COLUMN_COUNT; assertInterleavedPosition(ImmutableList.of(type), block.getRegion(alignedPosition, COLUMN_COUNT), position - alignedPosition, expectedValue); assertInterleavedPosition(TYPES, block.getRegion(0, alignedPosition + COLUMN_COUNT), position, expectedValue); assertInterleavedPosition(ImmutableList.of(type), block.getRegion(alignedPosition, block.getPositionCount() - alignedPosition), position - alignedPosition, expectedValue); assertInterleavedPosition(ImmutableList.of(type), block.copyRegion(alignedPosition, COLUMN_COUNT), position - alignedPosition, expectedValue); assertInterleavedPosition(TYPES, block.copyRegion(0, alignedPosition + COLUMN_COUNT), position, expectedValue); assertInterleavedPosition(ImmutableList.of(type), block.copyRegion(alignedPosition, block.getPositionCount() - alignedPosition), position - alignedPosition, expectedValue); assertInterleavedPosition(TYPES, block.copyPositions(IntStream.range(alignedPosition, alignedPosition + COLUMN_COUNT).boxed().collect(Collectors.toList())), position % COLUMN_COUNT, expectedValue); } private <T> void assertInterleavedPosition(List<Type> types, Block block, int position, T expectedValue) { assertPositionValue(block, position, expectedValue); Type type = types.get(position % types.size()); if (expectedValue == null) { assertTrue(block.isNull(position)); } else if (BIGINT.equals(type)) { Slice expectedSliceValue = (Slice) expectedValue; assertEquals(expectedSliceValue.length(), Longs.BYTES); assertEquals(block.getLong(position, 0), expectedSliceValue.getLong(0)); } else if (VARCHAR.equals(type)) { Slice expectedSliceValue = (Slice) expectedValue; assertSlicePosition(block, position, expectedSliceValue); } else { throw new IllegalArgumentException("Unsupported type " + type); } } @Override protected List<Block> splitBlock(Block block, int count) { double entriesPerSplit = block.getPositionCount() * 1.0 / count / COLUMN_COUNT; ImmutableList.Builder<Block> result = ImmutableList.builder(); for (int i = 0; i < count; i++) { int startPosition = toIntExact(Math.round(entriesPerSplit * i) * COLUMN_COUNT); int endPosition = toIntExact(Math.round(entriesPerSplit * (i + 1)) * COLUMN_COUNT); result.add(block.getRegion(startPosition, endPosition - startPosition)); } return result.build(); } @Override protected boolean isByteAccessSupported() { return false; } @Override protected boolean isShortAccessSupported() { return false; } @Override protected boolean isIntAccessSupported() { return false; } @Override protected boolean isLongAccessSupported() { return false; } @Override protected boolean isSliceAccessSupported() { return false; } }