This is an automated email from the ASF dual-hosted git repository.

ppa pushed a commit to branch ignite-26491
in repository https://gitbox.apache.org/repos/asf/ignite-3.git

commit 9d4a8006500aeed8142272ff7b1d0cd5456aa518
Author: Pavel Pereslegin <[email protected]>
AuthorDate: Thu Jan 8 13:00:58 2026 +0300

    IGNITE-26491 Implicit conversion (wip)
---
 .../java/org/apache/ignite/table/TupleImpl.java    | 133 +++++-
 .../ignite/table/AbstractImmutableTupleTest.java   | 429 +++++++++++++++++
 .../table/MutableTupleBinaryTupleAdapter.java      | 133 +++---
 .../ignite/internal/util/TupleTypeCastUtils.java   | 510 +++++++++++++++++++++
 .../internal/table/AbstractRowTupleAdapter.java    |  86 ++--
 5 files changed, 1179 insertions(+), 112 deletions(-)

diff --git a/modules/api/src/main/java/org/apache/ignite/table/TupleImpl.java 
b/modules/api/src/main/java/org/apache/ignite/table/TupleImpl.java
index d197f69f27a..814d0d5215d 100644
--- a/modules/api/src/main/java/org/apache/ignite/table/TupleImpl.java
+++ b/modules/api/src/main/java/org/apache/ignite/table/TupleImpl.java
@@ -196,73 +196,97 @@ class TupleImpl implements Tuple, Serializable {
     /** {@inheritDoc} */
     @Override
     public byte byteValue(String columnName) {
-        return valueNotNull(columnName);
+        Number number = valueNotNull(columnName);
+
+        return castToByte(number);
     }
 
     /** {@inheritDoc} */
     @Override
     public byte byteValue(int columnIndex) {
-        return valueNotNull(columnIndex);
+        Number number = valueNotNull(columnIndex);
+
+        return castToByte(number);
     }
 
     /** {@inheritDoc} */
     @Override
     public short shortValue(String columnName) {
-        return valueNotNull(columnName);
+        Number number = valueNotNull(columnName);
+
+        return castToShort(number);
     }
 
     /** {@inheritDoc} */
     @Override
     public short shortValue(int columnIndex) {
-        return valueNotNull(columnIndex);
+        Number number = valueNotNull(columnIndex);
+
+        return castToShort(number);
     }
 
     /** {@inheritDoc} */
     @Override
     public int intValue(String columnName) {
-        return valueNotNull(columnName);
+        Number number = valueNotNull(columnName);
+
+        return castToInt(number);
     }
 
     /** {@inheritDoc} */
     @Override
     public int intValue(int columnIndex) {
-        return valueNotNull(columnIndex);
+        Number number = valueNotNull(columnIndex);
+
+        return castToInt(number);
     }
 
     /** {@inheritDoc} */
     @Override
     public long longValue(String columnName) {
-        return valueNotNull(columnName);
+        Number number = valueNotNull(columnName);
+
+        return castToLong(number);
     }
 
     /** {@inheritDoc} */
     @Override
     public long longValue(int columnIndex) {
-        return valueNotNull(columnIndex);
+        Number number = valueNotNull(columnIndex);
+
+        return castToLong(number);
     }
 
     /** {@inheritDoc} */
     @Override
     public float floatValue(String columnName) {
-        return valueNotNull(columnName);
+        Number number = valueNotNull(columnName);
+
+        return castToFloat(number);
     }
 
     /** {@inheritDoc} */
     @Override
     public float floatValue(int columnIndex) {
-        return valueNotNull(columnIndex);
+        Number number = valueNotNull(columnIndex);
+
+        return castToFloat(number);
     }
 
     /** {@inheritDoc} */
     @Override
     public double doubleValue(String columnName) {
-        return valueNotNull(columnName);
+        Number number = valueNotNull(columnName);
+
+        return castToDouble(number);
     }
 
     /** {@inheritDoc} */
     @Override
     public double doubleValue(int columnIndex) {
-        return valueNotNull(columnIndex);
+        Number number = valueNotNull(columnIndex);
+
+        return castToDouble(number);
     }
 
     /** {@inheritDoc} */
@@ -459,4 +483,89 @@ class TupleImpl implements Tuple, Serializable {
 
         return value;
     }
+
+    private static byte castToByte(Number number) {
+        if (number instanceof Long
+                || number instanceof Integer
+                || number instanceof Short) {
+            long longVal = number.longValue();
+            byte byteVal = number.byteValue();
+
+            if (longVal == byteVal) {
+                return byteVal;
+            }
+
+            throw new ArithmeticException("Byte value overflow");
+        }
+
+        return (byte) number;
+    }
+
+    private static short castToShort(Number number) {
+        if (number instanceof Long
+                || number instanceof Integer
+                || number instanceof Byte) {
+            long longVal = number.longValue();
+            short shortVal = number.shortValue();
+
+            if (longVal == shortVal) {
+                return shortVal;
+            }
+
+            throw new ArithmeticException("Short value overflow");
+        }
+
+        return (short) number;
+    }
+
+    private static int castToInt(Number number) {
+        if (number instanceof Long
+                || number instanceof Short
+                || number instanceof Byte) {
+            long longVal = number.longValue();
+            int intVal = number.intValue();
+
+            if (longVal == intVal) {
+                return intVal;
+            }
+
+            throw new ArithmeticException("Int value overflow");
+        }
+
+        return (int) number;
+    }
+
+    private static long castToLong(Number number) {
+        if (number instanceof Integer
+                || number instanceof Short
+                || number instanceof Byte) {
+            return number.longValue();
+        }
+
+        return (long) number;
+    }
+
+    private static float castToFloat(Number number) {
+        if (number instanceof Double) {
+            double doubleVal = number.doubleValue();
+            float floatVal = number.floatValue();
+
+            //noinspection FloatingPointEquality
+            if (doubleVal == floatVal) {
+                return floatVal;
+            }
+
+            throw new ArithmeticException("Float value overflow");
+        }
+
+        return (float) number;
+    }
+
+    private static double castToDouble(Number number) {
+        if (number instanceof Float) {
+            return number.doubleValue();
+        }
+
+        return (double) number;
+    }
 }
diff --git 
a/modules/api/src/testFixtures/java/org/apache/ignite/table/AbstractImmutableTupleTest.java
 
b/modules/api/src/testFixtures/java/org/apache/ignite/table/AbstractImmutableTupleTest.java
index a3fb7e0e4eb..919e5c52368 100644
--- 
a/modules/api/src/testFixtures/java/org/apache/ignite/table/AbstractImmutableTupleTest.java
+++ 
b/modules/api/src/testFixtures/java/org/apache/ignite/table/AbstractImmutableTupleTest.java
@@ -18,6 +18,9 @@
 package org.apache.ignite.table;
 
 import static java.time.temporal.ChronoField.NANO_OF_SECOND;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
@@ -38,7 +41,10 @@ import java.time.Year;
 import java.time.ZoneId;
 import java.time.ZoneOffset;
 import java.time.temporal.Temporal;
+import java.util.ArrayList;
+import java.util.EnumSet;
 import java.util.List;
+import java.util.Objects;
 import java.util.Random;
 import java.util.UUID;
 import java.util.concurrent.ThreadLocalRandom;
@@ -334,6 +340,309 @@ public abstract class AbstractImmutableTupleTest {
         assertEquals(String.format(NULL_TO_PRIMITIVE_NAMED_ERROR_MESSAGE, 
"VAL"), err.getMessage());
     }
 
