You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by pt...@apache.org on 2021/07/27 12:18:12 UTC

[ignite-3] branch main updated: IGNITE-14342 Extend Tuple interface with ordered field access

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

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


The following commit(s) were added to refs/heads/main by this push:
     new a007259  IGNITE-14342 Extend Tuple interface with ordered field access
a007259 is described below

commit a00725987cdf49bb54246c552e000b4ad6832b8e
Author: Pavel Tupitsyn <pt...@apache.org>
AuthorDate: Tue Jul 27 15:17:44 2021 +0300

    IGNITE-14342 Extend Tuple interface with ordered field access
    
    To enable efficient data access, extend `Tuple` interface with:
    
    * `int columnCount()`
    * `String columnName(int columnIndex)`
    * `Integer columnIndex(String columnName)`
    * `T value(int columnIndex)` and typed overloads
    * `extends Iterable<Object>`
    
    Inspired by standard APIs like JDBC, ODBC, ADO.NET:
    * https://docs.oracle.com/javase/7/docs/api/java/sql/ResultSet.html
    * https://docs.microsoft.com/en-us/dotnet/api/system.data.common.dbdatareader?view=net-5.0
---
 .../main/java/org/apache/ignite/table/Tuple.java   | 164 +++++++++++++---
 .../java/org/apache/ignite/table/TupleBuilder.java |   4 +-
 .../handler/ClientInboundMessageHandler.java       |   6 +-
 .../ignite/internal/client/table/ClientSchema.java |  12 ++
 .../ignite/internal/client/table/ClientTable.java  |  20 +-
 .../internal/client/table/ClientTupleBuilder.java  | 209 +++++++++++++++++----
 .../apache/ignite/client/AbstractClientTest.java   |   8 +-
 .../org/apache/ignite/client/ClientTableTest.java  |  27 ++-
 .../ignite/client/ClientTupleBuilderTest.java      | 196 +++++++++++++++++++
 .../ignite/internal/schema/SchemaDescriptor.java   |  15 ++
 .../internal/table/LiveSchemaTupleBuilderImpl.java |   8 +-
 .../ignite/internal/table/RowChunkAdapter.java     | 160 +++++++++++++---
 .../ignite/internal/table/TupleBuilderImpl.java    | 171 +++++++++++++----
 .../org/apache/ignite/internal/table/Example.java  |   8 +-
 .../internal/table/TupleBuilderImplTest.java       | 133 +++++++++++++
 .../internal/table/impl/TestTupleBuilder.java      | 129 ++++++++++---
 16 files changed, 1100 insertions(+), 170 deletions(-)

diff --git a/modules/api/src/main/java/org/apache/ignite/table/Tuple.java b/modules/api/src/main/java/org/apache/ignite/table/Tuple.java
index 2b63d01..adc38e4 100644
--- a/modules/api/src/main/java/org/apache/ignite/table/Tuple.java
+++ b/modules/api/src/main/java/org/apache/ignite/table/Tuple.java
@@ -26,103 +26,215 @@ import org.apache.ignite.binary.BinaryObject;
  * <p>
  * Provides specialized method for some value-types to avoid boxing/unboxing.
  */
