/* * 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.rcfile; import com.facebook.presto.spi.type.DecimalType; import com.facebook.presto.spi.type.SqlDate; import com.facebook.presto.spi.type.SqlDecimal; import com.facebook.presto.spi.type.SqlTimestamp; import com.facebook.presto.spi.type.SqlVarbinary; import com.google.common.collect.ContiguousSet; import com.google.common.collect.DiscreteDomain; import com.google.common.collect.ImmutableList; import com.google.common.collect.Range; import org.joda.time.DateTimeZone; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.math.BigInteger; import java.util.ArrayList; import java.util.List; import static com.facebook.presto.rcfile.RcFileTester.Format.BINARY; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.BooleanType.BOOLEAN; import static com.facebook.presto.spi.type.DateType.DATE; import static com.facebook.presto.spi.type.DoubleType.DOUBLE; import static com.facebook.presto.spi.type.IntegerType.INTEGER; import static com.facebook.presto.spi.type.RealType.REAL; import static com.facebook.presto.spi.type.SmallintType.SMALLINT; import static com.facebook.presto.spi.type.TimeZoneKey.UTC_KEY; import static com.facebook.presto.spi.type.TimestampType.TIMESTAMP; import static com.facebook.presto.spi.type.TinyintType.TINYINT; import static com.facebook.presto.spi.type.VarbinaryType.VARBINARY; import static com.facebook.presto.spi.type.VarcharType.VARCHAR; import static com.google.common.collect.Iterables.cycle; import static com.google.common.collect.Iterables.limit; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Collections.nCopies; import static java.util.stream.Collectors.toList; import static org.testng.Assert.assertEquals; public abstract class AbstractTestRcFileReader { private static final DecimalType DECIMAL_TYPE_PRECISION_2 = DecimalType.createDecimalType(2, 1); private static final DecimalType DECIMAL_TYPE_PRECISION_4 = DecimalType.createDecimalType(4, 2); private static final DecimalType DECIMAL_TYPE_PRECISION_8 = DecimalType.createDecimalType(8, 4); private static final DecimalType DECIMAL_TYPE_PRECISION_17 = DecimalType.createDecimalType(17, 8); private static final DecimalType DECIMAL_TYPE_PRECISION_18 = DecimalType.createDecimalType(18, 8); private static final DecimalType DECIMAL_TYPE_PRECISION_38 = DecimalType.createDecimalType(38, 16); private final RcFileTester tester; public AbstractTestRcFileReader(RcFileTester tester) { this.tester = tester; } @BeforeClass public void setUp() { assertEquals(DateTimeZone.getDefault(), RcFileTester.HIVE_STORAGE_TIME_ZONE); } @Test public void testNoData() throws Exception { tester.testRoundTrip(VARCHAR, ImmutableList.of()); } @Test public void testBooleanSequence() throws Exception { tester.testRoundTrip(BOOLEAN, limit(cycle(ImmutableList.of(true, false, false)), 3_000)); } @Test public void testByteSequence() throws Exception { tester.testRoundTrip( TINYINT, intsBetween(-31_234, 31_234).stream() .filter(i -> i % 11 == 0) .map(Integer::byteValue) // truncate values to byte range .collect(toList())); } @Test public void testShortSequence() throws Exception { tester.testRoundTrip( SMALLINT, intsBetween(-31_234, 31_234).stream() .filter(i -> i % 11 == 0) .map(Integer::shortValue) // truncate values to short range .collect(toList())); tester.testRoundTrip( SMALLINT, intsBetween(Short.MIN_VALUE, Short.MIN_VALUE + 1000).stream() .map(Integer::shortValue) // truncate values to short range .collect(toList())); tester.testRoundTrip( SMALLINT, intsBetween(Short.MAX_VALUE - 1000, Short.MAX_VALUE).stream() .map(Integer::shortValue) // truncate values to short range .collect(toList())); } @Test public void testIntSequence() throws Exception { tester.testRoundTrip( INTEGER, intsBetween(-31_234, 31_234).stream() .filter(i -> i % 11 == 0) .collect(toList())); tester.testRoundTrip(INTEGER, intsBetween(Integer.MIN_VALUE, Integer.MIN_VALUE + 1000)); tester.testRoundTrip(INTEGER, intsBetween(Integer.MAX_VALUE - 1000, Integer.MAX_VALUE)); } @Test public void testLongSequence() throws Exception { tester.testRoundTrip( BIGINT, longsBetween(-31_234, 31_234).stream() .filter(i -> i % 11 == 0) .collect(toList())); tester.testRoundTrip(BIGINT, longsBetween(Long.MIN_VALUE, Long.MIN_VALUE + 1000)); tester.testRoundTrip(BIGINT, longsBetween(Long.MAX_VALUE - 1000, Long.MAX_VALUE)); } @Test public void testDateSequence() throws Exception { tester.testRoundTrip( DATE, intsBetween(-31_234, 31_234).stream() .filter(i -> i % 11 == 0) .map(SqlDate::new) .collect(toList())); } @Test public void testTimestampSequence() throws Exception { tester.testRoundTrip( TIMESTAMP, intsBetween(-31_234, 31_234).stream() .filter(i -> i % 19 == 0) .map(timestamp -> new SqlTimestamp(timestamp, UTC_KEY)) .collect(toList())); } @Test public void testFloatSequence() throws Exception { // this results in rounding errors inside of structural types tester.testRoundTrip( REAL, doubleSequence(-500.12f, 1.0f, 3_000).stream() .map(Double::floatValue) .collect(toList())); } @Test public void testDoubleSequence() throws Exception { tester.testRoundTrip(DOUBLE, doubleSequence(-500.12, 1, 3_000)); } @Test public void testDecimalSequence() throws Exception { tester.testRoundTrip(DECIMAL_TYPE_PRECISION_2, decimalSequence("-30", "1", 60, 2, 1)); tester.testRoundTrip(DECIMAL_TYPE_PRECISION_4, decimalSequence("-3000", "6", 1_000, 4, 2)); tester.testRoundTrip(DECIMAL_TYPE_PRECISION_8, decimalSequence("-3000000", "6000", 1_000, 8, 4)); tester.testRoundTrip(DECIMAL_TYPE_PRECISION_17, decimalSequence("-30000000000", "60000000", 1_000, 17, 8)); tester.testRoundTrip(DECIMAL_TYPE_PRECISION_18, decimalSequence("-30000000000", "60000000", 1_000, 18, 8)); tester.testRoundTrip(DECIMAL_TYPE_PRECISION_38, decimalSequence("-3000000000000000000", "6000000000000000", 1_000, 38, 16)); } @Test public void testStringSequence() throws Exception { tester.testRoundTrip( VARCHAR, intsBetween(0, 31_234).stream() .filter(i -> i % 19 == 0) .map(Object::toString) .collect(toList())); } @Test public void testEmptyStringSequence() throws Exception { tester.testRoundTrip(VARCHAR, nCopies(3_000, "")); } @Test public void testBinarySequence() throws Exception { tester.testRoundTrip( VARBINARY, intsBetween(0, 30_000).stream() .filter(i -> i % 19 == 0) .map(Object::toString) .map(string -> string.getBytes(UTF_8)) .map(SqlVarbinary::new) .collect(toList())); } @Test public void testEmptyBinarySequence() throws Exception { // Binary serde can not serialize an empty binary sequence tester.testRoundTrip(VARBINARY, nCopies(3_000, new SqlVarbinary(new byte[0])), BINARY); } private static List<Double> doubleSequence(double start, double step, int items) { List<Double> values = new ArrayList<>(); double nextValue = start; for (int i = 0; i < items; i++) { values.add(nextValue); nextValue += step; } return values; } private static ContiguousSet<Long> longsBetween(long lowerInclusive, long upperExclusive) { return ContiguousSet.create(Range.openClosed(lowerInclusive, upperExclusive), DiscreteDomain.longs()); } private static ContiguousSet<Integer> intsBetween(int lowerInclusive, int upperExclusive) { return ContiguousSet.create(Range.openClosed(lowerInclusive, upperExclusive), DiscreteDomain.integers()); } private static List<SqlDecimal> decimalSequence(String start, String step, int items, int precision, int scale) { BigInteger decimalStep = new BigInteger(step); List<SqlDecimal> values = new ArrayList<>(); BigInteger nextValue = new BigInteger(start); for (int i = 0; i < items; i++) { values.add(new SqlDecimal(nextValue, precision, scale)); nextValue = nextValue.add(decimalStep); } return values; } }