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());
+    }
 }