-public interface Tuple {
+public interface Tuple extends Iterable<Object> {
     /**
-     * Returns {@code true} if this tuple contains a column with the specified name.
+     * Gets the number of columns in this tuple.
      *
-     * @param colName Column name.
+     * @return Number of columns.
+     */
+    int columnCount();
+
+    /**
+     * Gets the name of the column with the specified index.
+     *
+     * @param columnIndex Column index.
+     * @return Column name.
+     */
+    String columnName(int columnIndex);
+
+    /**
+     * Gets the index of the column with the specified name.
+     *
+     * @param columnName Column name.
+     * @return Column index, or null when a column with given name is not present.
+     */
+    Integer columnIndex(String columnName);
+
+    /**
+     * Gets column value when a column with specified name is present in this tuple; returns default value otherwise.
+     *
+     * @param columnName Column name.
      * @param def Default value.
      * @param <T> Column default value type.
      * @return Column value if this tuple contains a column with the specified name. Otherwise returns {@code default}.
      */
-    <T> T valueOrDefault(String colName, T def);
+    <T> T valueOrDefault(String columnName, T def);
 
     /**
      * Gets column value for given column name.
      *
-     * @param colName Column name.
+     * @param columnName Column name.
+     * @param <T> Value type.
+     * @return Column value.
+     */
+    <T> T value(String columnName);
+
+    /**
+     * Gets column value for given column index.
+     *
+     * @param columnIndex Column index.
      * @param <T> Value type.
      * @return Column value.
      */
-    <T> T value(String colName);
+    <T> T value(int columnIndex);
 
     /**
      * Gets binary object column.
      *
-     * @param colName Column name.
+     * @param columnName Column name.
      * @return Column value.
      */
-    BinaryObject binaryObjectField(String colName);
+    BinaryObject binaryObjectValue(String columnName);
+
+    /**
+     * Gets binary object column.
+     *
+     * @param columnIndex Column index.
+     * @return Column value.
+     */
+    BinaryObject binaryObjectValue(int columnIndex);
+
+    /**
+     * Gets {@code byte} column value.
+     *
+     * @param columnName Column name.
+     * @return Column value.
+     */
+    byte byteValue(String columnName);
 
     /**
      * Gets {@code byte} column value.
      *
-     * @param colName Column name.
+     * @param columnIndex Column index.
      * @return Column value.
      */
-    byte byteValue(String colName);
+    byte byteValue(int columnIndex);
 
     /**
      * Gets {@code short} column value.
      *
-     * @param colName Column name.
+     * @param columnName Column name.
+     * @return Column value.
+     */
+    short shortValue(String columnName);
+
+    /**
+     * Gets {@code short} column value.
+     *
+     * @param columnIndex Column index.
+     * @return Column value.
+     */
+    short shortValue(int columnIndex);
+
+    /**
+     * Gets {@code int} column value.
+     *
+     * @param columnName Column name.
      * @return Column value.
      */
-    short shortValue(String colName);
+    int intValue(String columnName);
 
     /**
      * Gets {@code int} column value.
      *
-     * @param colName Column name.
+     * @param columnIndex Column index.
+     * @return Column value.
+     */
+    int intValue(int columnIndex);
+
+    /**
+     * Gets {@code long} column value.
+     *
+     * @param columnName Column name.
      * @return Column value.
      */
-    int intValue(String colName);
+    long longValue(String columnName);
 
     /**
      * Gets {@code long} column value.
      *
-     * @param colName Column name.
+     * @param columnIndex Column index.
+     * @return Column value.
+     */
+    long longValue(int columnIndex);
+
+    /**
+     * Gets {@code float} column value.
+     *
+     * @param columnName Column name.
      * @return Column value.
      */
-    long longValue(String colName);
+    float floatValue(String columnName);
 
     /**
      * Gets {@code float} column value.
      *
-     * @param colName Column name.
+     * @param columnIndex Column index.
      * @return Column value.
      */
-    float floatValue(String colName);
+    float floatValue(int columnIndex);
 
     /**
      * Gets {@code double} column value.
      *
-     * @param colName Column name.
+     * @param columnName Column name.
      * @return Column value.
      */
-    double doubleValue(String colName);
+    double doubleValue(String columnName);
+
+    /**
+     * Gets {@code double} column value.
+     *
+     * @param columnIndex Column index.
+     * @return Column value.
+     */
+    double doubleValue(int columnIndex);
+
+    /**
+     * Gets {@code String} column value.
+     *
+     * @param columnName Column name.
+     * @return Column value.
+     */
+    String stringValue(String columnName);
 
     /**
      * Gets {@code String} column value.
      *
-     * @param colName Column name.
+     * @param columnIndex Column index.
      * @return Column value.
      */
-    String stringValue(String colName);
+    String stringValue(int columnIndex);
 
     /**
      * Gets {@code UUID} column value.
      *
-     * @param colName Column name.
+     * @param columnName Column name.
+     * @return Column value.
+     */
+    UUID uuidValue(String columnName);
+
+    /**
+     * Gets {@code UUID} column value.
+     *
+     * @param columnIndex Column index.
+     * @return Column value.
+     */
+    UUID uuidValue(int columnIndex);
+
+    /**
+     * Gets {@code BitSet} column value.
+     *
+     * @param columnName Column name.
      * @return Column value.
      */
-    UUID uuidValue(String colName);
+    BitSet bitmaskValue(String columnName);
 
     /**
      * Gets {@code BitSet} column value.
      *
-     * @param colName Column name.
+     * @param columnIndex Column index.
      * @return Column value.
      */
-    BitSet bitmaskValue(String colName);
+    BitSet bitmaskValue(int columnIndex);
 }
diff --git a/modules/api/src/main/java/org/apache/ignite/table/TupleBuilder.java b/modules/api/src/main/java/org/apache/ignite/table/TupleBuilder.java
index db3e527..6ab4766 100644
--- a/modules/api/src/main/java/org/apache/ignite/table/TupleBuilder.java
+++ b/modules/api/src/main/java/org/apache/ignite/table/TupleBuilder.java
@@ -24,11 +24,11 @@ public interface TupleBuilder {
     /**
      * Sets column value.
      *
-     * @param colName Column name.
+     * @param columnName Column name.
      * @param value Value to set.
      * @return {@code this} for chaining.
      */
-    TupleBuilder set(String colName, Object value);
+    TupleBuilder set(String columnName, Object value);
 
     /**
      * Builds tuple.
diff --git a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientInboundMessageHandler.java b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientInboundMessageHandler.java
index be18616..1510691 100644
--- a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientInboundMessageHandler.java
+++ b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientInboundMessageHandler.java
@@ -41,10 +41,10 @@ import org.apache.ignite.internal.schema.SchemaAware;
 import org.apache.ignite.internal.schema.SchemaDescriptor;
 import org.apache.ignite.internal.table.IgniteTablesInternal;
 import org.apache.ignite.internal.table.TableImpl;
-import org.apache.ignite.internal.table.TupleBuilderImpl;
 import org.apache.ignite.lang.IgniteException;
 import org.apache.ignite.table.Table;
 import org.apache.ignite.table.Tuple;
+import org.apache.ignite.table.TupleBuilder;
 import org.msgpack.core.MessageFormat;
 import org.msgpack.core.buffer.ByteBufferInput;
 import org.slf4j.Logger;
@@ -340,7 +340,7 @@ public class ClientInboundMessageHandler extends ChannelInboundHandlerAdapter {
     private Tuple readTuple(ClientMessageUnpacker unpacker, TableImpl table, boolean keyOnly) throws IOException {
         var schemaId = unpacker.unpackInt();
         var schema = table.schemaView().schema(schemaId);
-        var builder = (TupleBuilderImpl) table.tupleBuilder();
+        var builder = table.tupleBuilder();
 
         var cnt = keyOnly ? schema.keyColumns().length() : schema.length();
 
@@ -375,7 +375,7 @@ public class ClientInboundMessageHandler extends ChannelInboundHandlerAdapter {
         return ((IgniteTablesInternal)ignite.tables()).table(tableId);
     }
 
-    private void readAndSetColumnValue(ClientMessageUnpacker unpacker, TupleBuilderImpl builder, Column col)
+    private void readAndSetColumnValue(ClientMessageUnpacker unpacker, TupleBuilder builder, Column col)
             throws IOException {
         builder.set(col.name(), unpacker.unpackObject(getClientDataType(col.type().spec())));
     }
diff --git a/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientSchema.java b/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientSchema.java
index d3b3a8c..c4bcb11 100644
--- a/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientSchema.java
+++ b/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientSchema.java
@@ -22,6 +22,7 @@ import java.util.Map;
 
 import org.apache.ignite.lang.IgniteException;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 /**
  * Client schema.
@@ -83,6 +84,7 @@ public class ClientSchema {
      *
      * @param name Column name.
      * @return Column by name.
+     * @throws IgniteException When a column with the specified name does not exist.
      */
     public @NotNull ClientColumn column(String name) {
         var column = map.get(name);
@@ -94,6 +96,16 @@ public class ClientSchema {
     }
 
     /**
+     * Gets a column by name.
+     *
+     * @param name Column name.
+     * @return Column by name.
+     */
+    public @Nullable ClientColumn columnSafe(String name) {
+        return map.get(name);
+    }
+
+    /**
      * @return Key column count.
      */
     public int keyColumnCount() {
diff --git a/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientTable.java b/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientTable.java
index c766206..071f26b 100644
--- a/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientTable.java
+++ b/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientTable.java
@@ -124,7 +124,7 @@ public class ClientTable implements Table {
 
     /** {@inheritDoc} */
     @Override public TupleBuilder tupleBuilder() {
-        return new ClientTupleBuilder();
+        return new ClientTupleBuilder(getLatestSchema().join());
     }
 
     /** {@inheritDoc} */
@@ -181,7 +181,7 @@ public class ClientTable implements Table {
 
     /** {@inheritDoc} */
     @Override public @NotNull CompletableFuture<Void> upsertAllAsync(@NotNull Collection<Tuple> recs) {
-        return null;
+        throw new UnsupportedOperationException();
     }
 
     /** {@inheritDoc} */
@@ -402,19 +402,17 @@ public class ClientTable implements Table {
     }
 
     private void writeTuple(@NotNull Tuple tuple, ClientSchema schema, PayloadOutputChannel w, boolean keyOnly) throws IOException {
-        // TODO: We should accept any Tuple implementation, but this requires extending the Tuple interface
-        // with methods to retrieve column list.
-        var rec = (ClientTupleBuilder) tuple;
-
         var vals = new Object[keyOnly ? schema.keyColumnCount() : schema.columns().length];
+        var tupleSize = tuple.columnCount();
 
-        for (var entry : rec.map().entrySet()) {
-            var col = schema.column(entry.getKey());
+        for (var i = 0; i < tupleSize; i++) {
+            var colName = tuple.columnName(i);
+            var col = schema.column(colName);
 
             if (keyOnly && !col.key())
                 continue;
 
-            vals[col.schemaIndex()] = entry.getValue();
+            vals[col.schemaIndex()] = tuple.value(i);
         }
 
         w.out().packUuid(id);
@@ -425,11 +423,11 @@ public class ClientTable implements Table {
     }
 
     private Tuple readTuple(ClientSchema schema, PayloadInputChannel r) {
-        var builder = new ClientTupleBuilder();
+        var builder = new ClientTupleBuilder(schema);
 
         try {
             for (var col : schema.columns())
-                builder.set(col.name(), r.in().unpackObject(col.type()));
+                builder.setInternal(col.schemaIndex(), r.in().unpackObject(col.type()));
         } catch (IOException e) {
             throw new CompletionException(e);
         }
diff --git a/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientTupleBuilder.java b/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientTupleBuilder.java
index 4aa7266..4079248 100644
--- a/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientTupleBuilder.java
+++ b/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientTupleBuilder.java
@@ -18,33 +18,46 @@
 package org.apache.ignite.internal.client.table;
 
 import java.util.BitSet;
-import java.util.HashMap;
+import java.util.Iterator;
 import java.util.UUID;
 
 import org.apache.ignite.binary.BinaryObject;
-import org.apache.ignite.lang.IgniteException;
 import org.apache.ignite.table.Tuple;
 import org.apache.ignite.table.TupleBuilder;
+import org.jetbrains.annotations.NotNull;
 
 /**
  * Client tuple builder.
  */
 public final class ClientTupleBuilder implements TupleBuilder, Tuple {
+    /** Null object to differentiate unset values and null values. */
+    private static final Object NULL_OBJ = new Object();
+
     /** Columns values. */
-    private final HashMap<String, Object> map = new HashMap<>();
+    private final Object[] vals;
+
+    /** Schema. */
+    private final ClientSchema schema;
 
     /**
-     * Gets the underlying map.
+     * Constructor.
      *
-     * @return Underlying map
+     * @param schema Schema.
      */
-    public HashMap<String, Object> map() {
-        return map;
+    public ClientTupleBuilder(ClientSchema schema) {
+        assert schema != null : "Schema can't be null.";
+        assert schema.columns().length > 0 : "Schema can't be empty.";
+
+        this.schema = schema;
+        this.vals = new Object[schema.columns().length];
     }
 
     /** {@inheritDoc} */
-    @Override public TupleBuilder set(String colName, Object value) {
-        map.put(colName, value);
+    @Override public TupleBuilder set(String columnName, Object value) {
+        // TODO: Live schema support IGNITE-15194
+        var col = schema.column(columnName);
+
+        vals[col.schemaIndex()] = value == null ? NULL_OBJ : value;
 
         return this;
     }
@@ -55,62 +68,192 @@ public final class ClientTupleBuilder implements TupleBuilder, Tuple {
     }
 
     /** {@inheritDoc} */
-    @Override public <T> T valueOrDefault(String colName, T def) {
-        return (T)map.getOrDefault(colName, def);
+    @Override public <T> T valueOrDefault(String columnName, T def) {
+        var col = schema.columnSafe(columnName);
+
+        if (col == null)
+            return def;
+
+        var val = (T)vals[col.schemaIndex()];
+
+        return val == null ? def : convertValue(val);
+    }
+
+    /** {@inheritDoc} */
+    @Override public <T> T value(String columnName) {
+        var col = schema.column(columnName);
+
+        return getValue(col.schemaIndex());
+    }
+
+    /** {@inheritDoc} */
+    @Override public <T> T value(int columnIndex) {
+        validateColumnIndex(columnIndex);
+
+        return getValue(columnIndex);
+    }
+
+    /** {@inheritDoc} */
+    @Override public int columnCount() {
+        return vals.length;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String columnName(int columnIndex) {
+        validateColumnIndex(columnIndex);
+
+        return schema.columns()[columnIndex].name();
+    }
+
+    /** {@inheritDoc} */
+    @Override public Integer columnIndex(String columnName) {
+        var col = schema.columnSafe(columnName);
+
+        return col == null ? null : col.schemaIndex();
+    }
+
+    /** {@inheritDoc} */
+    @Override public BinaryObject binaryObjectValue(String columnName) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override public BinaryObject binaryObjectValue(int columnIndex) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte byteValue(String columnName) {
+        return value(columnName);
     }
 
     /** {@inheritDoc} */
-    @Override public <T> T value(String colName) {
-        return (T)map.get(colName);
+    @Override public byte byteValue(int columnIndex) {
+        return value(columnIndex);
     }
 
     /** {@inheritDoc} */
-    @Override public BinaryObject binaryObjectField(String colName) {
-        throw new IgniteException("Not supported");
+    @Override public short shortValue(String columnName) {
+        return value(columnName);
     }
 
     /** {@inheritDoc} */
-    @Override public byte byteValue(String colName) {
-        return value(colName);
+    @Override public short shortValue(int columnIndex) {
+        return value(columnIndex);
     }
 
     /** {@inheritDoc} */
-    @Override public short shortValue(String colName) {
-        return value(colName);
+    @Override public int intValue(String columnName) {
+        return value(columnName);
     }
 
     /** {@inheritDoc} */
-    @Override public int intValue(String colName) {
-        return value(colName);
+    @Override public int intValue(int columnIndex) {
+        return value(columnIndex);
     }
 
     /** {@inheritDoc} */
-    @Override public long longValue(String colName) {
-        return value(colName);
+    @Override public long longValue(String columnName) {
+        return value(columnName);
     }
 
     /** {@inheritDoc} */
-    @Override public float floatValue(String colName) {
-        return value(colName);
+    @Override public long longValue(int columnIndex) {
+        return value(columnIndex);
     }
 
     /** {@inheritDoc} */
-    @Override public double doubleValue(String colName) {
-        return value(colName);
+    @Override public float floatValue(String columnName) {
+        return value(columnName);
     }
 
     /** {@inheritDoc} */
-    @Override public String stringValue(String colName) {
-        return value(colName);
+    @Override public float floatValue(int columnIndex) {
+        return value(columnIndex);
     }
 
     /** {@inheritDoc} */
-    @Override public UUID uuidValue(String colName) {
-        return value(colName);
+    @Override public double doubleValue(String columnName) {
+        return value(columnName);
     }
 
     /** {@inheritDoc} */
-    @Override public BitSet bitmaskValue(String colName) {
-        return value(colName);
+    @Override public double doubleValue(int columnIndex) {
+        return value(columnIndex);
+    }
+
+    /** {@inheritDoc} */
+    @Override public String stringValue(String columnName) {
+        return value(columnName);
+    }
+
+    /** {@inheritDoc} */
+    @Override public String stringValue(int columnIndex) {
+        return value(columnIndex);
+    }
+
+    /** {@inheritDoc} */
+    @Override public UUID uuidValue(String columnName) {
+        return value(columnName);
+    }
+
+    /** {@inheritDoc} */
+    @Override public UUID uuidValue(int columnIndex) {
+        return value(columnIndex);
+    }
+
+    /** {@inheritDoc} */
+    @Override public BitSet bitmaskValue(String columnName) {
+        return value(columnName);
+    }
+
+    /** {@inheritDoc} */
+    @Override public BitSet bitmaskValue(int columnIndex) {
+        return value(columnIndex);
+    }
+
+    /** {@inheritDoc} */
+    @NotNull @Override public Iterator<Object> iterator() {
+        return new Iterator<>() {
+            /** Current column index. */
+            private int cur;
+
+            /** {@inheritDoc} */
+            @Override public boolean hasNext() {
+                return cur < vals.length;
+            }
+
+            /** {@inheritDoc} */
+            @Override public Object next() {
+                return cur < vals.length ? vals[cur++] : null;
+            }
+        };
+    }
+
+    /**
+     * Sets column value by index.
+     *
+     * @param columnIndex Column index.
+     * @param value Value to set.
+     */
+    public void setInternal(int columnIndex, Object value) {
+        // Do not validate column index for internal needs.
+        vals[columnIndex] = value;
+    }
+
+    private void validateColumnIndex(int columnIndex) {
+        if (columnIndex < 0)
+            throw new IllegalArgumentException("Column index can't be negative");
+
+        if (columnIndex >= vals.length)
+            throw new IllegalArgumentException("Column index can't be greater than " + (vals.length - 1));
+    }
+
+    private <T> T getValue(int columnIndex) {
+        return convertValue((T)vals[columnIndex]);
+    }
+
+    private static <T> T convertValue(T val) {
+        return val == NULL_OBJ ? null : val;
     }
 }
diff --git a/modules/client/src/test/java/org/apache/ignite/client/AbstractClientTest.java b/modules/client/src/test/java/org/apache/ignite/client/AbstractClientTest.java
index 8177a61..f1a562f 100644
--- a/modules/client/src/test/java/org/apache/ignite/client/AbstractClientTest.java
+++ b/modules/client/src/test/java/org/apache/ignite/client/AbstractClientTest.java
@@ -102,9 +102,11 @@ public abstract class AbstractClientTest {
         var a = (ClientTupleBuilder) x;
         var b = (ClientTupleBuilder) y;
 
-        assertEquals(a.map().size(), b.map().size());
+        assertEquals(a.columnCount(), b.columnCount());
 
-        for (var kv : a.map().entrySet())
-            assertEquals(kv.getValue(), b.map().get(kv.getKey()), kv.getKey());
+        for (var i = 0; i < a.columnCount(); i++) {
+            assertEquals(a.columnName(i), b.columnName(i));
+            assertEquals((Object)a.value(i), b.value(i));
+        }
     }
 }
diff --git a/modules/client/src/test/java/org/apache/ignite/client/ClientTableTest.java b/modules/client/src/test/java/org/apache/ignite/client/ClientTableTest.java
index 1d4a39a..11e184a 100644
--- a/modules/client/src/test/java/org/apache/ignite/client/ClientTableTest.java
+++ b/modules/client/src/test/java/org/apache/ignite/client/ClientTableTest.java
@@ -24,6 +24,8 @@ import org.apache.ignite.table.Tuple;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
@@ -35,11 +37,12 @@ public class ClientTableTest extends AbstractClientTest {
     public void testGetWithNullInNotNullableKeyColumnThrowsException() {
         Table table = getDefaultTable();
 
-        var key = table.tupleBuilder().set("foo", "123").build();
+        var key = table.tupleBuilder().set("name", "123").build();
 
         var ex = assertThrows(CompletionException.class, () -> table.get(key));
 
-        assertTrue(ex.getMessage().contains("Column is not present in schema: foo"), ex.getMessage());
+        assertTrue(ex.getMessage().contains("Failed to set column (null was passed, but column is not nullable)"),
+                ex.getMessage());
     }
 
     @Test
@@ -58,6 +61,26 @@ public class ClientTableTest extends AbstractClientTest {
 
         assertEquals("John", resTuple.stringValue("name"));
         assertEquals(123L, resTuple.longValue("id"));
+        assertEquals("foo", resTuple.valueOrDefault("bar", "foo"));
+
+        assertEquals("John", resTuple.value(1));
+        assertEquals(123L, (Long) resTuple.value(0));
+
+        assertEquals(2, resTuple.columnCount());
+        assertEquals("id", resTuple.columnName(0));
+        assertEquals("name", resTuple.columnName(1));
+
+        var iter = tuple.iterator();
+
+        assertTrue(iter.hasNext());
+        assertEquals(123L, iter.next());
+
+        assertTrue(iter.hasNext());
+        assertEquals("John", iter.next());
+
+        assertFalse(iter.hasNext());
+        assertNull(iter.next());
+
         assertTupleEquals(tuple, resTuple);
     }
 
diff --git a/modules/client/src/test/java/org/apache/ignite/client/ClientTupleBuilderTest.java b/modules/client/src/test/java/org/apache/ignite/client/ClientTupleBuilderTest.java
new file mode 100644
index 0000000..592988f
--- /dev/null
+++ b/modules/client/src/test/java/org/apache/ignite/client/ClientTupleBuilderTest.java
@@ -0,0 +1,196 @@
+/*
+ * 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.client;
+
+import java.util.BitSet;
+import java.util.UUID;
+
+import org.apache.ignite.client.proto.ClientDataType;
+import org.apache.ignite.internal.client.table.ClientColumn;
+import org.apache.ignite.internal.client.table.ClientSchema;
+import org.apache.ignite.internal.client.table.ClientTupleBuilder;
+import org.apache.ignite.lang.IgniteException;
+import org.apache.ignite.table.Tuple;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * Tests client tuple builder implementation.
+ *
+ * Should be in sync with org.apache.ignite.internal.table.TupleBuilderImplTest.
+ */
+public class ClientTupleBuilderTest {
+    private static final ClientSchema SCHEMA = new ClientSchema(1, new ClientColumn[] {
+            new ClientColumn("id", ClientDataType.INT64, false, true, 0),
+            new ClientColumn("name", ClientDataType.STRING, false, false, 1)
+    });
+
+    @Test
+    public void testValueReturnsValueByName() {
+        assertEquals(3L, (Long) getTuple().value("id"));
+        assertEquals("Shirt", getTuple().value("name"));
+    }
+
+    @Test
+    public void testValueReturnsValueByIndex() {
+        assertEquals(3L, (Long) getTuple().value(0));
+        assertEquals("Shirt", getTuple().value(1));
+    }
+
+    @Test
+    public void testValueOrDefaultReturnsValueByName() {
+        assertEquals(3L, getTuple().valueOrDefault("id", -1L));
+        assertEquals("Shirt", getTuple().valueOrDefault("name", "y"));
+    }
+
+    @Test
+    public void testValueOrDefaultReturnsDefaultWhenColumnIsNotPresent() {
+        assertEquals("foo", getBuilder().valueOrDefault("x", "foo"));
+    }
+
+    @Test
+    public void testValueOrDefaultReturnsDefaultWhenColumnIsPresentButNotSet() {
+        assertEquals("foo", getBuilder().valueOrDefault("name", "foo"));
+    }
+
+    @Test
+    public void testValueOrDefaultReturnsNullWhenColumnIsSetToNull() {
+        var tuple = getBuilder().set("name", null).build();
+
+        assertNull(tuple.valueOrDefault("name", "foo"));
+    }
+
+    @Test
+    public void testEmptySchemaThrows() {
+        assertThrows(AssertionError.class, () -> new ClientTupleBuilder(new ClientSchema(1, new ClientColumn[0])));
+    }
+
+    @Test
+    public void testSetThrowsWhenColumnIsNotPresent() {
+        var ex = assertThrows(IgniteException.class, () -> getBuilder().set("x", "y"));
+        assertEquals("Column is not present in schema: x", ex.getMessage());
+    }
+
+    @Test
+    public void testValueThrowsWhenColumnIsNotPresent() {
+        var ex = assertThrows(IgniteException.class, () -> getBuilder().value("x"));
+        assertEquals("Column is not present in schema: x", ex.getMessage());
+
+        var ex2 = assertThrows(IllegalArgumentException.class, () -> getBuilder().value(100));
+        assertEquals("Column index can't be greater than 1", ex2.getMessage());
+    }
+
+    @Test
+    public void testColumnCountReturnsSchemaSize() {
+        assertEquals(SCHEMA.columns().length, getTuple().columnCount());
+    }
+
+    @Test
+    public void testColumnNameReturnsNameByIndex() {
+        assertEquals("id", getTuple().columnName(0));
+        assertEquals("name", getTuple().columnName(1));
+    }
+
+    @Test
+    public void testColumnNameThrowsOnInvalidIndex() {
+        var ex = assertThrows(IllegalArgumentException.class, () -> getTuple().columnName(-1));
+        assertEquals("Column index can't be negative", ex.getMessage());
+    }
+
+    @Test
+    public void testColumnIndexReturnsIndexByName() {
+        assertEquals(0, getTuple().columnIndex("id"));
+        assertEquals(1, getTuple().columnIndex("name"));
+    }
+
+    @Test
+    public void testColumnIndexReturnsNullForMissingColumns() {
+        assertNull(getTuple().columnIndex("foo"));
+    }
+
+    @Test
+    public void testTypedGetters() {
+        var schema = new ClientSchema(100, new ClientColumn[] {
+                new ClientColumn("i8", ClientDataType.INT8, false, false, 0),
+                new ClientColumn("i16", ClientDataType.INT16, false, false, 1),
+                new ClientColumn("i32", ClientDataType.INT32, false, false, 2),
+                new ClientColumn("i64", ClientDataType.INT64, false, false, 3),
+                new ClientColumn("float", ClientDataType.FLOAT, false, false, 4),
+                new ClientColumn("double", ClientDataType.DOUBLE, false, false, 5),
+                new ClientColumn("uuid", ClientDataType.UUID, false, false, 6),
+                new ClientColumn("str", ClientDataType.STRING, false, false, 7),
+                new ClientColumn("bits", ClientDataType.BITMASK, false, false, 8),
+        });
+
+        var uuid = UUID.randomUUID();
+
+        var builder = new ClientTupleBuilder(schema)
+                .set("i8", (byte)1)
+                .set("i16", (short)2)
+                .set("i32", (int)3)
+                .set("i64", (long)4)
+                .set("float", (float)5.5)
+                .set("double", (double)6.6)
+                .set("uuid", uuid)
+                .set("str", "8")
+                .set("bits", new BitSet(3));
+
+        var tuple = builder.build();
+
+        assertEquals(1, tuple.byteValue(0));
+        assertEquals(1, tuple.byteValue("i8"));
+
+        assertEquals(2, tuple.shortValue(1));
+        assertEquals(2, tuple.shortValue("i16"));
+
+        assertEquals(3, tuple.intValue(2));
+        assertEquals(3, tuple.intValue("i32"));
+
+        assertEquals(4, tuple.longValue(3));
+        assertEquals(4, tuple.longValue("i64"));
+
+        assertEquals(5.5, tuple.floatValue(4));
+        assertEquals(5.5, tuple.floatValue("float"));
+
+        assertEquals(6.6, tuple.doubleValue(5));
+        assertEquals(6.6, tuple.doubleValue("double"));
+
+        assertEquals(uuid, tuple.uuidValue(6));
+        assertEquals(uuid, tuple.uuidValue("uuid"));
+
+        assertEquals("8", tuple.stringValue(7));
+        assertEquals("8", tuple.stringValue("str"));
+
+        assertEquals(0, tuple.bitmaskValue(8).length());
+        assertEquals(0, tuple.bitmaskValue("bits").length());
+    }
+
+    private static ClientTupleBuilder getBuilder() {
+        return new ClientTupleBuilder(SCHEMA);
+    }
+
+    private static Tuple getTuple() {
+        return new ClientTupleBuilder(SCHEMA)
+                .set("id", 3L)
+                .set("name", "Shirt")
+                .build();
+    }
+}
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/SchemaDescriptor.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/SchemaDescriptor.java
index 35144bb..db18506 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/SchemaDescriptor.java
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/SchemaDescriptor.java
@@ -119,10 +119,25 @@ public class SchemaDescriptor implements Serializable {
      * @return Column instance.
      */
     public Column column(int colIdx) {
+        validateColumnIndex(colIdx);
+
         return colIdx < keyCols.length() ? keyCols.column(colIdx) : valCols.column(colIdx - keyCols.length());
     }
 
     /**
+     * Validates the column index.
+     *
+     * @param colIdx Column index.
+     */
+    public void validateColumnIndex(int colIdx) {
+        if (colIdx < 0)
+            throw new IllegalArgumentException("Column index can't be negative");
+
+        if (colIdx >= length())
+            throw new IllegalArgumentException("Column index can't be greater than " + (length() - 1));
+    }
+
+    /**
      * Gets columns names.
      *
      * @return Columns names.
diff --git a/modules/table/src/main/java/org/apache/ignite/internal/table/LiveSchemaTupleBuilderImpl.java b/modules/table/src/main/java/org/apache/ignite/internal/table/LiveSchemaTupleBuilderImpl.java
index 4f8ed1f..4cca5ff 100644
--- a/modules/table/src/main/java/org/apache/ignite/internal/table/LiveSchemaTupleBuilderImpl.java
+++ b/modules/table/src/main/java/org/apache/ignite/internal/table/LiveSchemaTupleBuilderImpl.java
@@ -69,8 +69,8 @@ public class LiveSchemaTupleBuilderImpl extends TupleBuilderImpl {
     }
 
     /** {@inheritDoc} */
-    @Override public TupleBuilder set(String colName, Object val) {
-        Column col = schema().column(colName);
+    @Override public TupleBuilder set(String columnName, Object val) {
+        Column col = schema().column(columnName);
 
         if (col == null) {
             if (val == null)
@@ -79,11 +79,11 @@ public class LiveSchemaTupleBuilderImpl extends TupleBuilderImpl {
             if (extraColumnsMap == null)
                 extraColumnsMap = new HashMap<>();
                 
-            extraColumnsMap.put(colName, val);
+            extraColumnsMap.put(columnName, val);
             
             return this;
         }
-        super.set(colName, val);
+        super.set(columnName, val);
 
         return this;
     }
diff --git a/modules/table/src/main/java/org/apache/ignite/internal/table/RowChunkAdapter.java b/modules/table/src/main/java/org/apache/ignite/internal/table/RowChunkAdapter.java
index dc57d67..d1cddd7 100644
--- a/modules/table/src/main/java/org/apache/ignite/internal/table/RowChunkAdapter.java
+++ b/modules/table/src/main/java/org/apache/ignite/internal/table/RowChunkAdapter.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.internal.table;
 
 import java.util.BitSet;
+import java.util.Iterator;
 import java.util.UUID;
 import org.apache.ignite.binary.BinaryObject;
 import org.apache.ignite.binary.BinaryObjects;
@@ -44,9 +45,26 @@ public abstract class RowChunkAdapter implements Tuple, SchemaAware {
     protected abstract Row row();
 
     /** {@inheritDoc} */
-    @Override public <T> T valueOrDefault(String colName, T def) {
+    @Override public int columnCount() {
+        return schema().length();
+    }
+
+    /** {@inheritDoc} */
+    @Override public String columnName(int columnIndex) {
+        return schema().column(columnIndex).name();
+    }
+
+    /** {@inheritDoc} */
+    @Override public Integer columnIndex(String columnName) {
+        var col = schema().column(columnName);
+
+        return col == null ? null : col.schemaIndex();
+    }
+
+    /** {@inheritDoc} */
+    @Override public <T> T valueOrDefault(String columnName, T def) {
         try {
-            return value(colName);
+            return value(columnName);
         }
         catch (ColumnNotFoundException ex) {
             return def;
@@ -54,79 +72,173 @@ public abstract class RowChunkAdapter implements Tuple, SchemaAware {
     }
 
     /** {@inheritDoc} */
-    @Override public <T> T value(String colName) {
-        final Column col = columnByName(colName);
+    @Override public <T> T value(String columnName) {
+        final Column col = columnByName(columnName);
+
+        return (T)col.type().spec().objectValue(row(), col.schemaIndex());
+    }
+
+    @Override public <T> T value(int columnIndex) {
+        final Column col = schema().column(columnIndex);
 
         return (T)col.type().spec().objectValue(row(), col.schemaIndex());
     }
 
     /** {@inheritDoc} */
-    @Override public BinaryObject binaryObjectField(String colName) {
-        Column col = columnByName(colName);
+    @Override public BinaryObject binaryObjectValue(String columnName) {
+        Column col = columnByName(columnName);
 
         return BinaryObjects.wrap(row().bytesValue(col.schemaIndex()));
     }
 
     /** {@inheritDoc} */
-    @Override public byte byteValue(String colName) {
-        Column col = columnByName(colName);
+    @Override public BinaryObject binaryObjectValue(int columnIndex) {
+        schema().validateColumnIndex(columnIndex);
+
+        return BinaryObjects.wrap(row().bytesValue(columnIndex));
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte byteValue(String columnName) {
+        Column col = columnByName(columnName);
 
         return row().byteValue(col.schemaIndex());
     }
 
     /** {@inheritDoc} */
-    @Override public short shortValue(String colName) {
-        Column col = columnByName(colName);
+    @Override public byte byteValue(int columnIndex) {
+        schema().validateColumnIndex(columnIndex);
+
+        return row().byteValue(columnIndex);
+    }
+
+    /** {@inheritDoc} */
+    @Override public short shortValue(String columnName) {
+        Column col = columnByName(columnName);
 
         return row().shortValue(col.schemaIndex());
     }
 
     /** {@inheritDoc} */
-    @Override public int intValue(String colName) {
-        Column col = columnByName(colName);
+    @Override public short shortValue(int columnIndex) {
+        schema().validateColumnIndex(columnIndex);
+
+        return row().shortValue(columnIndex);
+    }
+
+    /** {@inheritDoc} */
+    @Override public int intValue(String columnName) {
+        Column col = columnByName(columnName);
 
         return row().intValue(col.schemaIndex());
     }
 
     /** {@inheritDoc} */
-    @Override public long longValue(String colName) {
-        Column col = columnByName(colName);
+    @Override public int intValue(int columnIndex) {
+        schema().validateColumnIndex(columnIndex);
+
+        return row().intValue(columnIndex);
+    }
+
+    /** {@inheritDoc} */
+    @Override public long longValue(String columnName) {
+        Column col = columnByName(columnName);
 
         return row().longValue(col.schemaIndex());
     }
 
     /** {@inheritDoc} */
-    @Override public float floatValue(String colName) {
-        Column col = columnByName(colName);
+    @Override public long longValue(int columnIndex) {
+        schema().validateColumnIndex(columnIndex);
+
+        return row().longValue(columnIndex);
+    }
+
+    /** {@inheritDoc} */
+    @Override public float floatValue(String columnName) {
+        Column col = columnByName(columnName);
 
         return row().floatValue(col.schemaIndex());
     }
 
     /** {@inheritDoc} */
-    @Override public double doubleValue(String colName) {
-        Column col = columnByName(colName);
+    @Override public float floatValue(int columnIndex) {
+        schema().validateColumnIndex(columnIndex);
+
+        return row().floatValue(columnIndex);
+    }
+
+    /** {@inheritDoc} */
+    @Override public double doubleValue(String columnName) {
+        Column col = columnByName(columnName);
 
         return row().doubleValue(col.schemaIndex());
     }
 
     /** {@inheritDoc} */
-    @Override public String stringValue(String colName) {
-        Column col = columnByName(colName);
+    @Override public double doubleValue(int columnIndex) {
+        schema().validateColumnIndex(columnIndex);
+
+        return row().doubleValue(columnIndex);
+    }
+
+    /** {@inheritDoc} */
+    @Override public String stringValue(String columnName) {
+        Column col = columnByName(columnName);
 
         return row().stringValue(col.schemaIndex());
     }
 
     /** {@inheritDoc} */
-    @Override public UUID uuidValue(String colName) {
-        Column col = columnByName(colName);
+    @Override public String stringValue(int columnIndex) {
+        schema().validateColumnIndex(columnIndex);
+
+        return row().stringValue(columnIndex);
+    }
+
+    /** {@inheritDoc} */
+    @Override public UUID uuidValue(String columnName) {
+        Column col = columnByName(columnName);
 
         return row().uuidValue(col.schemaIndex());
     }
 
     /** {@inheritDoc} */
-    @Override public BitSet bitmaskValue(String colName) {
-        Column col = columnByName(colName);
+    @Override public UUID uuidValue(int columnIndex) {
+        schema().validateColumnIndex(columnIndex);
+
+        return row().uuidValue(columnIndex);
+    }
+
+    /** {@inheritDoc} */
+    @Override public BitSet bitmaskValue(String columnName) {
+        Column col = columnByName(columnName);
 
         return row().bitmaskValue(col.schemaIndex());
     }
+
+    /** {@inheritDoc} */
+    @Override public BitSet bitmaskValue(int columnIndex) {
+        schema().validateColumnIndex(columnIndex);
+
+        return row().bitmaskValue(columnIndex);
+    }
+
+    /** {@inheritDoc} */
+    @NotNull @Override public Iterator<Object> iterator() {
+        return new Iterator<>() {
+            /** Current column index. */
+            private int cur;
+
+            /** {@inheritDoc} */
+            @Override public boolean hasNext() {
+                return cur < schema().length();
+            }
+
+            /** {@inheritDoc} */
+            @Override public Object next() {
+                return hasNext() ? value(cur++) : null;
+            }
+        };
+    }
 }
diff --git a/modules/table/src/main/java/org/apache/ignite/internal/table/TupleBuilderImpl.java b/modules/table/src/main/java/org/apache/ignite/internal/table/TupleBuilderImpl.java
index 5f3fa08..6f5554d 100644
--- a/modules/table/src/main/java/org/apache/ignite/internal/table/TupleBuilderImpl.java
+++ b/modules/table/src/main/java/org/apache/ignite/internal/table/TupleBuilderImpl.java
@@ -19,6 +19,7 @@ package org.apache.ignite.internal.table;
 
 import java.util.BitSet;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.Map;
 import java.util.UUID;
 import java.util.Objects;
@@ -29,6 +30,7 @@ import org.apache.ignite.internal.schema.SchemaAware;
 import org.apache.ignite.internal.schema.SchemaDescriptor;
 import org.apache.ignite.table.Tuple;
 import org.apache.ignite.table.TupleBuilder;
+import org.jetbrains.annotations.NotNull;
 
 /**
  * Buildable tuple.
@@ -53,15 +55,10 @@ public class TupleBuilderImpl implements TupleBuilder, Tuple, SchemaAware {
     }
 
     /** {@inheritDoc} */
-    @Override public TupleBuilder set(String colName, Object val) {
-        Column col = schema().column(colName);
+    @Override public TupleBuilder set(String columnName, Object val) {
+        getColumnOrThrow(columnName).validate(val);
 
-        if (col == null)
-            throw new ColumnNotFoundException("Column not found [col=" + colName + "schema=" + schemaDesc + ']');
-
-        col.validate(val);
-
-        map.put(colName, val);
+        map.put(columnName, val);
 
         return this;
     }
@@ -89,65 +86,141 @@ public class TupleBuilderImpl implements TupleBuilder, Tuple, SchemaAware {
     }
 
     /** {@inheritDoc} */
-    @Override public <T> T valueOrDefault(String colName, T def) {
-        return (T)map.getOrDefault(colName, def);
+    @Override public String columnName(int columnIndex) {
+        return schemaDesc.column(columnIndex).name();
+    }
+
+    /** {@inheritDoc} */
+    @Override public Integer columnIndex(String columnName) {
+        var col = schemaDesc.column(columnName);
+
+        return col == null ? null : col.schemaIndex();
+    }
+
+    /** {@inheritDoc} */
+    @Override public int columnCount() {
+        return schemaDesc.length();
+    }
+
+    /** {@inheritDoc} */
+    @Override public <T> T valueOrDefault(String columnName, T def) {
+        return (T)map.getOrDefault(columnName, def);
     }
 
     /** {@inheritDoc} */
-    @Override public <T> T value(String colName) {
-        return (T)map.get(colName);
+    @Override public <T> T value(String columnName) {
+        getColumnOrThrow(columnName);
+
+        return (T)map.get(columnName);
     }
 
     /** {@inheritDoc} */
-    @Override public BinaryObject binaryObjectField(String colName) {
-        byte[] data = value(colName);
+    @Override public <T> T value(int columnIndex) {
+        Column col = schemaDesc.column(columnIndex);
+
+        return (T)map.get(col.name());
+    }
+
+    /** {@inheritDoc} */
+    @Override public BinaryObject binaryObjectValue(String columnName) {
+        byte[] data = value(columnName);
 
         return BinaryObjects.wrap(data);
     }
 
     /** {@inheritDoc} */
-    @Override public byte byteValue(String colName) {
-        return value(colName);
+    @Override public BinaryObject binaryObjectValue(int columnIndex) {
+        return value(columnIndex);
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte byteValue(String columnName) {
+        return value(columnName);
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte byteValue(int columnIndex) {
+        return value(columnIndex);
     }
 
     /** {@inheritDoc} */
-    @Override public short shortValue(String colName) {
-        return value(colName);
+    @Override public short shortValue(String columnName) {
+        return value(columnName);
     }
 
     /** {@inheritDoc} */
-    @Override public int intValue(String colName) {
-        return value(colName);
+    @Override public short shortValue(int columnIndex) {
+        return value(columnIndex);
     }
 
     /** {@inheritDoc} */
-    @Override public long longValue(String colName) {
-        return value(colName);
+    @Override public int intValue(String columnName) {
+        return value(columnName);
     }
 
     /** {@inheritDoc} */
-    @Override public float floatValue(String colName) {
-        return value(colName);
+    @Override public int intValue(int columnIndex) {
+        return value(columnIndex);
     }
 
     /** {@inheritDoc} */
-    @Override public double doubleValue(String colName) {
-        return value(colName);
+    @Override public long longValue(String columnName) {
+        return value(columnName);
     }
 
     /** {@inheritDoc} */
-    @Override public String stringValue(String colName) {
-        return value(colName);
+    @Override public long longValue(int columnIndex) {
+        return value(columnIndex);
     }
 
     /** {@inheritDoc} */
-    @Override public UUID uuidValue(String colName) {
-        return value(colName);
+    @Override public float floatValue(String columnName) {
+        return value(columnName);
     }
 
     /** {@inheritDoc} */
-    @Override public BitSet bitmaskValue(String colName) {
-        return value(colName);
+    @Override public float floatValue(int columnIndex) {
+        return value(columnIndex);
+    }
+
+    /** {@inheritDoc} */
+    @Override public double doubleValue(String columnName) {
+        return value(columnName);
+    }
+
+    /** {@inheritDoc} */
+    @Override public double doubleValue(int columnIndex) {
+        return value(columnIndex);
+    }
+
+    /** {@inheritDoc} */
+    @Override public String stringValue(String columnName) {
+        return value(columnName);
+    }
+
+    /** {@inheritDoc} */
+    @Override public String stringValue(int columnIndex) {
+        return value(columnIndex);
+    }
+
+    /** {@inheritDoc} */
+    @Override public UUID uuidValue(String columnName) {
+        return value(columnName);
+    }
+
+    /** {@inheritDoc} */
+    @Override public UUID uuidValue(int columnIndex) {
+        return value(columnIndex);
+    }
+
+    /** {@inheritDoc} */
+    @Override public BitSet bitmaskValue(String columnName) {
+        return value(columnName);
+    }
+
+    /** {@inheritDoc} */
+    @Override public BitSet bitmaskValue(int columnIndex) {
+        return value(columnIndex);
     }
 
     /** {@inheritDoc} */
@@ -155,10 +228,44 @@ public class TupleBuilderImpl implements TupleBuilder, Tuple, SchemaAware {
         return schemaDesc;
     }
 
+    /** {@inheritDoc} */
+    @NotNull @Override public Iterator<Object> iterator() {
+        return new Iterator<>() {
+            /** Current column index. */
+            private int cur;
+
+            /** {@inheritDoc} */
+            @Override public boolean hasNext() {
+                return cur < schemaDesc.length();
+            }
+
+            /** {@inheritDoc} */
+            @Override public Object next() {
+                return hasNext() ? value(cur++) : null;
+            }
+        };
+    }
+
     /**
      * @param schemaDesc New current schema descriptor.
      */
     protected void schema(SchemaDescriptor schemaDesc) {
         this.schemaDesc = schemaDesc;
     }
+
+    /**
+     * Gets column by name or throws an exception when not found.
+     *
+     * @param columnName Column name.
+     * @return Column.
+     * @throws ColumnNotFoundException when not found.
+     */
+    @NotNull private Column getColumnOrThrow(String columnName) {
+        Column col = schema().column(columnName);
+
+        if (col == null)
+            throw new ColumnNotFoundException("Column not found [col=" + columnName + "schema=" + schemaDesc + ']');
+
+        return col;
+    }
 }
diff --git a/modules/table/src/test/java/org/apache/ignite/internal/table/Example.java b/modules/table/src/test/java/org/apache/ignite/internal/table/Example.java
index 38dabb4..2f0ad09 100644
--- a/modules/table/src/test/java/org/apache/ignite/internal/table/Example.java
+++ b/modules/table/src/test/java/org/apache/ignite/internal/table/Example.java
@@ -258,7 +258,7 @@ public class Example {
         KeyValueView<OrderKey, OrderValue> orderKvView = t.kvView(Mappers.ofKeyClass(OrderKey.class),
             Mappers.ofValueClassBuilder(OrderValue.class)
                 .map("billingDetails", (row) -> {
-                    BinaryObject bObj = row.binaryObjectField("conditionalDetails");
+                    BinaryObject bObj = row.binaryObjectValue("conditionalDetails");
                     int type = row.intValue("type");
 
                     return type == 0 ?
@@ -276,7 +276,7 @@ public class Example {
         // Work with the binary object as in Ignite 2.x
 
         // Additionally, we may have a shortcut similar to primitive methods.
-        binObj = res.binaryObjectField("billingDetails");
+        binObj = res.binaryObjectValue("billingDetails");
 
         // Same with RecordAPI.
         class OrderRecord {
@@ -325,7 +325,7 @@ public class Example {
         // Work with the binary object as in Ignite 2.x
 
         // Additionally, we may have a shortcut similar to primitive methods.
-        binObj = res.binaryObjectField("upgradedObject");
+        binObj = res.binaryObjectValue("upgradedObject");
 
         // Plain byte[] and BinaryObject fields in a class are straightforward.
         class Record {
@@ -380,7 +380,7 @@ public class Example {
         RecordView<TruncatedRecord> truncatedView2 = t.recordView(
             Mappers.ofRecordClassBuilder(TruncatedRecord.class)
                 .map("upgradedObject", (row) -> {
-                    BinaryObject bObj = row.binaryObjectField("upgradedObject");
+                    BinaryObject bObj = row.binaryObjectValue("upgradedObject");
                     int dept = row.intValue("department");
 
                     return dept == 0 ?
diff --git a/modules/table/src/test/java/org/apache/ignite/internal/table/TupleBuilderImplTest.java b/modules/table/src/test/java/org/apache/ignite/internal/table/TupleBuilderImplTest.java
new file mode 100644
index 0000000..a387f2c
--- /dev/null
+++ b/modules/table/src/test/java/org/apache/ignite/internal/table/TupleBuilderImplTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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.table;
+
+import java.util.UUID;
+
+import org.apache.ignite.internal.schema.Column;
+import org.apache.ignite.internal.schema.NativeTypes;
+import org.apache.ignite.internal.schema.SchemaDescriptor;
+import org.apache.ignite.lang.IgniteException;
+import org.apache.ignite.table.Tuple;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Tests server tuple builder implementation.
+ *
+ * Should be in sync with org.apache.ignite.client.ClientTupleBuilderTest.
+ */
+public class TupleBuilderImplTest {
+    private static final SchemaDescriptor SCHEMA = new SchemaDescriptor(UUID.randomUUID(), 1,
+            new Column[] {new Column("id", NativeTypes.INT64, false)},
+            new Column[] {new Column("name", NativeTypes.STRING, true)
+    });
+
+    @Test
+    public void testValueReturnsValueByName() {
+        assertEquals(3L, (Long) getTuple().value("id"));
+        assertEquals("Shirt", getTuple().value("name"));
+    }
+
+    @Test
+    public void testValueReturnsValueByIndex() {
+        assertEquals(3L, (Long) getTuple().value(0));
+        assertEquals("Shirt", getTuple().value(1));
+    }
+
+    @Test
+    public void testValueOrDefaultReturnsValueByName() {
+        assertEquals(3L, getTuple().valueOrDefault("id", -1L));
+        assertEquals("Shirt", getTuple().valueOrDefault("name", "y"));
+    }
+
+    @Test
+    public void testValueOrDefaultReturnsDefaultWhenColumnIsNotPresent() {
+        assertEquals("foo", getBuilder().valueOrDefault("x", "foo"));
+    }
+
+    @Test
+    public void testValueOrDefaultReturnsDefaultWhenColumnIsPresentButNotSet() {
+        assertEquals("foo", getBuilder().valueOrDefault("name", "foo"));
+    }
+
+    @Test
+    public void testValueOrDefaultReturnsNullWhenColumnIsSetToNull() {
+        var tuple = getBuilder().set("name", null).build();
+
+        assertNull(tuple.valueOrDefault("name", "foo"));
+    }
+
+    @Test
+    public void testSetThrowsWhenColumnIsNotPresent() {
+        var ex = assertThrows(IgniteException.class, () -> getBuilder().set("x", "y"));
+        assertTrue(ex.getMessage().startsWith("Column not found"), ex.getMessage());
+    }
+
+    @Test
+    public void testValueThrowsWhenColumnIsNotPresent() {
+        var ex = assertThrows(IgniteException.class, () -> getBuilder().value("x"));
+        assertTrue(ex.getMessage().startsWith("Column not found"), ex.getMessage());
+
+        var ex2 = assertThrows(IllegalArgumentException.class, () -> getBuilder().value(100));
+        assertTrue(ex2.getMessage().startsWith("Column index can't be greater than 1"), ex2.getMessage());
+    }
+
+    @Test
+    public void testColumnCountReturnsSchemaSize() {
+        assertEquals(SCHEMA.length(), getTuple().columnCount());
+    }
+
+    @Test
+    public void testColumnNameReturnsNameByIndex() {
+        assertEquals("id", getTuple().columnName(0));
+        assertEquals("name", getTuple().columnName(1));
+    }
+
+    @Test
+    public void testColumnNameThrowsOnInvalidIndex() {
+        var ex = assertThrows(IllegalArgumentException.class, () -> getTuple().columnName(-1));
+        assertEquals("Column index can't be negative", ex.getMessage());
+    }
+
+    @Test
+    public void testColumnIndexReturnsIndexByName() {
+        assertEquals(0, getTuple().columnIndex("id"));
+        assertEquals(1, getTuple().columnIndex("name"));
+    }
+
+    @Test
+    public void testColumnIndexReturnsNullForMissingColumns() {
+        assertNull(getTuple().columnIndex("foo"));
+    }
+
+    private static TupleBuilderImpl getBuilder() {
+        return new TupleBuilderImpl(SCHEMA);
+    }
+
+    private static Tuple getTuple() {
+        return new TupleBuilderImpl(SCHEMA)
+                .set("id", 3L)
+                .set("name", "Shirt")
+                .build();
+    }
+}
diff --git a/modules/table/src/test/java/org/apache/ignite/internal/table/impl/TestTupleBuilder.java b/modules/table/src/test/java/org/apache/ignite/internal/table/impl/TestTupleBuilder.java
index f8520dc..440d7b7 100644
--- a/modules/table/src/test/java/org/apache/ignite/internal/table/impl/TestTupleBuilder.java
+++ b/modules/table/src/test/java/org/apache/ignite/internal/table/impl/TestTupleBuilder.java
@@ -19,12 +19,14 @@ package org.apache.ignite.internal.table.impl;
 
 import java.util.BitSet;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.Map;
 import java.util.UUID;
 import org.apache.ignite.binary.BinaryObject;
 import org.apache.ignite.binary.BinaryObjects;
 import org.apache.ignite.table.Tuple;
 import org.apache.ignite.table.TupleBuilder;
+import org.jetbrains.annotations.NotNull;
 
 /**
  * Dummy table storage implementation.
@@ -34,8 +36,8 @@ public class TestTupleBuilder implements TupleBuilder, Tuple {
     private final Map<String, Object> map = new HashMap<>();
 
     /** {@inheritDoc} */
-    @Override public TestTupleBuilder set(String colName, Object value) {
-        map.put(colName, value);
+    @Override public TestTupleBuilder set(String columnName, Object value) {
+        map.put(columnName, value);
 
         return this;
     }
@@ -46,64 +48,139 @@ public class TestTupleBuilder implements TupleBuilder, Tuple {
     }
 
     /** {@inheritDoc} */
-    @Override public <T> T valueOrDefault(String colName, T def) {
-        return (T)map.getOrDefault(colName, def);
+    @Override public <T> T valueOrDefault(String columnName, T def) {
+        return (T)map.getOrDefault(columnName, def);
     }
 
     /** {@inheritDoc} */
-    @Override public <T> T value(String colName) {
-        return (T)map.get(colName);
+    @Override public <T> T value(String columnName) {
+        return (T)map.get(columnName);
     }
 
     /** {@inheritDoc} */
-    @Override public BinaryObject binaryObjectField(String colName) {
-        byte[] data = value(colName);
+    @Override public <T> T value(int columnIndex) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override public int columnCount() {
+        return map.size();
+    }
+
+    /** {@inheritDoc} */
+    @Override public String columnName(int columnIndex) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override public Integer columnIndex(String columnName) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override public BinaryObject binaryObjectValue(String columnName) {
+        byte[] data = value(columnName);
 
         return BinaryObjects.wrap(data);
     }
 
     /** {@inheritDoc} */
-    @Override public byte byteValue(String colName) {
-        return value(colName);
+    @Override public BinaryObject binaryObjectValue(int columnIndex) {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte byteValue(String columnName) {
+        return value(columnName);
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte byteValue(int columnIndex) {
+        return 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public short shortValue(String columnName) {
+        return value(columnName);
+    }
+
+    /** {@inheritDoc} */
+    @Override public short shortValue(int columnIndex) {
+        return 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public int intValue(String columnName) {
+        return value(columnName);
+    }
+
+    /** {@inheritDoc} */
+    @Override public int intValue(int columnIndex) {
+        return 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long longValue(String columnName) {
+        return value(columnName);
+    }
+
+    /** {@inheritDoc} */
+    @Override public long longValue(int columnIndex) {
+        return 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public float floatValue(String columnName) {
+        return value(columnName);
+    }
+
+    /** {@inheritDoc} */
+    @Override public float floatValue(int columnIndex) {
+        return 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public double doubleValue(String columnName) {
+        return value(columnName);
     }
 
     /** {@inheritDoc} */
-    @Override public short shortValue(String colName) {
-        return value(colName);
+    @Override public double doubleValue(int columnIndex) {
+        return 0;
     }
 
     /** {@inheritDoc} */
-    @Override public int intValue(String colName) {
-        return value(colName);
+    @Override public String stringValue(String columnName) {
+        return value(columnName);
     }
 
     /** {@inheritDoc} */
-    @Override public long longValue(String colName) {
-        return value(colName);
+    @Override public String stringValue(int columnIndex) {
+        return null;
     }
 
     /** {@inheritDoc} */
-    @Override public float floatValue(String colName) {
-        return value(colName);
+    @Override public UUID uuidValue(String columnName) {
+        return value(columnName);
     }
 
     /** {@inheritDoc} */
-    @Override public double doubleValue(String colName) {
-        return value(colName);
+    @Override public UUID uuidValue(int columnIndex) {
+        return null;
     }
 
     /** {@inheritDoc} */
-    @Override public String stringValue(String colName) {
-        return value(colName);
+    @Override public BitSet bitmaskValue(String columnName) {
+        return value(columnName);
     }
 
     /** {@inheritDoc} */
-    @Override public UUID uuidValue(String colName) {
-        return value(colName);
+    @Override public BitSet bitmaskValue(int columnIndex) {
+        return null;
     }
 
     /** {@inheritDoc} */
-    @Override public BitSet bitmaskValue(String colName) {
-        return value(colName);
+    @NotNull @Override public Iterator<Object> iterator() {
+        throw new UnsupportedOperationException();
     }
 }