+    @Test
+    void castByte() {
+        Tuple tuple = createTupleOfSingleColumn(ColumnType.INT8, "INT8", 
Byte.MAX_VALUE);
+
+        assertThat(tuple.byteValue("INT8"), is(Byte.MAX_VALUE));
+        assertThat(tuple.shortValue("INT8"), is((short) Byte.MAX_VALUE));
+        assertThat(tuple.intValue("INT8"), is((int) Byte.MAX_VALUE));
+        assertThat(tuple.longValue("INT8"), is((long) Byte.MAX_VALUE));
+
+        assertThat(tuple.byteValue(0), is(Byte.MAX_VALUE));
+        assertThat(tuple.shortValue(0), is((short) Byte.MAX_VALUE));
+        assertThat(tuple.intValue(0), is((int) Byte.MAX_VALUE));
+        assertThat(tuple.longValue(0), is((long) Byte.MAX_VALUE));
+    }
+
+    @Test
+    void castShort() {
+        // The field value is within the byte range
+        {
+            String columnName = "INT16";
+            short value = Byte.MAX_VALUE;
+            Tuple tuple = createTupleOfSingleColumn(ColumnType.INT16, 
columnName, value);
+
+            assertThat(tuple.byteValue(columnName), is((byte) value));
+            assertThat(tuple.shortValue(columnName), is(value));
+            assertThat(tuple.intValue(columnName), is((int) value));
+            assertThat(tuple.longValue(columnName), is((long) value));
+
+            assertThat(tuple.byteValue(0), is((byte) value));
+            assertThat(tuple.shortValue(0), is(value));
+            assertThat(tuple.intValue(0), is((int) value));
+            assertThat(tuple.longValue(0), is((long) value));
+        }
+
+        // The field value is out of the byte range.
+        {
+            String columnName = "INT16";
+            short value = Byte.MAX_VALUE + 1;
+            Tuple tuple = createTupleOfSingleColumn(ColumnType.INT16, 
columnName, value);
+
+            tuple.set(columnName, value);
+
+            ArithmeticException ex0 = assertThrows(ArithmeticException.class, 
() -> tuple.byteValue(columnName));
+            assertThat(ex0.getMessage(), equalTo("Byte value overflow"));
+
+            assertThat(tuple.shortValue(columnName), is(value));
+            assertThat(tuple.intValue(columnName), is((int) value));
+            assertThat(tuple.longValue(columnName), is((long) value));
+
+            ArithmeticException ex1 = assertThrows(ArithmeticException.class, 
() -> tuple.byteValue(0));
+            assertThat(ex1.getMessage(), equalTo("Byte value overflow"));
+
+            assertThat(tuple.shortValue(0), is(value));
+            assertThat(tuple.intValue(0), is((int) value));
+            assertThat(tuple.longValue(0), is((long) value));
+        }
+    }
+
+    @Test
+    void castInt() {
+        {
+            int value = Byte.MAX_VALUE;
+            String columnName = "VALUE";
+            Tuple tuple = createTupleOfSingleColumn(ColumnType.INT32, 
columnName, value);
+
+            assertThat(tuple.byteValue(columnName), is((byte) value));
+            assertThat(tuple.shortValue(columnName), is((short) value));
+            assertThat(tuple.intValue(columnName), is(value));
+            assertThat(tuple.longValue(columnName), is((long) value));
+
+            assertThat(tuple.byteValue(0), is((byte) value));
+            assertThat(tuple.shortValue(0), is((short) value));
+            assertThat(tuple.intValue(0), is(value));
+            assertThat(tuple.longValue(0), is((long) value));
+        }
+
+        {
+            int value = Byte.MAX_VALUE + 1;
+            String columnName = "VALUE";
+            Tuple tuple = createTupleOfSingleColumn(ColumnType.INT32, 
columnName, value);
+
+            ArithmeticException ex0 = assertThrows(ArithmeticException.class, 
() -> tuple.byteValue(columnName));
+            assertThat(ex0.getMessage(), equalTo("Byte value overflow"));
+
+            assertThat(tuple.shortValue(columnName), is((short) value));
+            assertThat(tuple.intValue(columnName), is(value));
+            assertThat(tuple.longValue(columnName), is((long) value));
+
+            ArithmeticException ex1 = assertThrows(ArithmeticException.class, 
() -> tuple.byteValue(0));
+            assertThat(ex1.getMessage(), equalTo("Byte value overflow"));
+
+            assertThat(tuple.shortValue(0), is((short) value));
+            assertThat(tuple.intValue(0), is(value));
+            assertThat(tuple.longValue(0), is((long) value));
+        }
+
+        {
+            int value = Short.MAX_VALUE + 1;
+            String columnName = "VALUE";
+            Tuple tuple = createTupleOfSingleColumn(ColumnType.INT32, 
columnName, value);
+
+            {
+                ArithmeticException ex = 
assertThrows(ArithmeticException.class, () -> tuple.byteValue(columnName));
+                assertThat(ex.getMessage(), equalTo("Byte value overflow"));
+            }
+
+            {
+                ArithmeticException ex = 
assertThrows(ArithmeticException.class, () -> tuple.shortValue(columnName));
+                assertThat(ex.getMessage(), equalTo("Short value overflow"));
+            }
+
+            assertThat(tuple.intValue(columnName), is(value));
+            assertThat(tuple.longValue(columnName), is((long) value));
+
+            {
+                ArithmeticException ex = 
assertThrows(ArithmeticException.class, () -> tuple.byteValue(0));
+                assertThat(ex.getMessage(), equalTo("Byte value overflow"));
+            }
+
+            {
+                ArithmeticException ex = 
assertThrows(ArithmeticException.class, () -> tuple.shortValue(0));
+                assertThat(ex.getMessage(), equalTo("Short value overflow"));
+            }
+
+            assertThat(tuple.intValue(0), is(value));
+            assertThat(tuple.longValue(0), is((long) value));
+        }
+    }
+
+    @Test
+    void castLong() {
+        {
+            long value = Byte.MAX_VALUE;
+            String columnName = "VALUE";
+            Tuple tuple = createTupleOfSingleColumn(ColumnType.INT64, 
columnName, value);
+
+            assertThat(tuple.byteValue(columnName), is((byte) value));
+            assertThat(tuple.shortValue(columnName), is((short) value));
+            assertThat(tuple.intValue(columnName), is((int) value));
+            assertThat(tuple.longValue(columnName), is(value));
+
+            assertThat(tuple.byteValue(0), is((byte) value));
+            assertThat(tuple.shortValue(0), is((short) value));
+            assertThat(tuple.intValue(0), is((int) value));
+            assertThat(tuple.longValue(0), is(value));
+        }
+
+        {
+            long value = Byte.MAX_VALUE + 1;
+            String columnName = "VALUE";
+            Tuple tuple = createTupleOfSingleColumn(ColumnType.INT64, 
columnName, value);
+
+            ArithmeticException ex0 = assertThrows(ArithmeticException.class, 
() -> tuple.byteValue(columnName));
+            assertThat(ex0.getMessage(), equalTo("Byte value overflow"));
+
+            assertThat(tuple.shortValue(columnName), is((short) value));
+            assertThat(tuple.intValue(columnName), is((int) value));
+            assertThat(tuple.longValue(columnName), is(value));
+
+            ArithmeticException ex1 = assertThrows(ArithmeticException.class, 
() -> tuple.byteValue(0));
+            assertThat(ex1.getMessage(), equalTo("Byte value overflow"));
+
+            assertThat(tuple.shortValue(0), is((short) value));
+            assertThat(tuple.intValue(0), is((int) value));
+            assertThat(tuple.longValue(0), is(value));
+        }
+
+        {
+            long value = Short.MAX_VALUE + 1;
+            String columnName = "VALUE";
+            Tuple tuple = createTupleOfSingleColumn(ColumnType.INT64, 
columnName, value);
+
+            {
+                ArithmeticException ex = 
assertThrows(ArithmeticException.class, () -> tuple.byteValue(columnName));
+                assertThat(ex.getMessage(), equalTo("Byte value overflow"));
+            }
+
+            {
+                ArithmeticException ex = 
assertThrows(ArithmeticException.class, () -> tuple.shortValue(columnName));
+                assertThat(ex.getMessage(), equalTo("Short value overflow"));
+            }
+
+            assertThat(tuple.intValue(columnName), is((int) value));
+            assertThat(tuple.longValue(columnName), is(value));
+
+            {
+                ArithmeticException ex = 
assertThrows(ArithmeticException.class, () -> tuple.byteValue(0));
+                assertThat(ex.getMessage(), equalTo("Byte value overflow"));
+            }
+
+            {
+                ArithmeticException ex = 
assertThrows(ArithmeticException.class, () -> tuple.shortValue(0));
+                assertThat(ex.getMessage(), equalTo("Short value overflow"));
+            }
+
+            assertThat(tuple.intValue(0), is((int) value));
+            assertThat(tuple.longValue(0), is(value));
+        }
+
+        {
+            long value = Integer.MAX_VALUE + 1L;
+            String columnName = "VALUE";
+            Tuple tuple = createTupleOfSingleColumn(ColumnType.INT64, 
columnName, value);
+
+            {
+                ArithmeticException ex = 
assertThrows(ArithmeticException.class, () -> tuple.byteValue(columnName));
+                assertThat(ex.getMessage(), equalTo("Byte value overflow"));
+            }
+
+            {
+                ArithmeticException ex = 
assertThrows(ArithmeticException.class, () -> tuple.shortValue(columnName));
+                assertThat(ex.getMessage(), equalTo("Short value overflow"));
+            }
+
+            {
+                ArithmeticException ex = 
assertThrows(ArithmeticException.class, () -> tuple.intValue(columnName));
+                assertThat(ex.getMessage(), equalTo("Int value overflow"));
+            }
+
+            assertThat(tuple.longValue(columnName), is(value));
+
+            {
+                ArithmeticException ex = 
assertThrows(ArithmeticException.class, () -> tuple.byteValue(0));
+                assertThat(ex.getMessage(), equalTo("Byte value overflow"));
+            }
+
+            {
+                ArithmeticException ex = 
assertThrows(ArithmeticException.class, () -> tuple.shortValue(0));
+                assertThat(ex.getMessage(), equalTo("Short value overflow"));
+            }
+
+            {
+                ArithmeticException ex = 
assertThrows(ArithmeticException.class, () -> tuple.intValue(0));
+                assertThat(ex.getMessage(), equalTo("Int value overflow"));
+            }
+
+            assertThat(tuple.longValue(0), is(value));
+        }
+    }
+
+    @Test
+    void castFloat() {
+        Tuple tuple = createTupleOfSingleColumn(ColumnType.FLOAT, "FLOAT", 
Float.MAX_VALUE);
+
+        assertThat(tuple.floatValue("FLOAT"), is(Float.MAX_VALUE));
+        assertThat(tuple.doubleValue("FLOAT"), is((double) Float.MAX_VALUE));
+
+        assertThat(tuple.floatValue(0), is(Float.MAX_VALUE));
+        assertThat(tuple.doubleValue(0), is((double) Float.MAX_VALUE));
+    }
+
+    @Test
+    void castDouble() {
+        // The field value can be represented as float.
+        {
+            double value = Float.MAX_VALUE;
+            String columnName = "DOUBLE";
+            Tuple tuple = createTupleOfSingleColumn(ColumnType.DOUBLE, 
columnName, value);
+
+            assertThat(tuple.floatValue(columnName), is((float) value));
+            assertThat(tuple.floatValue(0), is((float) value));
+
+            assertThat(tuple.doubleValue(columnName), is(value));
+            assertThat(tuple.doubleValue(0), is(value));
+        }
+
+        // The field value cannot be represented as float.
+        {
+            double value = Double.MAX_VALUE;
+            String columnName = "DOUBLE";
+            Tuple tuple = createTupleOfSingleColumn(ColumnType.DOUBLE, 
columnName, value);
+
+            ArithmeticException ex0 = assertThrows(ArithmeticException.class, 
() -> tuple.floatValue(columnName));
+            assertThat(ex0.getMessage(), equalTo("Float value overflow"));
+
+            ArithmeticException ex1 = assertThrows(ArithmeticException.class, 
() -> tuple.floatValue(0));
+            assertThat(ex1.getMessage(), equalTo("Float value overflow"));
+
+            assertThat(tuple.doubleValue(columnName), is(value));
+            assertThat(tuple.doubleValue(0), is(value));
+        }
+    }
+
+    @ParameterizedTest(name = "{0} -> {1}")
+    @MethodSource("allTypesUnsupportedConversionArgs")
+    void allTypesUnsupportedConversion(ColumnType from, ColumnType to) {
+        Object value = generateMaxValue(from);
+        String columnName = "VALUE";
+        Tuple tuple = createTupleOfSingleColumn(from, columnName, value);
+
+        assertThrows(ClassCastException.class, () -> readValue(tuple, to, 
columnName));
+    }
+
+    @ParameterizedTest(name = "{0} -> {1}")
+    @MethodSource("allTypesDowncastOverflowArgs")
+    void allTypesDowncastOverflow(ColumnType from, ColumnType to) {
+        Object value = generateMaxValue(from);
+        String columnName = "VALUE";
+        Tuple tuple = createTupleOfSingleColumn(from, columnName, value);
+
+        assertThrows(ArithmeticException.class, () -> readValue(tuple, to, 
columnName));
+    }
+
     /**
      * Adds sample values for columns of default schema: id (long), simpleName 
(string), "QuotedName" (string), noValue (null).
      *
@@ -475,6 +784,25 @@ public abstract class AbstractImmutableTupleTest {
         }
     }
 
+    private static Object generateMaxValue(ColumnType type) {
+        switch (type) {
+            case INT8:
+                return Byte.MAX_VALUE;
+            case INT16:
+                return Short.MAX_VALUE;
+            case INT32:
+                return Integer.MAX_VALUE;
+            case INT64:
+                return Long.MAX_VALUE;
+            case FLOAT:
+                return Float.MAX_VALUE;
+            case DOUBLE:
+                return Double.MAX_VALUE;
+            default:
+                return 
Objects.requireNonNull(generateValue(ThreadLocalRandom.current(), type));
+        }
+    }
+
     private static Instant generateInstant(Random rnd) {
         long minTs = LocalDateTime.of(LocalDate.of(1, 1, 1), LocalTime.MIN)
                 
.minusSeconds(ZoneOffset.MIN.getTotalSeconds()).toInstant(ZoneOffset.UTC).toEpochMilli();
@@ -507,4 +835,105 @@ public abstract class AbstractImmutableTupleTest {
                 Arguments.of(ColumnType.DOUBLE, (BiConsumer<Tuple, String>) 
Tuple::doubleValue)
         );
     }
+
+    private static Object readValue(Tuple tuple, ColumnType type, String 
column) {
+        switch (type) {
+            case TIME:
+                return tuple.timeValue(column);
+            case TIMESTAMP:
+                return tuple.timestampValue(column);
+            case DATE:
+                return tuple.dateValue(column);
+            case DATETIME:
+                return tuple.datetimeValue(column);
+            case INT8:
+                return tuple.byteValue(column);
+            case INT16:
+                return tuple.shortValue(column);
+            case INT32:
+                return tuple.intValue(column);
+            case INT64:
+                return tuple.longValue(column);
+            case FLOAT:
+                return tuple.floatValue(column);
+            case DOUBLE:
+                return tuple.doubleValue(column);
+            case UUID:
+                return tuple.uuidValue(column);
+            case STRING:
+                return tuple.stringValue(column);
+            case BOOLEAN:
+                return tuple.booleanValue(column);
+            case DECIMAL:
+                return tuple.decimalValue(column);
+            case BYTE_ARRAY:
+                return tuple.bytesValue(column);
+            default:
+                throw new UnsupportedOperationException("Unexpected type: " + 
type);
+        }
+    }
+
+    private static List<Arguments> allTypesUnsupportedConversionArgs() {
+        EnumSet<ColumnType> allTypes = EnumSet.complementOf(
+                EnumSet.of(ColumnType.NULL, ColumnType.STRUCT, 
ColumnType.PERIOD, ColumnType.DURATION));
+        List<Arguments> arguments = new ArrayList<>();
+
+        for (ColumnType from : allTypes) {
+            for (ColumnType to : allTypes) {
+                if (from == to || isSupportedUpcast(from, to) || 
isSupportedDowncast(from, to)) {
+                    continue;
+                }
+
+                arguments.add(Arguments.of(from, to));
+            }
+        }
+
+        return arguments;
+    }
+
+    private static List<Arguments> allTypesDowncastOverflowArgs() {
+        EnumSet<ColumnType> allTypes = EnumSet.complementOf(
+                EnumSet.of(ColumnType.NULL, ColumnType.STRUCT, 
ColumnType.PERIOD, ColumnType.DURATION));
+        List<Arguments> arguments = new ArrayList<>();
+
+        for (ColumnType from : allTypes) {
+            for (ColumnType to : allTypes) {
+                if (isSupportedDowncast(from, to)) {
+                    arguments.add(Arguments.of(from, to));
+                }
+            }
+        }
+
+        return arguments;
+    }
+
+    private static boolean isSupportedUpcast(ColumnType source, ColumnType 
target) {
+        switch (source) {
+            case INT8:
+                return target == ColumnType.INT16 || target == 
ColumnType.INT32 || target == ColumnType.INT64;
+            case INT16:
+                return target == ColumnType.INT32 || target == 
ColumnType.INT64;
+            case INT32:
+                return target == ColumnType.INT64;
+            case FLOAT:
+                return target == ColumnType.DOUBLE;
+            default:
+                return false;
+        }
+    }
+
+    private static boolean isSupportedDowncast(ColumnType source, ColumnType 
target) {
+        switch (source) {
+            case INT64:
+                return target == ColumnType.INT8 || target == ColumnType.INT16 
|| target == ColumnType.INT32;
+            case INT32:
+                return target == ColumnType.INT8 || target == ColumnType.INT16;
+            case INT16:
+                return target == ColumnType.INT8;
+            case DOUBLE:
+                return target == ColumnType.FLOAT;
+            default:
+                return false;
+        }
+    }
 }
diff --git 
a/modules/client-common/src/main/java/org/apache/ignite/internal/client/table/MutableTupleBinaryTupleAdapter.java
 
b/modules/client-common/src/main/java/org/apache/ignite/internal/client/table/MutableTupleBinaryTupleAdapter.java
index a9804e793a3..790c973926e 100644
--- 
a/modules/client-common/src/main/java/org/apache/ignite/internal/client/table/MutableTupleBinaryTupleAdapter.java
+++ 
b/modules/client-common/src/main/java/org/apache/ignite/internal/client/table/MutableTupleBinaryTupleAdapter.java
@@ -17,6 +17,13 @@
 
 package org.apache.ignite.internal.client.table;
 
+import static org.apache.ignite.internal.util.TupleTypeCastUtils.readByteValue;
+import static 
org.apache.ignite.internal.util.TupleTypeCastUtils.readDoubleValue;
+import static 
org.apache.ignite.internal.util.TupleTypeCastUtils.readFloatValue;
+import static org.apache.ignite.internal.util.TupleTypeCastUtils.readIntValue;
+import static org.apache.ignite.internal.util.TupleTypeCastUtils.readLongValue;
+import static 
org.apache.ignite.internal.util.TupleTypeCastUtils.readShortValue;
+
 import java.math.BigDecimal;
 import java.time.Instant;
 import java.time.LocalDate;
@@ -97,7 +104,7 @@ public abstract class MutableTupleBinaryTupleAdapter 
implements Tuple, BinaryTup
             return tuple.columnIndex(columnName);
         }
 
-        int binaryTupleIndex = binaryTupleIndex(columnName, null);
+        int binaryTupleIndex = binaryTupleIndex(columnName);
 
         return binaryTupleIndex < 0 ? -1 : publicIndex(binaryTupleIndex);
     }
@@ -109,7 +116,7 @@ public abstract class MutableTupleBinaryTupleAdapter 
implements Tuple, BinaryTup
             return tuple.valueOrDefault(columnName, defaultValue);
         }
 
-        int binaryTupleIndex = binaryTupleIndex(columnName, null);
+        int binaryTupleIndex = binaryTupleIndex(columnName);
 
         return binaryTupleIndex < 0
                 || publicIndex(binaryTupleIndex) < 0
@@ -125,7 +132,7 @@ public abstract class MutableTupleBinaryTupleAdapter 
implements Tuple, BinaryTup
             return tuple.value(columnName);
         }
 
-        int binaryTupleIndex = binaryTupleIndex(columnName, null);
+        int binaryTupleIndex = binaryTupleIndex(columnName);
 
         if (binaryTupleIndex < 0 || publicIndex(binaryTupleIndex) < 0) {
             throw new IllegalArgumentException("Column doesn't exist [name=" + 
columnName + ']');
@@ -182,11 +189,10 @@ public abstract class MutableTupleBinaryTupleAdapter 
implements Tuple, BinaryTup
             return tuple.byteValue(columnName);
         }
 
-        int binaryTupleIndex = validateSchemaColumnType(columnName, 
ColumnType.INT8);
-
-        IgniteUtils.ensureNotNull(binaryTuple, binaryTupleIndex, columnName);
+        int binaryTupleIndex = resolveBinaryTupleIndexByColumnName(columnName);
+        ColumnType actualType = schemaColumnType(binaryTupleIndex);
 
-        return binaryTuple.byteValue(binaryTupleIndex);
+        return readByteValue(binaryTuple, binaryTupleIndex, actualType, 
columnName);
     }
 
     /** {@inheritDoc} */
