IGNITE-21435: Sql. Catalog DefaultValue should not depend on Serializable. (#3627)
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DefaultValue.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DefaultValue.java
index 7fbdfd6..04f5eda 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DefaultValue.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DefaultValue.java
@@ -17,16 +17,30 @@
package org.apache.ignite.internal.catalog.commands;
-import java.io.Serializable;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.Period;
+import java.util.BitSet;
import java.util.Objects;
+import java.util.UUID;
+import org.apache.ignite.internal.type.NativeType;
+import org.apache.ignite.internal.type.NativeTypes;
+import org.apache.ignite.internal.util.io.IgniteDataInput;
+import org.apache.ignite.internal.util.io.IgniteDataOutput;
+import org.apache.ignite.sql.ColumnType;
import org.jetbrains.annotations.Nullable;
/**
* Definition of value provider to use as default.
*/
@SuppressWarnings("PublicInnerClass")
-public class DefaultValue implements Serializable {
- private static final long serialVersionUID = -3056041395340876711L;
+public abstract class DefaultValue {
/**
* Defines value provider as functional provider.
@@ -51,10 +65,20 @@
/** Types of the defaults. */
public enum Type {
/** Default is specified as a constant. */
- CONSTANT,
+ CONSTANT(0),
/** Default is specified as a call to a function. */
- FUNCTION_CALL
+ FUNCTION_CALL(1);
+
+ /** Represents absent of default value ({@code null}). */
+ private static final int NULL_VALUE = -1;
+
+ /** Type id used by serialization. */
+ private final int typeId;
+
+ Type(int typeId) {
+ this.typeId = typeId;
+ }
}
protected final Type type;
@@ -70,7 +94,7 @@
/** Defines default value provider as a function call. */
public static class FunctionCall extends DefaultValue {
- private static final long serialVersionUID = -8166753714497411236L;
+
private final String functionName;
private FunctionCall(String functionName) {
@@ -108,16 +132,25 @@
/** Defines default value provider as a constant. */
public static class ConstantValue extends DefaultValue {
- private static final long serialVersionUID = -5909897953153236118L;
- private final @Nullable Serializable value;
+ private final ColumnType columnType;
+
+ private final @Nullable Object value;
private ConstantValue(@Nullable Object value) {
super(Type.CONSTANT);
- this.value = (Serializable) value;
+
+ NativeType nativeType = NativeTypes.fromObject(value);
+
+ if (nativeType == null) {
+ columnType = ColumnType.NULL;
+ } else {
+ columnType = nativeType.spec().asColumnType();
+ }
+ this.value = value;
}
/** Returns value to use as default. */
- public @Nullable Serializable value() {
+ public @Nullable Object value() {
return value;
}
@@ -143,4 +176,171 @@
return Objects.hash(type, value);
}
}
+
+ /**
+ * Writes the given default value into output.
+ *
+ * @param val Default value or null.
+ * @param out Output.
+ * @throws IOException if thrown.
+ */
+ public static void writeTo(@Nullable DefaultValue val, IgniteDataOutput out) throws IOException {
+ if (val == null) {
+ out.writeByte(Type.NULL_VALUE);
+ } else {
+ out.writeByte(val.type.typeId);
+
+ if (val instanceof ConstantValue) {
+ ConstantValue constantValue = (ConstantValue) val;
+
+ writeValue(constantValue.columnType, constantValue.value, out);
+ } else if (val instanceof FunctionCall) {
+ FunctionCall functionCall = (FunctionCall) val;
+ String functionName = functionCall.functionName();
+
+ out.writeUTF(functionName);
+ } else {
+ throw new IllegalArgumentException("Unknown or unexpected type: " + val);
+ }
+ }
+ }
+
+ /** Reads default value or {@code null}. */
+ public static @Nullable DefaultValue readFrom(IgniteDataInput in) throws IOException {
+ int typeId = in.readByte();
+ if (typeId == Type.NULL_VALUE) {
+ return null;
+ } else if (typeId == Type.CONSTANT.typeId) {
+ Object val = readValue(in);
+ return new ConstantValue(val);
+ } else if (typeId == Type.FUNCTION_CALL.typeId) {
+ String functionName = in.readUTF();
+ return new FunctionCall(functionName);
+ } else {
+ throw new IllegalArgumentException("Unexpected type: " + typeId);
+ }
+ }
+
+ private static void writeValue(ColumnType columnType, @Nullable Object value, IgniteDataOutput out) throws IOException {
+ out.writeByte(columnType.id());
+ if (value == null) {
+ return;
+ }
+
+ switch (columnType) {
+ case NULL:
+ break;
+ case BOOLEAN:
+ out.writeBoolean((Boolean) value);
+ break;
+ case INT8:
+ out.writeByte((Byte) value);
+ break;
+ case INT16:
+ out.writeShort((Short) value);
+ break;
+ case INT32:
+ out.writeInt((Integer) value);
+ break;
+ case INT64:
+ out.writeLong((Long) value);
+ break;
+ case FLOAT:
+ out.writeFloat((Float) value);
+ break;
+ case DOUBLE:
+ out.writeDouble((Double) value);
+ break;
+ case DECIMAL:
+ out.writeBigDecimal((BigDecimal) value);
+ break;
+ case DATE:
+ out.writeLocalDate((LocalDate) value);
+ break;
+ case TIME:
+ out.writeLocalTime((LocalTime) value);
+ break;
+ case DATETIME:
+ out.writeLocalDateTime((LocalDateTime) value);
+ break;
+ case TIMESTAMP:
+ out.writeInstant((Instant) value);
+ break;
+ case UUID:
+ out.writeUuid((UUID) value);
+ break;
+ case BITMASK:
+ out.writeBitSet((BitSet) value);
+ break;
+ case STRING:
+ out.writeUTF((String) value);
+ break;
+ case BYTE_ARRAY:
+ byte[] bytes = (byte[]) value;
+ out.writeInt(bytes.length);
+ out.writeByteArray((byte[]) value);
+ break;
+ case PERIOD:
+ out.writePeriod((Period) value);
+ break;
+ case DURATION:
+ out.writeDuration((Duration) value);
+ break;
+ case NUMBER:
+ out.writeBigInteger((BigInteger) value);
+ break;
+ default:
+ throw new IllegalArgumentException("Unexpected column type: " + columnType);
+ }
+ }
+
+ private static @Nullable Object readValue(IgniteDataInput in) throws IOException {
+ int typeId = in.readByte();
+ ColumnType columnType = ColumnType.getById(typeId);
+ switch (columnType) {
+ case NULL:
+ return null;
+ case BOOLEAN:
+ return in.readBoolean();
+ case INT8:
+ return in.readByte();
+ case INT16:
+ return in.readShort();
+ case INT32:
+ return in.readInt();
+ case INT64:
+ return in.readLong();
+ case FLOAT:
+ return in.readFloat();
+ case DOUBLE:
+ return in.readDouble();
+ case DECIMAL:
+ return in.readBigDecimal();
+ case DATE:
+ return in.readLocalDate();
+ case TIME:
+ return in.readLocalTime();
+ case DATETIME:
+ return in.readLocalDateTime();
+ case TIMESTAMP:
+ return in.readInstant();
+ case UUID:
+ return in.readUuid();
+ case BITMASK:
+ return in.readBitSet();
+ case STRING:
+ return in.readUTF();
+ case BYTE_ARRAY:
+ int bytesLength = in.readInt();
+ return in.readByteArray(bytesLength);
+ case PERIOD:
+ return in.readPeriod();
+ case DURATION:
+ return in.readDuration();
+ case NUMBER:
+ return in.readBigInteger();
+ default:
+ throw new IllegalArgumentException("Unexpected column type: " + columnType);
+ }
+ }
}
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableColumnDescriptor.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableColumnDescriptor.java
index 7d29d16..a6ce2dc 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableColumnDescriptor.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableColumnDescriptor.java
@@ -18,12 +18,10 @@
package org.apache.ignite.internal.catalog.descriptors;
import java.io.IOException;
-import java.io.Serializable;
import java.util.Objects;
import org.apache.ignite.internal.catalog.commands.DefaultValue;
import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
import org.apache.ignite.internal.tostring.S;
-import org.apache.ignite.internal.util.ByteUtils;
import org.apache.ignite.internal.util.io.IgniteDataInput;
import org.apache.ignite.internal.util.io.IgniteDataOutput;
import org.apache.ignite.sql.ColumnType;
@@ -147,8 +145,6 @@
private static class TableColumnDescriptorSerializer implements CatalogObjectSerializer<CatalogTableColumnDescriptor> {
@Override
public CatalogTableColumnDescriptor readFrom(IgniteDataInput input) throws IOException {
- // TODO https://issues.apache.org/jira/browse/IGNITE-21435 Should not depend on Serializable.
- DefaultValue defaultValue = readSerializableObject(input);
String name = input.readUTF();
int typeId = input.readInt();
ColumnType type = ColumnType.getById(typeId);
@@ -160,41 +156,21 @@
int scale = input.readInt();
int length = input.readInt();
+ DefaultValue defaultValue = DefaultValue.readFrom(input);
+
return new CatalogTableColumnDescriptor(name, type, nullable, precision, scale, length, defaultValue);
}
@Override
public void writeTo(CatalogTableColumnDescriptor descriptor, IgniteDataOutput output) throws IOException {
- // TODO https://issues.apache.org/jira/browse/IGNITE-21435 Should not depend on Serializable.
- writeSerializableObject(descriptor.defaultValue(), output);
-
output.writeUTF(descriptor.name());
output.writeInt(descriptor.type().id());
output.writeBoolean(descriptor.nullable());
output.writeInt(descriptor.precision());
output.writeInt(descriptor.scale());
output.writeInt(descriptor.length());
- }
- /** Reads {@link Serializable} object. */
- private static <T extends Serializable> @Nullable T readSerializableObject(IgniteDataInput input) throws IOException {
- int blockSize = input.readInt();
-
- return blockSize == -1 ? null : ByteUtils.fromBytes(input.readByteArray(blockSize));
- }
-
- /** Writes {@link Serializable} object. */
- private static void writeSerializableObject(@Nullable Serializable object, IgniteDataOutput output) throws IOException {
- if (object == null) {
- output.writeInt(-1);
-
- return;
- }
-
- byte[] bytes = ByteUtils.toBytes(object);
-
- output.writeInt(bytes.length);
- output.writeByteArray(bytes);
+ DefaultValue.writeTo(descriptor.defaultValue(), output);
}
}
}
diff --git a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogEntrySerializationTest.java b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogEntrySerializationTest.java
index 52c77d1..ac0e23b 100644
--- a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogEntrySerializationTest.java
+++ b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogEntrySerializationTest.java
@@ -21,13 +21,27 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
-import java.io.Serializable;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.BitSet;
import java.util.List;
+import java.util.Random;
import java.util.Set;
+import java.util.UUID;
+import java.util.stream.Stream;
import org.apache.ignite.internal.catalog.Catalog;
import org.apache.ignite.internal.catalog.commands.DefaultValue;
+import org.apache.ignite.internal.catalog.commands.DefaultValue.ConstantValue;
+import org.apache.ignite.internal.catalog.commands.DefaultValue.FunctionCall;
import org.apache.ignite.internal.catalog.descriptors.CatalogColumnCollation;
import org.apache.ignite.internal.catalog.descriptors.CatalogHashIndexDescriptor;
import org.apache.ignite.internal.catalog.descriptors.CatalogIndexColumnDescriptor;
@@ -45,20 +59,36 @@
import org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntryType;
import org.apache.ignite.internal.catalog.storage.serialization.UpdateLogMarshallerImpl;
import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
-import org.apache.ignite.internal.tostring.S;
+import org.apache.ignite.internal.type.NativeType;
+import org.apache.ignite.internal.type.NativeTypes;
+import org.apache.ignite.internal.util.io.IgniteUnsafeDataInput;
+import org.apache.ignite.internal.util.io.IgniteUnsafeDataOutput;
import org.apache.ignite.sql.ColumnType;
import org.assertj.core.api.BDDAssertions;
import org.jetbrains.annotations.Nullable;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.EnumSource;
import org.junit.jupiter.params.provider.EnumSource.Mode;
+import org.junit.jupiter.params.provider.MethodSource;
/**
* Tests to verify catalog storage entries serialization.
*/
public class CatalogEntrySerializationTest extends BaseIgniteAbstractTest {
+ private static final long SEED = System.nanoTime();
+
+ private static final Random RND = new Random(SEED);
+
private final UpdateLogMarshallerImpl marshaller = new UpdateLogMarshallerImpl();
+ @BeforeEach
+ public void setup() {
+ log.info("Seed: {}", SEED);
+ }
+
@ParameterizedTest
@EnumSource(value = MarshallableEntryType.class, names = "VERSIONED_UPDATE", mode = Mode.EXCLUDE)
void test(MarshallableEntryType type) {
@@ -144,6 +174,85 @@
}
}
+ @ParameterizedTest(name = "{0}")
+ @MethodSource("values")
+ public void testConstantDefaultAllTypes(ColumnType columnType, Object value) throws IOException {
+ ConstantValue val = (ConstantValue) DefaultValue.constant(value);
+
+ log.info("{}: {}", columnType, value);
+
+ try (IgniteUnsafeDataOutput os = new IgniteUnsafeDataOutput(128)) {
+ DefaultValue.writeTo(val, os);
+
+ try (IgniteUnsafeDataInput in = new IgniteUnsafeDataInput(os.internalArray())) {
+ DefaultValue actual = DefaultValue.readFrom(in);
+ assertEquals(val, actual);
+ }
+ }
+ }
+
+ private static Stream<Arguments> values() {
+ List<Object> list = new ArrayList<>();
+
+ list.add(null);
+ list.add(RND.nextBoolean());
+
+ list.add((byte) RND.nextInt());
+ list.add((short) RND.nextInt());
+ list.add(RND.nextInt());
+ list.add(RND.nextLong());
+ list.add((float) RND.nextDouble());
+ list.add(RND.nextDouble());
+
+ list.add(BigDecimal.valueOf(RND.nextLong()));
+ list.add(BigDecimal.valueOf(RND.nextLong(), RND.nextInt(100)));
+
+ list.add(BigInteger.valueOf(RND.nextLong()));
+
+ list.add(LocalTime.of(RND.nextInt(24), RND.nextInt(60), RND.nextInt(60), RND.nextInt(100_000)));
+ list.add(LocalDate.of(RND.nextInt(4000) - 1000, RND.nextInt(12) + 1, RND.nextInt(27) + 1));
+ list.add(LocalDateTime.of(
+ LocalDate.of(RND.nextInt(4000) - 1000, RND.nextInt(12) + 1, RND.nextInt(27) + 1),
+ LocalTime.of(RND.nextInt(24), RND.nextInt(60), RND.nextInt(60), RND.nextInt(100_000))
+ ));
+
+ byte[] bytes = new byte[RND.nextInt(1000)];
+ RND.nextBytes(bytes);
+ list.add(Base64.getEncoder().encodeToString(bytes));
+
+ list.add(UUID.randomUUID());
+
+ // TODO Include ignored values to test after https://issues.apache.org/jira/browse/IGNITE-15200
+ // list.add(Duration.of(11, ChronoUnit.HOURS));
+ // list.add(Period.of(5, 4, 3));
+
+ BitSet bitSet = new BitSet();
+ for (int i = 0; i < RND.nextInt(100); i++) {
+ int b = RND.nextInt(1024);
+ bitSet.set(b);
+ }
+ list.add(bitSet);
+
+ return list.stream().map(val -> {
+ NativeType nativeType = NativeTypes.fromObject(val);
+ return Arguments.of(nativeType == null ? ColumnType.NULL : nativeType.spec().asColumnType(), val);
+ });
+ }
+
+ @Test
+ public void testFunctionCallDefault() throws IOException {
+ FunctionCall val = (FunctionCall) DefaultValue.functionCall("func");
+
+ try (IgniteUnsafeDataOutput os = new IgniteUnsafeDataOutput(128)) {
+ DefaultValue.writeTo(val, os);
+
+ try (IgniteUnsafeDataInput in = new IgniteUnsafeDataInput(os.internalArray())) {
+ DefaultValue actual = DefaultValue.readFrom(in);
+ assertEquals(val, actual);
+ }
+ }
+ }
+
private void checkAlterZoneEntry() {
CatalogStorageProfilesDescriptor profiles =
new CatalogStorageProfilesDescriptor(List.of(new CatalogStorageProfileDescriptor("default")));
@@ -168,7 +277,7 @@
private void checkAlterColumnEntry() {
CatalogTableColumnDescriptor desc1 = newCatalogTableColumnDescriptor("c0", null);
CatalogTableColumnDescriptor desc2 =
- newCatalogTableColumnDescriptor("c1", DefaultValue.constant(new CustomDefaultValue(Integer.MAX_VALUE)));
+ newCatalogTableColumnDescriptor("c1", DefaultValue.constant(UUID.randomUUID()));
CatalogTableColumnDescriptor desc3 =
newCatalogTableColumnDescriptor("c2", DefaultValue.functionCall("function"));
CatalogTableColumnDescriptor desc4 = newCatalogTableColumnDescriptor("c3", DefaultValue.constant(null));
@@ -373,19 +482,4 @@
"default"
);
}
-
- private static class CustomDefaultValue implements Serializable {
- private static final long serialVersionUID = 0L;
-
- private final int field;
-
- CustomDefaultValue(int field) {
- this.field = field;
- }
-
- @Override
- public String toString() {
- return S.toString(this);
- }
- }
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/io/IgniteDataInput.java b/modules/core/src/main/java/org/apache/ignite/internal/util/io/IgniteDataInput.java
index f7f2d61..0722241 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/io/IgniteDataInput.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/io/IgniteDataInput.java
@@ -20,6 +20,16 @@
import java.io.DataInput;
import java.io.IOException;
import java.io.InputStream;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.Period;
+import java.util.BitSet;
+import java.util.UUID;
/**
* Extended data input.
@@ -150,6 +160,86 @@
char[] readCharArray(int length) throws IOException;
/**
+ * Reads big integer.
+ *
+ * @return Big integer.
+ * @throws IOException In case of error.
+ */
+ BigInteger readBigInteger() throws IOException;
+
+ /**
+ * Reads big decimal.
+ *
+ * @return Big decimal.
+ * @throws IOException In case of error.
+ */
+ BigDecimal readBigDecimal() throws IOException;
+
+ /**
+ * Reads local time.
+ *
+ * @return Local time.
+ * @throws IOException In case of error.
+ */
+ LocalTime readLocalTime() throws IOException;
+
+ /**
+ * Reads local date.
+ *
+ * @return Local date.
+ * @throws IOException In case of error.
+ */
+ LocalDate readLocalDate() throws IOException;
+
+ /**
+ * Reads local date time.
+ *
+ * @return Local date time.
+ * @throws IOException In case of error.
+ */
+ LocalDateTime readLocalDateTime() throws IOException;
+
+ /**
+ * Reads instant.
+ *
+ * @return Instant.
+ * @throws IOException In case of error.
+ */
+ Instant readInstant() throws IOException;
+
+ /**
+ * Reads duration.
+ *
+ * @return Duration.
+ * @throws IOException In case of error.
+ */
+ Duration readDuration() throws IOException;
+
+ /**
+ * Reads period.
+ *
+ * @return Period.
+ * @throws IOException In case of error.
+ */
+ Period readPeriod() throws IOException;
+
+ /**
+ * Reads uuid.
+ *
+ * @return UUID.
+ * @throws IOException In case of error.
+ */
+ UUID readUuid() throws IOException;
+
+ /**
+ * Reads bit set.
+ *
+ * @return Bit set.
+ * @throws IOException In case of error.
+ */
+ BitSet readBitSet() throws IOException;
+
+ /**
* Reads the requested number of bytes from the input stream into the given
* byte array. This method blocks until {@code len} bytes of input data have
* been read, end of stream is detected, or an exception is thrown. The
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/io/IgniteDataOutput.java b/modules/core/src/main/java/org/apache/ignite/internal/util/io/IgniteDataOutput.java
index 7fd631f..c1d54f2 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/io/IgniteDataOutput.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/io/IgniteDataOutput.java
@@ -20,6 +20,16 @@
import java.io.DataOutput;
import java.io.IOException;
import java.io.OutputStream;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.Period;
+import java.util.BitSet;
+import java.util.UUID;
/**
* Extended data output.
@@ -130,6 +140,86 @@
void writeCharArray(char[] arr) throws IOException;
/**
+ * Writes big integer.
+ *
+ * @param val Big integer.
+ * @throws IOException In case of error.
+ */
+ void writeBigInteger(BigInteger val) throws IOException;
+
+ /**
+ * Writes decimal.
+ *
+ * @param val Big decimal.
+ * @throws IOException In case of error.
+ */
+ void writeBigDecimal(BigDecimal val) throws IOException;
+
+ /**
+ * Writes local time.
+ *
+ * @param val Local time.
+ * @throws IOException In case of error.
+ */
+ void writeLocalTime(LocalTime val) throws IOException;
+
+ /**
+ * Writes local date.
+ *
+ * @param date Local date.
+ * @throws IOException In case of error.
+ */
+ void writeLocalDate(LocalDate date) throws IOException;
+
+ /**
+ * Writes local date time.
+ *
+ * @param val Local date time.
+ * @throws IOException In case of error.
+ */
+ void writeLocalDateTime(LocalDateTime val) throws IOException;
+
+ /**
+ * Writes instant.
+ *
+ * @param val Instant.
+ * @throws IOException In case of error.
+ */
+ void writeInstant(Instant val) throws IOException;
+
+ /**
+ * Writes period.
+ *
+ * @param val Period.
+ * @throws IOException In case of error.
+ */
+ void writePeriod(Period val) throws IOException;
+
+ /**
+ * Writes duration.
+ *
+ * @param val Duration.
+ * @throws IOException In case of error.
+ */
+ void writeDuration(Duration val) throws IOException;
+
+ /**
+ * Writes uuid.
+ *
+ * @param val UUID.
+ * @throws IOException In case of error.
+ */
+ void writeUuid(UUID val) throws IOException;
+
+ /**
+ * Writes bit set.
+ *
+ * @param val Bit set.
+ * @throws IOException In case of error.
+ */
+ void writeBitSet(BitSet val) throws IOException;
+
+ /**
* Flushes the output. This flushes the interlying {@link OutputStream} (if exists).
*
* @throws IOException if something went wrong
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/io/IgniteUnsafeDataInput.java b/modules/core/src/main/java/org/apache/ignite/internal/util/io/IgniteUnsafeDataInput.java
index 012c9ec..d3df8ee 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/io/IgniteUnsafeDataInput.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/io/IgniteUnsafeDataInput.java
@@ -30,7 +30,18 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.UTFDataFormatException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.Period;
+import java.util.BitSet;
import java.util.Objects;
+import java.util.UUID;
import org.apache.ignite.internal.tostring.IgniteToStringBuilder;
import org.apache.ignite.internal.tostring.IgniteToStringExclude;
import org.apache.ignite.internal.util.FastTimestamps;
@@ -373,6 +384,105 @@
/** {@inheritDoc} */
@Override
+ public BigInteger readBigInteger() throws IOException {
+ int length = readInt();
+ byte[] bytes = readByteArray(length);
+ return new BigInteger(bytes);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public BigDecimal readBigDecimal() throws IOException {
+ short scale = readShort();
+ BigInteger bigInteger = readBigInteger();
+
+ return new BigDecimal(bigInteger, scale);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public LocalTime readLocalTime() throws IOException {
+ byte hour = readByte();
+ byte minute = readByte();
+ byte second = readByte();
+ int nano = readInt();
+
+ return LocalTime.of(hour, minute, second, nano);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public LocalDate readLocalDate() throws IOException {
+ int year = readInt();
+ short month = readShort();
+ short day = readShort();
+
+ return LocalDate.of(year, month, day);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public LocalDateTime readLocalDateTime() throws IOException {
+ int year = readInt();
+ short month = readShort();
+ short day = readShort();
+ byte hour = readByte();
+ byte minute = readByte();
+ byte second = readByte();
+ int nano = readInt();
+
+ return LocalDateTime.of(year, month, day, hour, minute, second, nano);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Instant readInstant() throws IOException {
+ long epochSecond = readLong();
+ int nano = readInt();
+ return Instant.ofEpochSecond(epochSecond, nano);
+ }
+
+
+ /** {@inheritDoc} */
+ @Override
+ public Duration readDuration() throws IOException {
+ long seconds = readLong();
+ int nano = readInt();
+
+ return Duration.ofSeconds(seconds, nano);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Period readPeriod() throws IOException {
+ int years = readInt();
+ int months = readInt();
+ int days = readInt();
+
+ return Period.of(years, months, days);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public UUID readUuid() throws IOException {
+ int length = readByte();
+ byte[] bytes = readByteArray(length);
+
+ return UUID.fromString(new String(bytes, StandardCharsets.UTF_8));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public BitSet readBitSet() throws IOException {
+ int length = readInt();
+ byte[] bytes = readByteArray(length);
+
+ return BitSet.valueOf(bytes);
+ }
+
+
+ /** {@inheritDoc} */
+ @Override
public long[] readLongArray(int arrSize) throws IOException {
int bytesToCp = arrSize << 3;
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/io/IgniteUnsafeDataOutput.java b/modules/core/src/main/java/org/apache/ignite/internal/util/io/IgniteUnsafeDataOutput.java
index d7f93dd..7dee8c0 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/io/IgniteUnsafeDataOutput.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/io/IgniteUnsafeDataOutput.java
@@ -28,7 +28,18 @@
import java.io.IOException;
import java.io.OutputStream;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.Period;
import java.util.Arrays;
+import java.util.BitSet;
+import java.util.UUID;
import org.apache.ignite.internal.tostring.IgniteToStringBuilder;
import org.apache.ignite.internal.util.FastTimestamps;
import org.apache.ignite.internal.util.GridUnsafe;
@@ -324,6 +335,116 @@
/** {@inheritDoc} */
@Override
+ public void writeBigInteger(BigInteger val) throws IOException {
+ byte[] bytes = val.toByteArray();
+ writeInt(bytes.length);
+ writeByteArray(bytes);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeBigDecimal(BigDecimal val) throws IOException {
+ short scale = (short) val.scale();
+ byte[] bytes = val.unscaledValue().toByteArray();
+
+ writeShort(scale);
+ writeInt(bytes.length);
+ writeByteArray(bytes);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeLocalTime(LocalTime val) throws IOException {
+ byte hour = (byte) val.getHour();
+ byte minute = (byte) val.getMinute();
+ byte second = (byte) val.getSecond();
+ int nano = val.getNano();
+
+ writeByte(hour);
+ writeByte(minute);
+ writeByte(second);
+ writeInt(nano);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeLocalDate(LocalDate date) throws IOException {
+ int year = date.getYear();
+ short month = (short) date.getMonth().getValue();
+ short day = (short) date.getDayOfMonth();
+
+ writeInt(year);
+ writeShort(month);
+ writeShort(day);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeLocalDateTime(LocalDateTime val) throws IOException {
+ int year = val.getYear();
+ short month = (short) val.getMonth().getValue();
+ short day = (short) val.getDayOfMonth();
+
+ byte hour = (byte) val.getHour();
+ byte minute = (byte) val.getMinute();
+ byte second = (byte) val.getSecond();
+ int nano = val.getNano();
+
+ writeInt(year);
+ writeShort(month);
+ writeShort(day);
+
+ writeByte(hour);
+ writeByte(minute);
+ writeByte(second);
+ writeInt(nano);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeInstant(Instant val) throws IOException {
+ long epochSecond = val.getEpochSecond();
+ int nano = val.getNano();
+
+ writeLong(epochSecond);
+ writeInt(nano);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writePeriod(Period val) throws IOException {
+ writeInt(val.getYears());
+ writeInt(val.getMonths());
+ writeInt(val.getDays());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeDuration(Duration val) throws IOException {
+ writeLong(val.getSeconds());
+ writeInt(val.getNano());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeUuid(UUID val) throws IOException {
+ byte[] bytes = val.toString().getBytes(StandardCharsets.US_ASCII);
+
+ writeByte(bytes.length);
+ writeByteArray(bytes);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeBitSet(BitSet val) throws IOException {
+ byte[] bytes = val.toByteArray();
+
+ writeInt(bytes.length);
+ writeByteArray(bytes);
+ }
+
+ /** {@inheritDoc} */
+ @Override
public void writeLongArray(long[] arr) throws IOException {
checkArrayAllocationOverflow(8, arr.length, "long");
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/util/io/IgniteUnsafeDataInputOutputByteOrderTest.java b/modules/core/src/test/java/org/apache/ignite/internal/util/io/IgniteUnsafeDataInputOutputByteOrderTest.java
index fabbb56..34b1c80 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/util/io/IgniteUnsafeDataInputOutputByteOrderTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/util/io/IgniteUnsafeDataInputOutputByteOrderTest.java
@@ -27,7 +27,19 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.Period;
+import java.util.BitSet;
import java.util.Random;
+import java.util.UUID;
+import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -35,15 +47,22 @@
/**
* Ignite unsafe data input/output byte order sanity tests.
*/
-class IgniteUnsafeDataInputOutputByteOrderTest {
+class IgniteUnsafeDataInputOutputByteOrderTest extends BaseIgniteAbstractTest {
/** Array length. */
private static final int ARR_LEN = 16;
/** Length bytes. */
private static final int LEN_BYTES = 0;
+ private static final long SEED = System.nanoTime();
+
/** Rnd. */
- private static final Random RND = new Random();
+ private static final Random RND = new Random(SEED);
+
+ @BeforeEach
+ public void setup() {
+ log.info("Seed: {}", SEED);
+ }
/** Out. */
private IgniteUnsafeDataOutput out;
@@ -237,4 +256,101 @@
assertArrayEquals(arr, in.readDoubleArray(ARR_LEN), 0);
}
+
+ @Test
+ public void testLocalTime() throws IOException {
+ LocalTime val = LocalTime.of(RND.nextInt(24), RND.nextInt(60), RND.nextInt(60), RND.nextInt(10000));
+
+ out.writeLocalTime(val);
+
+ assertEquals(val, in.readLocalTime());
+ }
+
+ @Test
+ public void testLocalDate() throws IOException {
+ LocalDate val = LocalDate.of(RND.nextInt(4000) - 1000, RND.nextInt(12) + 1, 1 + RND.nextInt(27));
+
+ out.writeLocalDate(val);
+
+ assertEquals(val, in.readLocalDate());
+ }
+
+ @Test
+ public void testLocalDateTime() throws IOException {
+ LocalTime time = LocalTime.of(RND.nextInt(24), RND.nextInt(60), RND.nextInt(60), RND.nextInt(10000));
+ LocalDate date = LocalDate.of(RND.nextInt(4000) - 1000, RND.nextInt(12) + 1, 1 + RND.nextInt(27));
+ LocalDateTime val = LocalDateTime.of(date, time);
+
+ out.writeLocalDateTime(val);
+
+ assertEquals(val, in.readLocalDateTime());
+ }
+
+ @Test
+ public void testInstant() throws IOException {
+ Instant val = Instant.ofEpochMilli(RND.nextLong());
+
+ out.writeInstant(val);
+
+ assertEquals(val, in.readInstant());
+ }
+
+ @Test
+ public void testBigInteger() throws IOException {
+ BigInteger val = BigInteger.valueOf(RND.nextLong());
+
+ out.writeBigInteger(val);
+
+ assertEquals(val, in.readBigInteger());
+ }
+
+ @Test
+ public void testBigDecimal() throws IOException {
+ BigDecimal val = new BigDecimal(BigInteger.valueOf(RND.nextLong()), RND.nextInt(120));
+
+ out.writeBigDecimal(val);
+
+ assertEquals(val, in.readBigDecimal());
+ }
+
+ @Test
+ public void testPeriod() throws IOException {
+ Period val = Period.of(RND.nextInt(10_000), RND.nextInt(10_000), RND.nextInt(10_000));
+
+ out.writePeriod(val);
+
+ assertEquals(val, in.readPeriod());
+ }
+
+ @Test
+ public void testDuration() throws IOException {
+ Duration val = Duration.ofSeconds(RND.nextInt(100_000), RND.nextInt(100_000));
+
+ out.writeDuration(val);
+
+ assertEquals(val, in.readDuration());
+ }
+
+ @Test
+ public void testUuid() throws IOException {
+ UUID val = UUID.randomUUID();
+
+ out.writeUuid(val);
+
+ assertEquals(val, in.readUuid());
+ }
+
+ @Test
+ public void testBitSet() throws IOException {
+ BitSet val = new BitSet();
+
+ for (int i = 0; i < RND.nextInt(100); i++) {
+ int b = RND.nextInt(256);
+ val.set(Math.abs(b));
+ }
+
+ out.writeBitSet(val);
+
+ assertEquals(val, in.readBitSet());
+ }
}