Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.trino.plugin.faker.FakerPageSource Maven / Gradle / Ivy
/*
* 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 io.trino.plugin.faker;
import io.airlift.slice.Slice;
import io.airlift.slice.Slices;
import io.trino.spi.Page;
import io.trino.spi.PageBuilder;
import io.trino.spi.TrinoException;
import io.trino.spi.block.BlockBuilder;
import io.trino.spi.connector.ColumnHandle;
import io.trino.spi.connector.ConnectorPageSource;
import io.trino.spi.predicate.Domain;
import io.trino.spi.predicate.Range;
import io.trino.spi.predicate.TupleDomain;
import io.trino.spi.type.CharType;
import io.trino.spi.type.DecimalType;
import io.trino.spi.type.Decimals;
import io.trino.spi.type.Int128;
import io.trino.spi.type.Int128Math;
import io.trino.spi.type.LongTimeWithTimeZone;
import io.trino.spi.type.LongTimestamp;
import io.trino.spi.type.LongTimestampWithTimeZone;
import io.trino.spi.type.TimeType;
import io.trino.spi.type.TimeWithTimeZoneType;
import io.trino.spi.type.TimeZoneKey;
import io.trino.spi.type.TimestampType;
import io.trino.spi.type.TimestampWithTimeZoneType;
import io.trino.spi.type.Type;
import io.trino.spi.type.UuidType;
import io.trino.spi.type.VarbinaryType;
import io.trino.spi.type.VarcharType;
import io.trino.type.IpAddressType;
import net.datafaker.Faker;
import java.math.BigInteger;
import java.net.Inet4Address;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Random;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static io.trino.plugin.faker.FakerMetadata.ROW_ID_COLUMN_NAME;
import static io.trino.spi.StandardErrorCode.INVALID_ROW_FILTER;
import static io.trino.spi.type.BigintType.BIGINT;
import static io.trino.spi.type.BooleanType.BOOLEAN;
import static io.trino.spi.type.DateTimeEncoding.packDateTimeWithZone;
import static io.trino.spi.type.DateTimeEncoding.packTimeWithTimeZone;
import static io.trino.spi.type.DateTimeEncoding.unpackMillisUtc;
import static io.trino.spi.type.DateTimeEncoding.unpackOffsetMinutes;
import static io.trino.spi.type.DateTimeEncoding.unpackTimeNanos;
import static io.trino.spi.type.DateTimeEncoding.unpackZoneKey;
import static io.trino.spi.type.DateType.DATE;
import static io.trino.spi.type.DoubleType.DOUBLE;
import static io.trino.spi.type.IntegerType.INTEGER;
import static io.trino.spi.type.LongTimestampWithTimeZone.fromEpochMillisAndFraction;
import static io.trino.spi.type.RealType.REAL;
import static io.trino.spi.type.SmallintType.SMALLINT;
import static io.trino.spi.type.Timestamps.NANOSECONDS_PER_DAY;
import static io.trino.spi.type.Timestamps.PICOSECONDS_PER_DAY;
import static io.trino.spi.type.Timestamps.PICOSECONDS_PER_MICROSECOND;
import static io.trino.spi.type.Timestamps.PICOSECONDS_PER_MILLISECOND;
import static io.trino.spi.type.Timestamps.roundDiv;
import static io.trino.spi.type.TinyintType.TINYINT;
import static io.trino.spi.type.UuidType.UUID;
import static io.trino.spi.type.VarcharType.VARCHAR;
import static io.trino.type.IntervalDayTimeType.INTERVAL_DAY_TIME;
import static io.trino.type.IntervalYearMonthType.INTERVAL_YEAR_MONTH;
import static io.trino.type.IpAddressType.IPADDRESS;
import static java.lang.Float.floatToRawIntBits;
import static java.lang.Float.intBitsToFloat;
import static java.lang.Math.toIntExact;
import static java.lang.System.arraycopy;
import static java.util.Objects.requireNonNull;
class FakerPageSource
implements ConnectorPageSource
{
static final long[] POWERS_OF_TEN = {
1L,
10L,
100L,
1_000L,
10_000L,
100_000L,
1_000_000L,
10_000_000L,
100_000_000L,
1_000_000_000L,
10_000_000_000L,
100_000_000_000L,
1_000_000_000_000L,
10_000_000_000_000L,
100_000_000_000_000L,
1_000_000_000_000_000L,
10_000_000_000_000_000L,
100_000_000_000_000_000L,
1_000_000_000_000_000_000L
};
private static final int ROWS_PER_PAGE = 4096;
private final Random random;
private final Faker faker;
private final long limit;
private final List generators;
private long completedRows;
private final PageBuilder pageBuilder;
private boolean closed;
FakerPageSource(
Faker faker,
Random random,
List columns,
TupleDomain constraint,
long offset,
long limit)
{
this.faker = requireNonNull(faker, "faker is null");
this.random = requireNonNull(random, "random is null");
List types = requireNonNull(columns, "columns is null")
.stream()
.map(FakerColumnHandle::type)
.collect(toImmutableList());
requireNonNull(constraint, "constraint is null");
this.limit = limit;
this.generators = columns
.stream()
.map(column -> getGenerator(column, constraint, offset))
.collect(toImmutableList());
this.pageBuilder = new PageBuilder(types);
}
private Generator getGenerator(
FakerColumnHandle column,
TupleDomain constraint,
long offset)
{
if (ROW_ID_COLUMN_NAME.equals(column.name())) {
return new Generator() {
long currentRowId = offset;
@Override
public void accept(BlockBuilder blockBuilder)
{
BIGINT.writeLong(blockBuilder, currentRowId++);
}
};
}
return constraintedValueGenerator(
column,
constraint.getDomains().get().getOrDefault(column, Domain.all(column.type())));
}
@Override
public long getCompletedBytes()
{
return 0;
}
@Override
public long getReadTimeNanos()
{
return 0;
}
@Override
public boolean isFinished()
{
return closed && pageBuilder.isEmpty();
}
@Override
public Page getNextPage()
{
if (!closed) {
int positions = (int) Math.min(limit - completedRows, ROWS_PER_PAGE);
if (positions <= 0) {
closed = true;
}
else {
pageBuilder.declarePositions(positions);
for (int column = 0; column < generators.size(); column++) {
BlockBuilder blockBuilder = pageBuilder.getBlockBuilder(column);
Generator generator = generators.get(column);
for (int i = 0; i < positions; i++) {
generator.accept(blockBuilder);
}
}
completedRows += positions;
}
}
// only return a page if the buffer is full, or we are finishing
if ((closed && !pageBuilder.isEmpty()) || pageBuilder.isFull()) {
Page page = pageBuilder.build();
pageBuilder.reset();
return page;
}
return null;
}
@Override
public long getMemoryUsage()
{
return pageBuilder.getRetainedSizeInBytes();
}
@Override
public void close()
{
closed = true;
}
private Generator constraintedValueGenerator(FakerColumnHandle handle, Domain domain)
{
if (domain.isSingleValue()) {
ObjectWriter singleValueWriter = objectWriter(handle.type());
return (blockBuilder) -> singleValueWriter.accept(blockBuilder, domain.getSingleValue());
}
if (domain.getValues().isDiscreteSet()) {
List values = domain.getValues().getDiscreteSet();
ObjectWriter singleValueWriter = objectWriter(handle.type());
return (blockBuilder) -> singleValueWriter.accept(blockBuilder, values.get(random.nextInt(values.size())));
}
if (domain.getValues().getRanges().getRangeCount() > 1) {
// this would require calculating weights for each range to retain uniform distribution
throw new TrinoException(INVALID_ROW_FILTER, "Generating random values from more than one range is not supported");
}
Generator generator = randomValueGenerator(handle, domain.getValues().getRanges().getSpan());
if (handle.nullProbability() == 0) {
return generator;
}
return (blockBuilder) -> {
if (random.nextDouble() <= handle.nullProbability()) {
blockBuilder.appendNull();
}
else {
generator.accept(blockBuilder);
}
};
}
private Generator randomValueGenerator(FakerColumnHandle handle, Range range)
{
if (handle.generator() != null) {
if (!range.isAll()) {
throw new TrinoException(INVALID_ROW_FILTER, "Predicates for columns with a generator expression are not supported");
}
return (blockBuilder) -> VARCHAR.writeSlice(blockBuilder, Slices.utf8Slice(faker.expression(handle.generator())));
}
Type type = handle.type();
// check every type in order defined in StandardTypes
if (BIGINT.equals(type)) {
return (blockBuilder) -> BIGINT.writeLong(blockBuilder, generateLong(range, 1));
}
if (INTEGER.equals(type)) {
return (blockBuilder) -> INTEGER.writeLong(blockBuilder, generateInt(range));
}
if (SMALLINT.equals(type)) {
return (blockBuilder) -> SMALLINT.writeLong(blockBuilder, generateShort(range));
}
if (TINYINT.equals(type)) {
return (blockBuilder) -> TINYINT.writeLong(blockBuilder, generateTiny(range));
}
if (BOOLEAN.equals(type)) {
if (!range.isAll()) {
throw new TrinoException(INVALID_ROW_FILTER, "Range or not a single value predicates for boolean columns are not supported");
}
return (blockBuilder) -> BOOLEAN.writeBoolean(blockBuilder, random.nextBoolean());
}
if (DATE.equals(type)) {
return (blockBuilder) -> DATE.writeLong(blockBuilder, generateInt(range));
}
if (type instanceof DecimalType decimalType) {
return decimalGenerator(range, decimalType);
}
if (REAL.equals(type)) {
return (blockBuilder) -> REAL.writeLong(blockBuilder, floatToRawIntBits(generateFloat(range)));
}
if (DOUBLE.equals(type)) {
return (blockBuilder) -> DOUBLE.writeDouble(blockBuilder, generateDouble(range));
}
// not supported: HYPER_LOG_LOG, QDIGEST, TDIGEST, P4_HYPER_LOG_LOG
if (INTERVAL_DAY_TIME.equals(type)) {
return (blockBuilder) -> INTERVAL_DAY_TIME.writeLong(blockBuilder, generateLong(range, 1));
}
if (INTERVAL_YEAR_MONTH.equals(type)) {
return (blockBuilder) -> INTERVAL_YEAR_MONTH.writeLong(blockBuilder, generateInt(range));
}
if (type instanceof TimestampType) {
return timestampGenerator(range, (TimestampType) type);
}
if (type instanceof TimestampWithTimeZoneType) {
return timestampWithTimeZoneGenerator(range, (TimestampWithTimeZoneType) type);
}
if (type instanceof TimeType timeType) {
long factor = POWERS_OF_TEN[12 - timeType.getPrecision()];
return (blockBuilder) -> timeType.writeLong(blockBuilder, generateLongDefaults(range, factor, 0, PICOSECONDS_PER_DAY));
}
if (type instanceof TimeWithTimeZoneType) {
return timeWithTimeZoneGenerator(range, (TimeWithTimeZoneType) type);
}
if (type instanceof VarbinaryType varType) {
if (!range.isAll()) {
throw new TrinoException(INVALID_ROW_FILTER, "Predicates for varbinary columns are not supported");
}
return (blockBuilder) -> varType.writeSlice(blockBuilder, Slices.utf8Slice(faker.lorem().sentence(3 + random.nextInt(38))));
}
if (type instanceof VarcharType varcharType) {
if (!range.isAll()) {
throw new TrinoException(INVALID_ROW_FILTER, "Predicates for varchar columns are not supported");
}
if (varcharType.getLength().isPresent()) {
int length = varcharType.getLength().get();
return (blockBuilder) -> varcharType.writeSlice(blockBuilder, Slices.utf8Slice(faker.lorem().maxLengthSentence(random.nextInt(length))));
}
return (blockBuilder) -> varcharType.writeSlice(blockBuilder, Slices.utf8Slice(faker.lorem().sentence(3 + random.nextInt(38))));
}
if (type instanceof CharType charType) {
if (!range.isAll()) {
throw new TrinoException(INVALID_ROW_FILTER, "Predicates for char columns are not supported");
}
return (blockBuilder) -> charType.writeSlice(blockBuilder, Slices.utf8Slice(faker.lorem().maxLengthSentence(charType.getLength())));
}
// not supported: ROW, ARRAY, MAP, JSON
if (type instanceof IpAddressType) {
return generateIpV4(range);
}
// not supported: GEOMETRY
if (type instanceof UuidType) {
return generateUUID(range);
}
throw new IllegalArgumentException("Unsupported type " + type);
}
private ObjectWriter objectWriter(Type type)
{
// check every type in order defined in StandardTypes
if (BIGINT.equals(type)) {
return (blockBuilder, value) -> BIGINT.writeLong(blockBuilder, (Long) value);
}
if (INTEGER.equals(type)) {
return (blockBuilder, value) -> INTEGER.writeLong(blockBuilder, (Long) value);
}
if (SMALLINT.equals(type)) {
return (blockBuilder, value) -> SMALLINT.writeLong(blockBuilder, (Long) value);
}
if (TINYINT.equals(type)) {
return (blockBuilder, value) -> TINYINT.writeLong(blockBuilder, (Long) value);
}
if (BOOLEAN.equals(type)) {
return (blockBuilder, value) -> BOOLEAN.writeBoolean(blockBuilder, (Boolean) value);
}
if (DATE.equals(type)) {
return (blockBuilder, value) -> DATE.writeLong(blockBuilder, (Long) value);
}
if (type instanceof DecimalType decimalType) {
if (decimalType.isShort()) {
return (blockBuilder, value) -> decimalType.writeLong(blockBuilder, (Long) value);
}
else {
return decimalType::writeObject;
}
}
if (REAL.equals(type)) {
return (blockBuilder, value) -> REAL.writeLong(blockBuilder, (Long) value);
}
if (DOUBLE.equals(type)) {
return (blockBuilder, value) -> DOUBLE.writeDouble(blockBuilder, (Double) value);
}
// not supported: HYPER_LOG_LOG, QDIGEST, TDIGEST, P4_HYPER_LOG_LOG
if (INTERVAL_DAY_TIME.equals(type)) {
return (blockBuilder, value) -> INTERVAL_DAY_TIME.writeLong(blockBuilder, (Long) value);
}
if (INTERVAL_YEAR_MONTH.equals(type)) {
return (blockBuilder, value) -> INTERVAL_YEAR_MONTH.writeLong(blockBuilder, (Long) value);
}
if (type instanceof TimestampType tzType) {
if (tzType.isShort()) {
return (blockBuilder, value) -> tzType.writeLong(blockBuilder, (Long) value);
}
else {
return tzType::writeObject;
}
}
if (type instanceof TimestampWithTimeZoneType tzType) {
if (tzType.isShort()) {
return (blockBuilder, value) -> tzType.writeLong(blockBuilder, (Long) value);
}
else {
return tzType::writeObject;
}
}
if (type instanceof TimeType timeType) {
return (blockBuilder, value) -> timeType.writeLong(blockBuilder, (Long) value);
}
if (type instanceof TimeWithTimeZoneType tzType) {
if (tzType.isShort()) {
return (blockBuilder, value) -> tzType.writeLong(blockBuilder, (Long) value);
}
else {
return tzType::writeObject;
}
}
if (type instanceof VarbinaryType varType) {
return (blockBuilder, value) -> varType.writeSlice(blockBuilder, (Slice) value);
}
if (type instanceof VarcharType varType) {
return (blockBuilder, value) -> varType.writeSlice(blockBuilder, (Slice) value);
}
if (type instanceof CharType charType) {
return (blockBuilder, value) -> charType.writeSlice(blockBuilder, (Slice) value);
}
// not supported: ROW, ARRAY, MAP, JSON
if (type instanceof IpAddressType) {
return (blockBuilder, value) -> IPADDRESS.writeSlice(blockBuilder, (Slice) value);
}
// not supported: GEOMETRY
if (type instanceof UuidType) {
return (blockBuilder, value) -> UUID.writeSlice(blockBuilder, (Slice) value);
}
throw new IllegalArgumentException("Unsupported type " + type);
}
private long generateLong(Range range, long factor)
{
return generateLongDefaults(range, factor, Long.MIN_VALUE, Long.MAX_VALUE);
}
private long generateLongDefaults(Range range, long factor, long min, long max)
{
return faker.number().numberBetween(
roundDiv((long) range.getLowValue().orElse(min), factor) + (!range.isLowUnbounded() && !range.isLowInclusive() ? 1 : 0),
// TODO does the inclusion only apply to positive numbers?
roundDiv((long) range.getHighValue().orElse(max), factor) + (!range.isHighUnbounded() && range.isHighInclusive() ? 1 : 0)) * factor;
}
private int generateInt(Range range)
{
return (int) faker.number().numberBetween(
(long) range.getLowValue().orElse((long) Integer.MIN_VALUE) + (!range.isLowUnbounded() && !range.isLowInclusive() ? 1 : 0),
(long) range.getHighValue().orElse((long) Integer.MAX_VALUE) + (!range.isHighUnbounded() && range.isHighInclusive() ? 1 : 0));
}
private short generateShort(Range range)
{
return (short) faker.number().numberBetween(
(long) range.getLowValue().orElse((long) Short.MIN_VALUE) + (!range.isLowUnbounded() && !range.isLowInclusive() ? 1 : 0),
(long) range.getHighValue().orElse((long) Short.MAX_VALUE) + (!range.isHighUnbounded() && range.isHighInclusive() ? 1 : 0));
}
private byte generateTiny(Range range)
{
return (byte) faker.number().numberBetween(
(long) range.getLowValue().orElse((long) Byte.MIN_VALUE) + (!range.isLowUnbounded() && !range.isLowInclusive() ? 1 : 0),
(long) range.getHighValue().orElse((long) Byte.MAX_VALUE) + (!range.isHighUnbounded() && range.isHighInclusive() ? 1 : 0));
}
private float generateFloat(Range range)
{
// TODO normalize ranges in applyFilter, so they always have bounds
float minValue = range.getLowValue().map(v -> intBitsToFloat(toIntExact((long) v))).orElse(Float.MIN_VALUE);
if (!range.isLowUnbounded() && !range.isLowInclusive()) {
minValue = Math.nextUp(minValue);
}
float maxValue = range.getHighValue().map(v -> intBitsToFloat(toIntExact((long) v))).orElse(Float.MAX_VALUE);
if (!range.isHighUnbounded() && !range.isHighInclusive()) {
maxValue = Math.nextDown(maxValue);
}
return minValue + (maxValue - minValue) * random.nextFloat();
}
private double generateDouble(Range range)
{
double minValue = (double) range.getLowValue().orElse(Double.MIN_VALUE);
if (!range.isLowUnbounded() && !range.isLowInclusive()) {
minValue = Math.nextUp(minValue);
}
double maxValue = (double) range.getHighValue().orElse(Double.MAX_VALUE);
if (!range.isHighUnbounded() && !range.isHighInclusive()) {
maxValue = Math.nextDown(maxValue);
}
return minValue + (maxValue - minValue) * random.nextDouble();
}
private Generator decimalGenerator(Range range, DecimalType decimalType)
{
if (decimalType.isShort()) {
long min = -999999999999999999L / POWERS_OF_TEN[18 - decimalType.getPrecision()];
long max = 999999999999999999L / POWERS_OF_TEN[18 - decimalType.getPrecision()];
return (blockBuilder) -> decimalType.writeLong(blockBuilder, generateLongDefaults(range, 1, min, max));
}
Int128 low = (Int128) range.getLowValue().orElse(Decimals.MIN_UNSCALED_DECIMAL);
Int128 high = (Int128) range.getHighValue().orElse(Decimals.MAX_UNSCALED_DECIMAL);
if (!range.isLowUnbounded() && !range.isLowInclusive()) {
long[] result = new long[2];
Int128Math.add(low.getHigh(), low.getLow(), 0, 1, result, 0);
low = Int128.valueOf(result);
}
if (!range.isHighUnbounded() && range.isHighInclusive()) {
long[] result = new long[2];
Int128Math.add(high.getHigh(), high.getLow(), 0, 1, result, 0);
high = Int128.valueOf(result);
}
BigInteger currentRange = BigInteger.valueOf(Long.MAX_VALUE);
BigInteger desiredRange = high.toBigInteger().subtract(low.toBigInteger());
Int128 finalLow = low;
return (blockBuilder) -> decimalType.writeObject(blockBuilder, Int128.valueOf(
new BigInteger(63, random).multiply(desiredRange).divide(currentRange).add(finalLow.toBigInteger())));
}
private Generator timestampGenerator(Range range, TimestampType tzType)
{
if (tzType.isShort()) {
long factor = POWERS_OF_TEN[6 - tzType.getPrecision()];
return (blockBuilder) -> tzType.writeLong(blockBuilder, generateLong(range, factor));
}
LongTimestamp low = (LongTimestamp) range.getLowValue()
.orElse(new LongTimestamp(Long.MIN_VALUE, 0));
LongTimestamp high = (LongTimestamp) range.getHighValue()
.orElse(new LongTimestamp(Long.MAX_VALUE, PICOSECONDS_PER_MICROSECOND - 1));
int factor;
if (tzType.getPrecision() <= 6) {
factor = (int) POWERS_OF_TEN[6 - tzType.getPrecision()];
low = new LongTimestamp(
roundDiv(low.getEpochMicros(), factor) + (!range.isLowUnbounded() && !range.isLowInclusive() ? 1 : 0),
0);
high = new LongTimestamp(
roundDiv(high.getEpochMicros(), factor) + (!range.isHighUnbounded() && range.isHighInclusive() ? 1 : 0),
0);
}
else {
factor = (int) POWERS_OF_TEN[12 - tzType.getPrecision()];
int lowPicosOfMicro = roundDiv(low.getPicosOfMicro(), factor) + (!range.isLowUnbounded() && !range.isLowInclusive() ? 1 : 0);
low = new LongTimestamp(
low.getEpochMicros() - (lowPicosOfMicro < 0 ? 1 : 0),
(lowPicosOfMicro + factor) % factor);
int highPicosOfMicro = roundDiv(high.getPicosOfMicro(), factor) + (!range.isHighUnbounded() && range.isHighInclusive() ? 1 : 0);
high = new LongTimestamp(
high.getEpochMicros() + (highPicosOfMicro > factor ? 1 : 0),
highPicosOfMicro % factor);
}
LongTimestamp finalLow = low;
LongTimestamp finalHigh = high;
return (blockBuilder) -> {
long epochMicros = faker.number().numberBetween(finalLow.getEpochMicros(), finalHigh.getEpochMicros());
if (tzType.getPrecision() <= 6) {
epochMicros *= factor;
tzType.writeObject(blockBuilder, new LongTimestamp(epochMicros * factor, 0));
return;
}
int picosOfMicro;
if (epochMicros == finalLow.getEpochMicros()) {
picosOfMicro = faker.number().numberBetween(
finalLow.getPicosOfMicro(),
finalLow.getEpochMicros() == finalHigh.getEpochMicros() ?
finalHigh.getPicosOfMicro()
: (int) POWERS_OF_TEN[tzType.getPrecision() - 6] - 1);
}
else if (epochMicros == finalHigh.getEpochMicros()) {
picosOfMicro = faker.number().numberBetween(0, finalHigh.getPicosOfMicro());
}
else {
picosOfMicro = faker.number().numberBetween(0, (int) POWERS_OF_TEN[tzType.getPrecision() - 6] - 1);
}
tzType.writeObject(blockBuilder, new LongTimestamp(epochMicros, picosOfMicro * factor));
};
}
private Generator timestampWithTimeZoneGenerator(Range range, TimestampWithTimeZoneType tzType)
{
if (tzType.isShort()) {
TimeZoneKey defaultTZ = range.getLowValue()
.map(v -> unpackZoneKey((long) v))
.orElse(range.getHighValue()
.map(v -> unpackZoneKey((long) v))
.orElse(TimeZoneKey.UTC_KEY));
long factor = POWERS_OF_TEN[3 - tzType.getPrecision()];
return (blockBuilder) -> {
long millis = faker.number().numberBetween(
roundDiv(unpackMillisUtc((long) range.getLowValue().orElse(Long.MIN_VALUE)), factor) + (!range.isLowUnbounded() && !range.isLowInclusive() ? 1 : 0),
roundDiv(unpackMillisUtc((long) range.getHighValue().orElse(Long.MAX_VALUE)), factor) + (!range.isHighUnbounded() && range.isHighInclusive() ? 1 : 0)) * factor;
tzType.writeLong(blockBuilder, packDateTimeWithZone(millis, defaultTZ));
};
}
short defaultTZ = range.getLowValue()
.map(v -> ((LongTimestampWithTimeZone) v).getTimeZoneKey())
.orElse(range.getHighValue()
.map(v -> ((LongTimestampWithTimeZone) v).getTimeZoneKey())
.orElse(TimeZoneKey.UTC_KEY.getKey()));
LongTimestampWithTimeZone low = (LongTimestampWithTimeZone) range.getLowValue()
.orElse(fromEpochMillisAndFraction(Long.MIN_VALUE >> 12, 0, defaultTZ));
LongTimestampWithTimeZone high = (LongTimestampWithTimeZone) range.getHighValue()
.orElse(fromEpochMillisAndFraction(Long.MAX_VALUE >> 12, PICOSECONDS_PER_MILLISECOND - 1, defaultTZ));
if (low.getTimeZoneKey() != high.getTimeZoneKey()) {
throw new TrinoException(INVALID_ROW_FILTER, "Range boundaries for timestamp with time zone columns must have the same time zone");
}
int factor = (int) POWERS_OF_TEN[12 - tzType.getPrecision()];
int lowPicosOfMilli = roundDiv(low.getPicosOfMilli(), factor) + (!range.isLowUnbounded() && !range.isLowInclusive() ? 1 : 0);
low = fromEpochMillisAndFraction(
low.getEpochMillis() - (lowPicosOfMilli < 0 ? 1 : 0),
(lowPicosOfMilli + factor) % factor,
low.getTimeZoneKey());
int highPicosOfMilli = roundDiv(high.getPicosOfMilli(), factor) + (!range.isHighUnbounded() && range.isHighInclusive() ? 1 : 0);
high = fromEpochMillisAndFraction(
high.getEpochMillis() + (highPicosOfMilli > factor ? 1 : 0),
highPicosOfMilli % factor,
high.getTimeZoneKey());
LongTimestampWithTimeZone finalLow = low;
LongTimestampWithTimeZone finalHigh = high;
return (blockBuilder) -> {
long millis = faker.number().numberBetween(finalLow.getEpochMillis(), finalHigh.getEpochMillis());
int picosOfMilli;
if (millis == finalLow.getEpochMillis()) {
picosOfMilli = faker.number().numberBetween(
finalLow.getPicosOfMilli(),
finalLow.getEpochMillis() == finalHigh.getEpochMillis() ?
finalHigh.getPicosOfMilli()
: (int) POWERS_OF_TEN[tzType.getPrecision() - 3] - 1);
}
else if (millis == finalHigh.getEpochMillis()) {
picosOfMilli = faker.number().numberBetween(0, finalHigh.getPicosOfMilli());
}
else {
picosOfMilli = faker.number().numberBetween(0, (int) POWERS_OF_TEN[tzType.getPrecision() - 3] - 1);
}
tzType.writeObject(blockBuilder, fromEpochMillisAndFraction(millis, picosOfMilli * factor, defaultTZ));
};
}
private Generator timeWithTimeZoneGenerator(Range range, TimeWithTimeZoneType timeType)
{
if (timeType.isShort()) {
int offsetMinutes = range.getLowValue()
.map(v -> unpackOffsetMinutes((long) v))
.orElse(range.getHighValue()
.map(v -> unpackOffsetMinutes((long) v))
.orElse(0));
long factor = POWERS_OF_TEN[9 - timeType.getPrecision()];
long low = roundDiv(range.getLowValue().map(v -> unpackTimeNanos((long) v)).orElse(0L), factor) + (!range.isLowUnbounded() && !range.isLowInclusive() ? 1 : 0);
long high = roundDiv(range.getHighValue().map(v -> unpackTimeNanos((long) v)).orElse(NANOSECONDS_PER_DAY), factor) + (!range.isHighUnbounded() && range.isHighInclusive() ? 1 : 0);
return (blockBuilder) -> {
long nanos = faker.number().numberBetween(low, high) * factor;
timeType.writeLong(blockBuilder, packTimeWithTimeZone(nanos, offsetMinutes));
};
}
int offsetMinutes = range.getLowValue()
.map(v -> ((LongTimeWithTimeZone) v).getOffsetMinutes())
.orElse(range.getHighValue()
.map(v -> ((LongTimeWithTimeZone) v).getOffsetMinutes())
.orElse(0));
LongTimeWithTimeZone low = (LongTimeWithTimeZone) range.getLowValue()
.orElse(new LongTimeWithTimeZone(0, offsetMinutes));
LongTimeWithTimeZone high = (LongTimeWithTimeZone) range.getHighValue()
.orElse(new LongTimeWithTimeZone(PICOSECONDS_PER_DAY, offsetMinutes));
if (low.getOffsetMinutes() != high.getOffsetMinutes()) {
throw new TrinoException(INVALID_ROW_FILTER, "Range boundaries for time with time zone columns must have the same time zone");
}
int factor = (int) POWERS_OF_TEN[12 - timeType.getPrecision()];
long longLow = roundDiv(low.getPicoseconds(), factor) + (!range.isLowUnbounded() && !range.isLowInclusive() ? 1 : 0);
long longHigh = roundDiv(high.getPicoseconds(), factor) + (!range.isHighUnbounded() && range.isHighInclusive() ? 1 : 0);
return (blockBuilder) -> {
long picoseconds = faker.number().numberBetween(longLow, longHigh) * factor;
timeType.writeObject(blockBuilder, new LongTimeWithTimeZone(picoseconds, offsetMinutes));
};
}
private Generator generateIpV4(Range range)
{
if (!range.isAll()) {
throw new TrinoException(INVALID_ROW_FILTER, "Predicates for ipaddress columns are not supported");
}
return (blockBuilder) -> {
byte[] address;
try {
address = Inet4Address.getByAddress(new byte[] {
(byte) (random.nextInt(254) + 2),
(byte) (random.nextInt(254) + 2),
(byte) (random.nextInt(254) + 2),
(byte) (random.nextInt(254) + 2)}).getAddress();
}
catch (UnknownHostException e) {
// ignore
blockBuilder.appendNull();
return;
}
byte[] bytes = new byte[16];
bytes[10] = (byte) 0xff;
bytes[11] = (byte) 0xff;
arraycopy(address, 0, bytes, 12, 4);
IPADDRESS.writeSlice(blockBuilder, Slices.wrappedBuffer(bytes, 0, 16));
};
}
private Generator generateUUID(Range range)
{
if (!range.isAll()) {
throw new TrinoException(INVALID_ROW_FILTER, "Predicates for uuid columns are not supported");
}
return (blockBuilder) -> {
java.util.UUID uuid = java.util.UUID.randomUUID();
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
bb.putLong(uuid.getMostSignificantBits());
bb.putLong(uuid.getLeastSignificantBits());
UUID.writeSlice(blockBuilder, Slices.wrappedBuffer(bb.array(), 0, 16));
};
}
@FunctionalInterface
private interface ObjectWriter
{
void accept(BlockBuilder blockBuilder, Object value);
}
@FunctionalInterface
private interface Generator
{
void accept(BlockBuilder blockBuilder);
}
}