@@ -196,11 +202,11 @@ public abstract class MutableTupleBinaryTupleAdapter 
implements Tuple, BinaryTup
             return tuple.byteValue(columnIndex);
         }
 
-        int binaryTupleIndex = validateSchemaColumnType(columnIndex, 
ColumnType.INT8);
-
-        IgniteUtils.ensureNotNull(binaryTuple, binaryTupleIndex, columnIndex);
+        Objects.checkIndex(columnIndex, columnCount);
+        int binaryTupleIndex = binaryTupleIndex(columnIndex);
+        ColumnType actualType = schemaColumnType(binaryTupleIndex);
 
-        return binaryTuple.byteValue(binaryTupleIndex);
+        return readByteValue(binaryTuple, binaryTupleIndex, actualType, 
columnIndex);
     }
 
     /** {@inheritDoc} */
@@ -210,11 +216,10 @@ public abstract class MutableTupleBinaryTupleAdapter 
implements Tuple, BinaryTup
             return tuple.shortValue(columnName);
         }
 
-        int binaryTupleIndex = validateSchemaColumnType(columnName, 
ColumnType.INT16);
+        var binaryTupleIndex = resolveBinaryTupleIndexByColumnName(columnName);
+        ColumnType actualType = schemaColumnType(binaryTupleIndex);
 
-        IgniteUtils.ensureNotNull(binaryTuple, binaryTupleIndex, columnName);
-
-        return binaryTuple.shortValue(binaryTupleIndex);
+        return readShortValue(binaryTuple, binaryTupleIndex, actualType, 
columnName);
     }
 
     /** {@inheritDoc} */
@@ -224,11 +229,11 @@ public abstract class MutableTupleBinaryTupleAdapter 
implements Tuple, BinaryTup
             return tuple.shortValue(columnIndex);
         }
 
-        int binaryTupleIndex = validateSchemaColumnType(columnIndex, 
ColumnType.INT16);
-
-        IgniteUtils.ensureNotNull(binaryTuple, binaryTupleIndex, columnIndex);
+        Objects.checkIndex(columnIndex, columnCount);
+        int binaryTupleIndex = binaryTupleIndex(columnIndex);
+        ColumnType actualType = schemaColumnType(binaryTupleIndex);
 
-        return binaryTuple.shortValue(binaryTupleIndex);
+        return readShortValue(binaryTuple, binaryTupleIndex, actualType, 
columnIndex);
     }
 
     /** {@inheritDoc} */
@@ -238,11 +243,10 @@ public abstract class MutableTupleBinaryTupleAdapter 
implements Tuple, BinaryTup
             return tuple.intValue(columnName);
         }
 
-        int binaryTupleIndex = validateSchemaColumnType(columnName, 
ColumnType.INT32);
-
-        IgniteUtils.ensureNotNull(binaryTuple, binaryTupleIndex, columnName);
+        var binaryTupleIndex = resolveBinaryTupleIndexByColumnName(columnName);
+        ColumnType actualType = schemaColumnType(binaryTupleIndex);
 
-        return binaryTuple.intValue(binaryTupleIndex);
+        return readIntValue(binaryTuple, binaryTupleIndex, actualType, 
columnName);
     }
 
     /** {@inheritDoc} */
@@ -252,11 +256,11 @@ public abstract class MutableTupleBinaryTupleAdapter 
implements Tuple, BinaryTup
             return tuple.intValue(columnIndex);
         }
 
-        int binaryTupleIndex = validateSchemaColumnType(columnIndex, 
ColumnType.INT32);
-
-        IgniteUtils.ensureNotNull(binaryTuple, binaryTupleIndex, columnIndex);
+        Objects.checkIndex(columnIndex, columnCount);
+        int binaryTupleIndex = binaryTupleIndex(columnIndex);
+        ColumnType actualType = schemaColumnType(binaryTupleIndex);
 
-        return binaryTuple.intValue(binaryTupleIndex);
+        return readIntValue(binaryTuple, binaryTupleIndex, actualType, 
columnIndex);
     }
 
     /** {@inheritDoc} */
@@ -266,11 +270,10 @@ public abstract class MutableTupleBinaryTupleAdapter 
implements Tuple, BinaryTup
             return tuple.longValue(columnName);
         }
 
-        int binaryTupleIndex = validateSchemaColumnType(columnName, 
ColumnType.INT64);
+        var binaryTupleIndex = resolveBinaryTupleIndexByColumnName(columnName);
+        ColumnType actualType = schemaColumnType(binaryTupleIndex);
 
-        IgniteUtils.ensureNotNull(binaryTuple, binaryTupleIndex, columnName);
-
-        return binaryTuple.longValue(binaryTupleIndex);
+        return readLongValue(binaryTuple, binaryTupleIndex, actualType, 
columnName);
     }
 
     /** {@inheritDoc} */
@@ -280,11 +283,11 @@ public abstract class MutableTupleBinaryTupleAdapter 
implements Tuple, BinaryTup
             return tuple.longValue(columnIndex);
         }
 
-        int binaryTupleIndex = validateSchemaColumnType(columnIndex, 
ColumnType.INT64);
-
-        IgniteUtils.ensureNotNull(binaryTuple, binaryTupleIndex, columnIndex);
+        Objects.checkIndex(columnIndex, columnCount);
+        int binaryTupleIndex = binaryTupleIndex(columnIndex);
+        ColumnType actualType = schemaColumnType(binaryTupleIndex);
 
-        return binaryTuple.longValue(binaryTupleIndex);
+        return readLongValue(binaryTuple, binaryTupleIndex, actualType, 
columnIndex);
     }
 
     /** {@inheritDoc} */
@@ -294,11 +297,10 @@ public abstract class MutableTupleBinaryTupleAdapter 
implements Tuple, BinaryTup
             return tuple.floatValue(columnName);
         }
 
-        int binaryTupleIndex = validateSchemaColumnType(columnName, 
ColumnType.FLOAT);
+        var binaryTupleIndex = resolveBinaryTupleIndexByColumnName(columnName);
+        ColumnType actualType = schemaColumnType(binaryTupleIndex);
 
-        IgniteUtils.ensureNotNull(binaryTuple, binaryTupleIndex, columnName);
-
-        return binaryTuple.floatValue(binaryTupleIndex);
+        return readFloatValue(binaryTuple, binaryTupleIndex, actualType, 
columnName);
     }
 
     /** {@inheritDoc} */
@@ -308,11 +310,11 @@ public abstract class MutableTupleBinaryTupleAdapter 
implements Tuple, BinaryTup
             return tuple.floatValue(columnIndex);
         }
 
-        int binaryTupleIndex = validateSchemaColumnType(columnIndex, 
ColumnType.FLOAT);
-
-        IgniteUtils.ensureNotNull(binaryTuple, binaryTupleIndex, columnIndex);
+        Objects.checkIndex(columnIndex, columnCount);
+        int binaryTupleIndex = binaryTupleIndex(columnIndex);
+        ColumnType actualType = schemaColumnType(binaryTupleIndex);
 
-        return binaryTuple.floatValue(binaryTupleIndex);
+        return readFloatValue(binaryTuple, binaryTupleIndex, actualType, 
columnIndex);
     }
 
     /** {@inheritDoc} */
@@ -322,11 +324,10 @@ public abstract class MutableTupleBinaryTupleAdapter 
implements Tuple, BinaryTup
             return tuple.doubleValue(columnName);
         }
 
-        int binaryTupleIndex = validateSchemaColumnType(columnName, 
ColumnType.DOUBLE);
-
-        IgniteUtils.ensureNotNull(binaryTuple, binaryTupleIndex, columnName);
+        var binaryTupleIndex = resolveBinaryTupleIndexByColumnName(columnName);
+        ColumnType actualType = schemaColumnType(binaryTupleIndex);
 
-        return binaryTuple.doubleValue(binaryTupleIndex);
+        return readDoubleValue(binaryTuple, binaryTupleIndex, actualType, 
columnName);
     }
 
     /** {@inheritDoc} */
@@ -336,11 +337,11 @@ public abstract class MutableTupleBinaryTupleAdapter 
implements Tuple, BinaryTup
             return tuple.doubleValue(columnIndex);
         }
 
-        int binaryTupleIndex = validateSchemaColumnType(columnIndex, 
ColumnType.DOUBLE);
-
-        IgniteUtils.ensureNotNull(binaryTuple, binaryTupleIndex, columnIndex);
+        Objects.checkIndex(columnIndex, columnCount);
+        int binaryTupleIndex = binaryTupleIndex(columnIndex);
+        ColumnType actualType = schemaColumnType(binaryTupleIndex);
 
-        return binaryTuple.doubleValue(binaryTupleIndex);
+        return readDoubleValue(binaryTuple, binaryTupleIndex, actualType, 
columnIndex);
     }
 
     /** {@inheritDoc} */
@@ -539,15 +540,15 @@ public abstract class MutableTupleBinaryTupleAdapter 
implements Tuple, BinaryTup
         return publicIndex;
     }
 
-    private int binaryTupleIndex(String columnName, @Nullable ColumnType type) 
{
-        var binaryTupleIndex = binaryTupleIndex(columnName);
+    private int validateSchemaColumnType(String columnName, ColumnType type) {
+        var index = binaryTupleIndex(columnName);
 
-        if (binaryTupleIndex < 0) {
-            return binaryTupleIndex;
+        if (index < 0) {
+            throw new IllegalArgumentException("Column doesn't exist [name=" + 
columnName + ']');
         }
 
         if (type != null) {
-            ColumnType actualType = schemaColumnType(binaryTupleIndex);
+            ColumnType actualType = schemaColumnType(index);
 
             if (type != actualType) {
                 throw new ClassCastException("Column with name '" + columnName 
+ "' has type " + actualType
@@ -555,16 +556,6 @@ public abstract class MutableTupleBinaryTupleAdapter 
implements Tuple, BinaryTup
             }
         }
 
-        return binaryTupleIndex;
-    }
-
-    private int validateSchemaColumnType(String columnName, ColumnType type) {
-        var index = binaryTupleIndex(columnName, type);
-
-        if (index < 0) {
-            throw new IllegalArgumentException("Column doesn't exist [name=" + 
columnName + ']');
-        }
-
         return index;
     }
 
@@ -652,6 +643,16 @@ public abstract class MutableTupleBinaryTupleAdapter 
implements Tuple, BinaryTup
         }
     }
 
+    private int resolveBinaryTupleIndexByColumnName(String columnName) {
+        int binaryTupleIndex = binaryTupleIndex(columnName);
+
+        if (binaryTupleIndex < 0) {
+            throw new IllegalArgumentException("Column doesn't exist [name=" + 
columnName + ']');
+        }
+
+        return binaryTupleIndex;
+    }
+
     @Override
     public String toString() {
         return S.tupleToString(this);
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/util/TupleTypeCastUtils.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/util/TupleTypeCastUtils.java
new file mode 100644
index 00000000000..bc9489a85b6
--- /dev/null
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/util/TupleTypeCastUtils.java
@@ -0,0 +1,510 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.internal.util;
+
+import java.util.EnumSet;
+import java.util.Set;
+import org.apache.ignite.internal.lang.IgniteStringFormatter;
+import org.apache.ignite.internal.lang.InternalTuple;
+import org.apache.ignite.sql.ColumnType;
+
+/**
+ * Utility helpers for reading and casting values from {@link InternalTuple} 
instances.
+ *
+ * <p>Provides typed readers (byte, short, int, long, float, double) that 
validate the actual column
+ * type and perform safe implicit conversions where allowed. Methods throw 
{@link ClassCastException}
+ * when the requested type is incompatible with the actual column type. 
Narrowing conversions that
+ * would lose information throw {@link ArithmeticException}.</p>
+ */
+public class TupleTypeCastUtils {
+    private static final String TYPE_CAST_ERROR_COLUMN_NAME = "Column with 
name '{}' has type {} but {} was requested";
+
+    private static final String TYPE_CAST_ERROR_COLUMN_INDEX = "Column with 
index {} has type {} but {} was requested";
+
+    private static final Set<ColumnType> INT_TYPES =
+            EnumSet.of(ColumnType.INT8, ColumnType.INT16, ColumnType.INT32, 
ColumnType.INT64);
+
+    /**
+     * Reads a value from the tuple and converts it to a byte if possible.
+     *
+     * @param binaryTuple the tuple instance
+     * @param binaryTupleIndex index of the column in the tuple
+     * @param actualType actual column type stored in the tuple
+     * @param columnIndex column index used for error reporting
+     * @return the column value as a {@code byte}
+     * @throws ClassCastException if the actual column type is not an integer 
type
+     * @throws NullPointerException if {@code binaryTuple} is null (enforced 
by {@code IgniteUtils.ensureNotNull})
+     * @throws ArithmeticException if the value cannot be represented as 
{@code byte} without overflow
+     */
+    public static byte readByteValue(InternalTuple binaryTuple, int 
binaryTupleIndex, ColumnType actualType, int columnIndex) {
+        if (!integerType(actualType)) {
+            throwClassCastException(ColumnType.INT8, actualType, columnIndex);
+        }
+
+        IgniteUtils.ensureNotNull(binaryTuple, binaryTupleIndex, columnIndex);
+
+        return castToByte(binaryTuple, binaryTupleIndex, actualType);
+    }
+
+    /**
+     * Reads a value from the tuple and converts it to a byte if possible.
+     *
+     * @param binaryTuple the tuple instance
+     * @param binaryTupleIndex index of the column in the tuple
+     * @param actualType actual column type stored in the tuple
+     * @param columnName column name used for error reporting
+     * @return the column value as a {@code byte}
+     * @throws ClassCastException if the actual column type is not an integer 
type
+     * @throws NullPointerException if {@code binaryTuple} is null (enforced 
by {@code IgniteUtils.ensureNotNull})
+     * @throws ArithmeticException if the value cannot be represented as 
{@code byte} without overflow
+     */
+    public static byte readByteValue(InternalTuple binaryTuple, int 
binaryTupleIndex, ColumnType actualType, String columnName) {
+        if (!integerType(actualType)) {
+            throwClassCastException(ColumnType.INT8, actualType, columnName);
+        }
+
+        IgniteUtils.ensureNotNull(binaryTuple, binaryTupleIndex, columnName);
+
+        return castToByte(binaryTuple, binaryTupleIndex, actualType);
+    }
+
+    /**
+     * Reads a value from the tuple and converts it to a short if possible.
+     *
+     * @param binaryTuple the tuple instance
+     * @param binaryTupleIndex index of the column in the tuple
+     * @param actualType actual column type stored in the tuple
+     * @param columnIndex column index used for error reporting
+     * @return the column value as a {@code short}
+     * @throws ClassCastException if the actual column type is not an integer 
type
+     * @throws NullPointerException if {@code binaryTuple} is null
+     * @throws ArithmeticException if the value cannot be represented as 
{@code short} without overflow
+     */
+    public static short readShortValue(InternalTuple binaryTuple, int 
binaryTupleIndex, ColumnType actualType, int columnIndex) {
+        if (!integerType(actualType)) {
+            throwClassCastException(ColumnType.INT16, actualType, columnIndex);
+        }
+
+        IgniteUtils.ensureNotNull(binaryTuple, binaryTupleIndex, columnIndex);
+
+        return castToShort(binaryTuple, binaryTupleIndex, actualType);
+    }
+
+    /**
+     * Reads a value from the tuple and converts it to a short if possible.
+     *
+     * @param binaryTuple the tuple instance
+     * @param binaryTupleIndex index of the column in the tuple
+     * @param actualType actual column type stored in the tuple
+     * @param columnName column name used for error reporting
+     * @return the column value as a {@code short}
+     * @throws ClassCastException if the actual column type is not an integer 
type
+     * @throws NullPointerException if {@code binaryTuple} is null
+     * @throws ArithmeticException if the value cannot be represented as 
{@code short} without overflow
+     */
+    public static short readShortValue(InternalTuple binaryTuple, int 
binaryTupleIndex, ColumnType actualType, String columnName) {
+        if (!integerType(actualType)) {
+            throwClassCastException(ColumnType.INT16, actualType, columnName);
+        }
+
+        IgniteUtils.ensureNotNull(binaryTuple, binaryTupleIndex, columnName);
+
+        return castToShort(binaryTuple, binaryTupleIndex, actualType);
+    }
+
+    /**
+     * Reads a value from the tuple and converts it to an int if possible.
+     *
+     * @param binaryTuple the tuple instance
+     * @param binaryTupleIndex index of the column in the tuple
+     * @param actualType actual column type stored in the tuple
+     * @param columnIndex column index used for error reporting
+     * @return the column value as an {@code int}
+     * @throws ClassCastException if the actual column type is not an integer 
type
+     * @throws NullPointerException if {@code binaryTuple} is null
+     * @throws ArithmeticException if the value cannot be represented as 
{@code int} without overflow
+     */
+    public static int readIntValue(InternalTuple binaryTuple, int 
binaryTupleIndex, ColumnType actualType, int columnIndex) {
+        if (!integerType(actualType)) {
+            throwClassCastException(ColumnType.INT32, actualType, columnIndex);
+        }
+
+        IgniteUtils.ensureNotNull(binaryTuple, binaryTupleIndex, columnIndex);
+
+        return castToInt(binaryTuple, binaryTupleIndex, actualType);
+    }
+
+    /**
+     * Reads a value from the tuple and converts it to an int if possible.
+     *
+     * @param binaryTuple the tuple instance
+     * @param binaryTupleIndex index of the column in the tuple
+     * @param actualType actual column type stored in the tuple
+     * @param columnName column name used for error reporting
+     * @return the column value as an {@code int}
+     * @throws ClassCastException if the actual column type is not an integer 
type
+     * @throws NullPointerException if {@code binaryTuple} is null
+     * @throws ArithmeticException if the value cannot be represented as 
{@code int} without overflow
+     */
+    public static int readIntValue(InternalTuple binaryTuple, int 
binaryTupleIndex, ColumnType actualType, String columnName) {
+        if (!integerType(actualType)) {
+            throwClassCastException(ColumnType.INT32, actualType, columnName);
+        }
+
+        IgniteUtils.ensureNotNull(binaryTuple, binaryTupleIndex, columnName);
+
+        return castToInt(binaryTuple, binaryTupleIndex, actualType);
+    }
+
+    /**
+     * Reads a value from the tuple and returns it as a long. Only integer 
column types are allowed.
+     *
+     * @param binaryTuple the tuple instance
+     * @param binaryTupleIndex index of the column in the tuple
+     * @param actualType actual column type stored in the tuple
+     * @param columnIndex column index used for error reporting
+     * @return the column value as a {@code long}
+     * @throws ClassCastException if the actual column type is not an integer 
type
+     * @throws NullPointerException if {@code binaryTuple} is null
+     */
+    public static long readLongValue(InternalTuple binaryTuple, int 
binaryTupleIndex, ColumnType actualType, int columnIndex) {
+        if (!integerType(actualType)) {
+            throwClassCastException(ColumnType.INT64, actualType, columnIndex);
+        }
+
+        IgniteUtils.ensureNotNull(binaryTuple, binaryTupleIndex, columnIndex);
+
+        return binaryTuple.longValue(binaryTupleIndex);
+    }
+
+    /**
+     * Reads a value from the tuple and returns it as a long. Only integer 
column types are allowed.
+     *
+     * @param binaryTuple the tuple instance
+     * @param binaryTupleIndex index of the column in the tuple
+     * @param actualType actual column type stored in the tuple
+     * @param columnName column name used for error reporting
+     * @return the column value as a {@code long}
+     * @throws ClassCastException if the actual column type is not an integer 
type
+     * @throws NullPointerException if {@code binaryTuple} is null
+     */
+    public static long readLongValue(InternalTuple binaryTuple, int 
binaryTupleIndex, ColumnType actualType, String columnName) {
+        if (!integerType(actualType)) {
+            throwClassCastException(ColumnType.INT64, actualType, columnName);
+        }
+
+        IgniteUtils.ensureNotNull(binaryTuple, binaryTupleIndex, columnName);
+
+        return binaryTuple.longValue(binaryTupleIndex);
+    }
+
+    /**
+     * Reads a value from the tuple and converts it to a float if possible. 
Accepts FLOAT and DOUBLE actual types.
+     *
+     * @param binaryTuple the tuple instance
+     * @param binaryTupleIndex index of the column in the tuple
+     * @param actualType actual column type stored in the tuple
+     * @param columnIndex column index used for error reporting
+     * @return the column value as a {@code float}
+     * @throws ClassCastException if the actual column type is neither FLOAT 
nor DOUBLE
+     * @throws NullPointerException if {@code binaryTuple} is null
+     * @throws ArithmeticException if a double value cannot be represented as 
float without precision loss
+     */
+    public static float readFloatValue(InternalTuple binaryTuple, int 
binaryTupleIndex, ColumnType actualType, int columnIndex) {
+        if (actualType == ColumnType.FLOAT || actualType == ColumnType.DOUBLE) 
{
+            IgniteUtils.ensureNotNull(binaryTuple, binaryTupleIndex, 
columnIndex);
+
+            return castToFloat(binaryTuple, binaryTupleIndex, actualType);
+        }
+
+        throw newClassCastException(ColumnType.FLOAT, actualType, columnIndex);
+    }
+
+    /**
+     * Reads a value from the tuple and converts it to a float if possible. 
Accepts FLOAT and DOUBLE actual types.
+     *
+     * @param binaryTuple the tuple instance
+     * @param binaryTupleIndex index of the column in the tuple
+     * @param actualType actual column type stored in the tuple
+     * @param columnName column name used for error reporting
+     * @return the column value as a {@code float}
+     * @throws ClassCastException if the actual column type is neither FLOAT 
nor DOUBLE
+     * @throws NullPointerException if {@code binaryTuple} is null
+     * @throws ArithmeticException if a double value cannot be represented as 
float without precision loss
+     */
+    public static float readFloatValue(InternalTuple binaryTuple, int 
binaryTupleIndex, ColumnType actualType, String columnName) {
+        if (actualType == ColumnType.FLOAT || actualType == ColumnType.DOUBLE) 
{
+            IgniteUtils.ensureNotNull(binaryTuple, binaryTupleIndex, 
columnName);
+
+            return castToFloat(binaryTuple, binaryTupleIndex, actualType);
+        }
+
+        throw newClassCastException(ColumnType.FLOAT, actualType, columnName);
+    }
+
+    /**
+     * Reads a value from the tuple and returns it as a double. Accepts DOUBLE 
and FLOAT actual types.
+     *
+     * @param binaryTuple the tuple instance
+     * @param binaryTupleIndex index of the column in the tuple
+     * @param actualType actual column type stored in the tuple
+     * @param columnIndex column index used for error reporting
+     * @return the column value as a {@code double}
+     * @throws ClassCastException if the actual column type is neither DOUBLE 
nor FLOAT
+     * @throws NullPointerException if {@code binaryTuple} is null
+     */
+    public static double readDoubleValue(InternalTuple binaryTuple, int 
binaryTupleIndex, ColumnType actualType, int columnIndex) {
+        if (actualType == ColumnType.DOUBLE || actualType == ColumnType.FLOAT) 
{
+            IgniteUtils.ensureNotNull(binaryTuple, binaryTupleIndex, 
columnIndex);
+
+            return binaryTuple.doubleValue(binaryTupleIndex);
+        }
+
+        throw newClassCastException(ColumnType.FLOAT, actualType, columnIndex);
+    }
+
+    /**
+     * Reads a value from the tuple and returns it as a double. Accepts DOUBLE 
and FLOAT actual types.
+     *
+     * @param binaryTuple the tuple instance
+     * @param binaryTupleIndex index of the column in the tuple
+     * @param actualType actual column type stored in the tuple
+     * @param columnName column name used for error reporting
+     * @return the column value as a {@code double}
+     * @throws ClassCastException if the actual column type is neither DOUBLE 
nor FLOAT
+     * @throws NullPointerException if {@code binaryTuple} is null
+     */
+    public static double readDoubleValue(InternalTuple binaryTuple, int 
binaryTupleIndex, ColumnType actualType, String columnName) {
+        if (actualType == ColumnType.DOUBLE || actualType == ColumnType.FLOAT) 
{
+            IgniteUtils.ensureNotNull(binaryTuple, binaryTupleIndex, 
columnName);
+
+            return binaryTuple.doubleValue(binaryTupleIndex);
+        }
+
+        throw newClassCastException(ColumnType.FLOAT, actualType, columnName);
+    }
+
+    /**
+     * Casts an integer value from the tuple to {@code byte} performing range 
checks.
+     *
+     * @param binaryTuple the tuple instance
+     * @param binaryTupleIndex index of the column in the tuple
+     * @param valueType actual integer column type
+     * @return the value as a {@code byte}
+     * @throws ArithmeticException if value doesn't fit into a {@code byte}
+     */
+    private static byte castToByte(InternalTuple binaryTuple, int 
binaryTupleIndex, ColumnType valueType) {
+        switch (valueType) {
+            case INT8:
+                return binaryTuple.byteValue(binaryTupleIndex);
+
+            case INT16: {
+                short value = binaryTuple.shortValue(binaryTupleIndex);
+                byte byteValue = (byte) value;
+
+                if (byteValue == value) {
+                    return byteValue;
+                }
+
+                break;
+            }
+            case INT32: {
+                int value = binaryTuple.intValue(binaryTupleIndex);
+                byte byteValue = (byte) value;
+
+                if (byteValue == value) {
+                    return byteValue;
+                }
+
+                break;
+            }
+            case INT64: {
+                long value = binaryTuple.longValue(binaryTupleIndex);
+                byte byteValue = (byte) value;
+
+                if (byteValue == value) {
+                    return byteValue;
+                }
+
+                break;
+            }
+
+            default:
+                assert false : valueType;
+        }
+
+        throw new ArithmeticException("Byte value overflow");
+    }
+
+    /**
+     * Casts an integer value from the tuple to {@code short} performing range 
checks.
+     *
+     * @param binaryTuple the tuple instance
+     * @param binaryTupleIndex index of the column in the tuple
+     * @param valueType actual integer column type
+     * @return the value as a {@code short}
+     * @throws ArithmeticException if value doesn't fit into a {@code short}
+     */
+    private static short castToShort(InternalTuple binaryTuple, int 
binaryTupleIndex, ColumnType valueType) {
+        switch (valueType) {
+            case INT16:
+            case INT8:
+                return binaryTuple.shortValue(binaryTupleIndex);
+
+            case INT32: {
+                int value = binaryTuple.intValue(binaryTupleIndex);
+                short shortValue = (short) value;
+
+                if (shortValue == value) {
+                    return shortValue;
+                }
+
+                break;
+            }
+            case INT64: {
+                long value = binaryTuple.longValue(binaryTupleIndex);
+                short shortValue = (short) value;
+
+                if (shortValue == value) {
+                    return shortValue;
+                }
+
+                break;
+            }
+
+            default:
+                assert false : valueType;
+        }
+
+        throw new ArithmeticException("Short value overflow");
+    }
+
+    /**
+     * Casts an integer value from the tuple to {@code int} performing range 
checks.
+     *
+     * @param binaryTuple the tuple instance
+     * @param binaryTupleIndex index of the column in the tuple
+     * @param valueType actual integer column type
+     * @return the value as an {@code int}
+     * @throws ArithmeticException if value doesn't fit into an {@code int}
+     */
+    private static int castToInt(InternalTuple binaryTuple, int 
binaryTupleIndex, ColumnType valueType) {
+        switch (valueType) {
+            case INT32:
+            case INT16:
+            case INT8:
+                return binaryTuple.intValue(binaryTupleIndex);
+
+            case INT64: {
+                long value = binaryTuple.longValue(binaryTupleIndex);
+                int intValue = (int) value;
+
+                if (intValue == value) {
+                    return intValue;
+                }
+
+                break;
+            }
+
+            default:
+                assert false : valueType;
+        }
+
+        throw new ArithmeticException("Int value overflow");
+    }
+
+    /**
+     * Casts a floating-point value from the tuple to {@code float} performing 
precision checks.
+     *
+     * @param binaryTuple the tuple instance
+     * @param binaryTupleIndex index of the column in the tuple
+     * @param actualType actual floating-point column type
+     * @return the value as a {@code float}
+     * @throws ArithmeticException if a double value cannot be represented as 
a float without precision loss
+     */
+    private static float castToFloat(InternalTuple binaryTuple, int 
binaryTupleIndex, ColumnType actualType) {
+        if (actualType == ColumnType.FLOAT) {
+            return binaryTuple.floatValue(binaryTupleIndex);
+        }
+
+        double doubleValue = binaryTuple.doubleValue(binaryTupleIndex);
+        float floatValue = (float) doubleValue;
+
+        if (doubleValue == floatValue) {
+            return floatValue;
+        }
+
+        throw new ArithmeticException("Float value overflow");
+    }
+
+    /**
+     * Validates that the requested column type matches the actual type and 
throws {@link ClassCastException}
+     * otherwise.
+     *
+     * @param requestedType the requested column type
+     * @param actualType the actual column type
+     * @param columnIndex column index used for error reporting
+     * @throws ClassCastException when types do not match
+     */
+    public static void validateColumnType(ColumnType requestedType, ColumnType 
actualType, int columnIndex) {
+        if (requestedType != actualType) {
+            throwClassCastException(requestedType, actualType, columnIndex);
+        }
+    }
+
+    /**
+     * Validates that the requested column type matches the actual type and 
throws {@link ClassCastException}
+     * otherwise.
+     *
+     * @param requestedType the requested column type
+     * @param actualType the actual column type
+     * @param columnName column name used for error reporting
+     * @throws ClassCastException when types do not match
+     */
+    public static void validateColumnType(ColumnType requestedType, ColumnType 
actualType, String columnName) {
+        if (requestedType != actualType) {
+            throwClassCastException(requestedType, actualType, columnName);
+        }
+    }
+
+    private static void throwClassCastException(ColumnType requestedType, 
ColumnType actualType, int index) {
+        throw newClassCastException(requestedType, actualType, index);
+    }
+
+    private static void throwClassCastException(ColumnType requestedType, 
ColumnType actualType, String columnName) {
+        throw newClassCastException(requestedType, actualType, columnName);
+    }
+
+    private static RuntimeException newClassCastException(ColumnType 
requestedType, ColumnType actualType, int index) {
+        return new 
ClassCastException(IgniteStringFormatter.format(TYPE_CAST_ERROR_COLUMN_INDEX, 
index, actualType, requestedType));
+    }
+
+    private static RuntimeException newClassCastException(ColumnType 
requestedType, ColumnType actualType, String columnName) {
+        return new 
ClassCastException(IgniteStringFormatter.format(TYPE_CAST_ERROR_COLUMN_NAME, 
columnName, actualType, requestedType));
+    }
+
+    /**
+     * Returns {@code true} when the provided {@link ColumnType} is an integer 
type.
+     *
+     * @param actualType column type to check
+     * @return {@code true} if {@code actualType} is one of INT8, INT16, INT32 
or INT64
+     */
+    private static boolean integerType(ColumnType actualType) {
+        return INT_TYPES.contains(actualType);
+    }
+}
diff --git 
a/modules/table/src/main/java/org/apache/ignite/internal/table/AbstractRowTupleAdapter.java
 
b/modules/table/src/main/java/org/apache/ignite/internal/table/AbstractRowTupleAdapter.java
index 08a9f898904..96b2ac2bb59 100644
--- 
a/modules/table/src/main/java/org/apache/ignite/internal/table/AbstractRowTupleAdapter.java
+++ 
b/modules/table/src/main/java/org/apache/ignite/internal/table/AbstractRowTupleAdapter.java
@@ -17,6 +17,8 @@
 
 package org.apache.ignite.internal.table;
 
+import static 
org.apache.ignite.internal.util.TupleTypeCastUtils.validateColumnType;
+
 import java.math.BigDecimal;
 import java.time.Instant;
 import java.time.LocalDate;
@@ -31,7 +33,9 @@ import org.apache.ignite.internal.schema.SchemaAware;
 import org.apache.ignite.internal.schema.SchemaDescriptor;
 import org.apache.ignite.internal.schema.row.Row;
 import org.apache.ignite.internal.util.IgniteUtils;
+import org.apache.ignite.internal.util.TupleTypeCastUtils;
 import org.apache.ignite.lang.util.IgniteNameUtils;
+import org.apache.ignite.sql.ColumnType;
 import org.apache.ignite.table.Tuple;
 import org.jetbrains.annotations.Nullable;
 
@@ -115,6 +119,8 @@ public abstract class AbstractRowTupleAdapter implements 
Tuple, SchemaAware {
     public boolean booleanValue(String columnName) {
         Column col = rowColumnByName(columnName);
 
+        validateColumnType(ColumnType.BOOLEAN, col.type().spec(), columnName);
+
         int binaryTupleIndex = correctIndex(col);
 
         IgniteUtils.ensureNotNull(row, binaryTupleIndex, columnName);
@@ -127,6 +133,8 @@ public abstract class AbstractRowTupleAdapter implements 
Tuple, SchemaAware {
     public boolean booleanValue(int columnIndex) {
         Column col = rowColumnByIndex(columnIndex);
 
+        validateColumnType(ColumnType.BOOLEAN, col.type().spec(), columnIndex);
+
         int binaryTupleIndex = correctIndex(col);
 
         IgniteUtils.ensureNotNull(row, binaryTupleIndex, columnIndex);
@@ -141,9 +149,7 @@ public abstract class AbstractRowTupleAdapter implements 
Tuple, SchemaAware {
 
         int binaryTupleIndex = correctIndex(col);
 
-        IgniteUtils.ensureNotNull(row, binaryTupleIndex, columnName);
-
-        return row.byteValue(binaryTupleIndex);
+        return TupleTypeCastUtils.readByteValue(row, binaryTupleIndex, 
col.type().spec(), columnName);
     }
 
     /** {@inheritDoc} */
@@ -153,9 +159,7 @@ public abstract class AbstractRowTupleAdapter implements 
Tuple, SchemaAware {
 
         int binaryTupleIndex = correctIndex(col);
 
-        IgniteUtils.ensureNotNull(row, binaryTupleIndex, columnIndex);
-
-        return row.byteValue(binaryTupleIndex);
+        return TupleTypeCastUtils.readByteValue(row, binaryTupleIndex, 
col.type().spec(), columnIndex);
     }
 
     /** {@inheritDoc} */
@@ -165,9 +169,7 @@ public abstract class AbstractRowTupleAdapter implements 
Tuple, SchemaAware {
 
         int binaryTupleIndex = correctIndex(col);
 
-        IgniteUtils.ensureNotNull(row, binaryTupleIndex, columnName);
-
-        return row.shortValue(binaryTupleIndex);
+        return TupleTypeCastUtils.readShortValue(row, binaryTupleIndex, 
col.type().spec(), columnName);
     }
 
     /** {@inheritDoc} */
@@ -177,9 +179,7 @@ public abstract class AbstractRowTupleAdapter implements 
Tuple, SchemaAware {
 
         int binaryTupleIndex = correctIndex(col);
 
-        IgniteUtils.ensureNotNull(row, binaryTupleIndex, columnIndex);
-
-        return row.shortValue(binaryTupleIndex);
+        return TupleTypeCastUtils.readShortValue(row, binaryTupleIndex, 
col.type().spec(), columnIndex);
     }
 
     /** {@inheritDoc} */
@@ -189,9 +189,7 @@ public abstract class AbstractRowTupleAdapter implements 
Tuple, SchemaAware {
 
         int binaryTupleIndex = correctIndex(col);
 
-        IgniteUtils.ensureNotNull(row, binaryTupleIndex, columnName);
-
-        return row.intValue(binaryTupleIndex);
+        return TupleTypeCastUtils.readIntValue(row, binaryTupleIndex, 
col.type().spec(), columnName);
     }
 
     /** {@inheritDoc} */
@@ -201,9 +199,7 @@ public abstract class AbstractRowTupleAdapter implements 
Tuple, SchemaAware {
 
         int binaryTupleIndex = correctIndex(col);
 
-        IgniteUtils.ensureNotNull(row, binaryTupleIndex, columnIndex);
-
-        return row.intValue(binaryTupleIndex);
+        return TupleTypeCastUtils.readIntValue(row, binaryTupleIndex, 
col.type().spec(), columnIndex);
     }
 
     /** {@inheritDoc} */
@@ -213,9 +209,7 @@ public abstract class AbstractRowTupleAdapter implements 
Tuple, SchemaAware {
 
         int binaryTupleIndex = correctIndex(col);
 
-        IgniteUtils.ensureNotNull(row, binaryTupleIndex, columnName);
-
-        return row.longValue(binaryTupleIndex);
+        return TupleTypeCastUtils.readLongValue(row, binaryTupleIndex, 
col.type().spec(), columnName);
     }
 
     /** {@inheritDoc} */
@@ -225,9 +219,7 @@ public abstract class AbstractRowTupleAdapter implements 
Tuple, SchemaAware {
 
         int binaryTupleIndex = correctIndex(col);
 
-        IgniteUtils.ensureNotNull(row, binaryTupleIndex, columnIndex);
-
-        return row.longValue(binaryTupleIndex);
+        return TupleTypeCastUtils.readLongValue(row, binaryTupleIndex, 
col.type().spec(), columnIndex);
     }
 
     /** {@inheritDoc} */
@@ -237,9 +229,7 @@ public abstract class AbstractRowTupleAdapter implements 
Tuple, SchemaAware {
 
         int binaryTupleIndex = correctIndex(col);
 
-        IgniteUtils.ensureNotNull(row, binaryTupleIndex, columnName);
-
-        return row.floatValue(binaryTupleIndex);
+        return TupleTypeCastUtils.readFloatValue(row, binaryTupleIndex, 
col.type().spec(), columnName);
     }
 
     /** {@inheritDoc} */
@@ -251,7 +241,7 @@ public abstract class AbstractRowTupleAdapter implements 
Tuple, SchemaAware {
 
         IgniteUtils.ensureNotNull(row, binaryTupleIndex, columnIndex);
 
-        return row.floatValue(binaryTupleIndex);
+        return TupleTypeCastUtils.readFloatValue(row, binaryTupleIndex, 
col.type().spec(), columnIndex);
     }
 
     /** {@inheritDoc} */
@@ -261,9 +251,7 @@ public abstract class AbstractRowTupleAdapter implements 
Tuple, SchemaAware {
 
         int binaryTupleIndex = correctIndex(col);
 
-        IgniteUtils.ensureNotNull(row, binaryTupleIndex, columnName);
-
-        return row.doubleValue(binaryTupleIndex);
+        return TupleTypeCastUtils.readDoubleValue(row, binaryTupleIndex, 
col.type().spec(), columnName);
     }
 
     /** {@inheritDoc} */
@@ -273,9 +261,7 @@ public abstract class AbstractRowTupleAdapter implements 
Tuple, SchemaAware {
 
         int binaryTupleIndex = correctIndex(col);
 
-        IgniteUtils.ensureNotNull(row, binaryTupleIndex, columnIndex);
-
-        return row.doubleValue(binaryTupleIndex);
+        return TupleTypeCastUtils.readDoubleValue(row, binaryTupleIndex, 
col.type().spec(), columnIndex);
     }
 
     /** {@inheritDoc} */
@@ -283,6 +269,8 @@ public abstract class AbstractRowTupleAdapter implements 
Tuple, SchemaAware {
     public BigDecimal decimalValue(String columnName) {
         Column col = rowColumnByName(columnName);
 
+        validateColumnType(ColumnType.DECIMAL, col.type().spec(), columnName);
+
         return row.decimalValue(correctIndex(col));
     }
 
@@ -291,6 +279,8 @@ public abstract class AbstractRowTupleAdapter implements 
Tuple, SchemaAware {
     public BigDecimal decimalValue(int columnIndex) {
         Column col = rowColumnByIndex(columnIndex);
 
+        validateColumnType(ColumnType.DECIMAL, col.type().spec(), columnIndex);
+
         return row.decimalValue(correctIndex(col));
     }
 
@@ -299,6 +289,8 @@ public abstract class AbstractRowTupleAdapter implements 
Tuple, SchemaAware {
     public String stringValue(String columnName) {
         Column col = rowColumnByName(columnName);
 
+        validateColumnType(ColumnType.STRING, col.type().spec(), columnName);
+
         return row.stringValue(correctIndex(col));
     }
 
@@ -307,6 +299,8 @@ public abstract class AbstractRowTupleAdapter implements 
Tuple, SchemaAware {
     public String stringValue(int columnIndex) {
         Column col = rowColumnByIndex(columnIndex);
 
+        validateColumnType(ColumnType.STRING, col.type().spec(), columnIndex);
+
         return row.stringValue(correctIndex(col));
     }
 
@@ -315,6 +309,8 @@ public abstract class AbstractRowTupleAdapter implements 
Tuple, SchemaAware {
     public byte[] bytesValue(String columnName) {
         Column col = rowColumnByName(columnName);
 
+        validateColumnType(ColumnType.BYTE_ARRAY, col.type().spec(), 
columnName);
+
         return row.bytesValue(correctIndex(col));
     }
 
@@ -323,6 +319,8 @@ public abstract class AbstractRowTupleAdapter implements 
Tuple, SchemaAware {
     public byte[] bytesValue(int columnIndex) {
         Column col = rowColumnByIndex(columnIndex);
 
+        validateColumnType(ColumnType.BYTE_ARRAY, col.type().spec(), 
columnIndex);
+
         return row.bytesValue(correctIndex(col));
     }
 
@@ -331,6 +329,8 @@ public abstract class AbstractRowTupleAdapter implements 
Tuple, SchemaAware {
     public UUID uuidValue(String columnName) {
         Column col = rowColumnByName(columnName);
 
+        validateColumnType(ColumnType.UUID, col.type().spec(), columnName);
+
         return row.uuidValue(correctIndex(col));
     }
 
@@ -339,6 +339,8 @@ public abstract class AbstractRowTupleAdapter implements 
Tuple, SchemaAware {
     public UUID uuidValue(int columnIndex) {
         Column col = rowColumnByIndex(columnIndex);
 
+        validateColumnType(ColumnType.UUID, col.type().spec(), columnIndex);
+
         return row.uuidValue(correctIndex(col));
     }
 
@@ -347,6 +349,8 @@ public abstract class AbstractRowTupleAdapter implements 
Tuple, SchemaAware {
     public LocalDate dateValue(String columnName) {
         Column col = rowColumnByName(columnName);
 
+        validateColumnType(ColumnType.DATE, col.type().spec(), columnName);
+
         return row.dateValue(correctIndex(col));
     }
 
@@ -355,6 +359,8 @@ public abstract class AbstractRowTupleAdapter implements 
Tuple, SchemaAware {
     public LocalDate dateValue(int columnIndex) {
         Column col = rowColumnByIndex(columnIndex);
 
+        validateColumnType(ColumnType.DATE, col.type().spec(), columnIndex);
+
         return row.dateValue(correctIndex(col));
     }
 
@@ -363,6 +369,8 @@ public abstract class AbstractRowTupleAdapter implements 
Tuple, SchemaAware {
     public LocalTime timeValue(String columnName) {
         Column col = rowColumnByName(columnName);
 
+        validateColumnType(ColumnType.TIME, col.type().spec(), columnName);
+
         return row.timeValue(correctIndex(col));
     }
 
@@ -371,6 +379,8 @@ public abstract class AbstractRowTupleAdapter implements 
Tuple, SchemaAware {
     public LocalTime timeValue(int columnIndex) {
         Column col = rowColumnByIndex(columnIndex);
 
+        validateColumnType(ColumnType.TIME, col.type().spec(), columnIndex);
+
         return row.timeValue(correctIndex(col));
     }
 
@@ -379,6 +389,8 @@ public abstract class AbstractRowTupleAdapter implements 
Tuple, SchemaAware {
     public LocalDateTime datetimeValue(String columnName) {
         Column col = rowColumnByName(columnName);
 
+        validateColumnType(ColumnType.DATETIME, col.type().spec(), columnName);
+
         return row.dateTimeValue(correctIndex(col));
     }
 
@@ -387,6 +399,8 @@ public abstract class AbstractRowTupleAdapter implements 
Tuple, SchemaAware {
     public LocalDateTime datetimeValue(int columnIndex) {
         Column col = rowColumnByIndex(columnIndex);
 
+        validateColumnType(ColumnType.DATETIME, col.type().spec(), 
columnIndex);
+
         return row.dateTimeValue(correctIndex(col));
     }
 
@@ -395,6 +409,8 @@ public abstract class AbstractRowTupleAdapter implements 
Tuple, SchemaAware {
     public Instant timestampValue(String columnName) {
         Column col = rowColumnByName(columnName);
 
+        validateColumnType(ColumnType.TIMESTAMP, col.type().spec(), 
columnName);
+
         return row.timestampValue(correctIndex(col));
     }
 
@@ -403,6 +419,8 @@ public abstract class AbstractRowTupleAdapter implements 
Tuple, SchemaAware {
     public Instant timestampValue(int columnIndex) {
         Column col = rowColumnByIndex(columnIndex);
 
+        validateColumnType(ColumnType.TIMESTAMP, col.type().spec(), 
columnIndex);
+
         return row.timestampValue(correctIndex(col));
     }
 

Reply via email to