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

[ignite-3] branch main updated: IGNITE-14861 Live-schema. Upgrade schema when new column detected. (#212)

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

amashenkov 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 7ada2bb  IGNITE-14861 Live-schema. Upgrade schema when new column detected. (#212)
7ada2bb is described below

commit 7ada2bb40d038aea01614bfadae4a5e3454bb1ef
Author: Vladimir Ermakov <85...@users.noreply.github.com>
AuthorDate: Wed Jul 14 20:13:54 2021 +0300

    IGNITE-14861 Live-schema. Upgrade schema when new column detected. (#212)
---
 .../java/org/apache/ignite/schema/SchemaMode.java  |  31 ++
 .../runner/app/LiveSchemaChangeKVViewTest.java     | 220 ++++++++++++++
 .../runner/app/LiveSchemaChangeTableTest.java      | 325 +++++++++++++++++++++
 .../SchemaConfigurationConverter.java              |  48 +++
 .../ignite/distributed/ITDistributedTableTest.java |   3 +-
 .../ignite/internal/table/InternalTable.java       |  13 +
 .../ignite/internal/table/KVBinaryViewImpl.java    |  29 +-
 .../internal/table/LiveSchemaTupleBuilderImpl.java | 145 +++++++++
 .../apache/ignite/internal/table/TableImpl.java    |  26 +-
 .../ignite/internal/table/TupleBuilderImpl.java    |  19 +-
 .../internal/table/distributed/TableManager.java   |   2 +-
 .../distributed/storage/InternalTableImpl.java     |  16 +
 .../org/apache/ignite/internal/table/Example.java  |   2 +-
 .../internal/table/StrictSchemaOperationsTest.java |   8 +-
 .../table/TableBinaryViewOperationsTest.java       |  18 +-
 .../table/impl/DummyInternalTableImpl.java         |  10 +
 16 files changed, 889 insertions(+), 26 deletions(-)

diff --git a/modules/api/src/main/java/org/apache/ignite/schema/SchemaMode.java b/modules/api/src/main/java/org/apache/ignite/schema/SchemaMode.java
new file mode 100644
index 0000000..c7aab32
--- /dev/null
+++ b/modules/api/src/main/java/org/apache/ignite/schema/SchemaMode.java
@@ -0,0 +1,31 @@
+/*
+ * 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.schema;
+
+/**
+ * Schema mode.
+ *
+ * Defines the way inserting data will be validated against the schema and schema evolution capabilities.
+ */
+public enum SchemaMode {
+    /** Normal mode offers strong validation for the inserting data. Explicit schema changes only are allowed. */
+    STRICT_SCHEMA,
+
+    /** Extended mode that allows the schema to be fit the inserting data automatically. Only safe implicit schema changes are allowed, e.g. adding extra columns and widening column type. Changes like column removal or narrowing column type won't be applied implicitly. */
+    LIVE_SCHEMA
+}
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/LiveSchemaChangeKVViewTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/LiveSchemaChangeKVViewTest.java
new file mode 100644
index 0000000..d52301d
--- /dev/null
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/LiveSchemaChangeKVViewTest.java
@@ -0,0 +1,220 @@
+/*
+ * 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.runner.app;
+
+import java.util.List;
+import org.apache.ignite.app.Ignite;
+import org.apache.ignite.internal.schema.SchemaDescriptor;
+import org.apache.ignite.internal.table.ColumnNotFoundException;
+import org.apache.ignite.internal.table.TableImpl;
+import org.apache.ignite.internal.table.TupleBuilderImpl;
+import org.apache.ignite.schema.SchemaMode;
+import org.apache.ignite.table.KeyValueBinaryView;
+import org.apache.ignite.table.Table;
+import org.apache.ignite.table.Tuple;
+import org.apache.ignite.table.TupleBuilder;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Live schema tests for KV View.
+ */
+@Disabled("https://issues.apache.org/jira/browse/IGNITE-14581")
+class LiveSchemaChangeKVViewTest extends AbstractSchemaChangeTest {
+    /**
+     * Check exception for unknown column when STRICT_SCHEMA is enabled.
+     */
+    @Test
+    public void testStrictSchemaInsertRowOfNewSchema() {
+        List<Ignite> grid = startGrid();
+
+        createTable(grid);
+
+        KeyValueBinaryView view = grid.get(1).tables().table(TABLE).kvView();
+
+        assertThrows(ColumnNotFoundException.class, () -> view.tupleBuilder().set("key", 1L).set("unknownColumn", 10).build());
+    }
+
+    /**
+     * Check live schema kvBinaryView add columns.
+     */
+    @Test
+    public void testLiveSchemaAddColumns() {
+        List<Ignite> grid = startGrid();
+
+        createTable(grid);
+
+        Table tbl = grid.get(1).tables().table(TABLE);
+
+        ((TableImpl)tbl).schemaType(SchemaMode.LIVE_SCHEMA);
+
+        KeyValueBinaryView kvBinaryView = tbl.kvView();
+
+        Tuple key = kvBinaryView.tupleBuilder().set("key", 1L).build();
+        Tuple val = kvBinaryView.tupleBuilder().set("valStrNew", "111").set("valIntNew", 333).build();
+
+        kvBinaryView.put(key, val);
+
+        Tuple res = kvBinaryView.get(key);
+        assertEquals("111", res.value("valStrNew"));
+        assertEquals(Integer.valueOf(333), res.value("valIntNew"));
+    }
+
+    /**
+     * Check strict schema works correctly after live schema
+     */
+    @Test
+    public void testLiveSchemaAddColumnsSwitchToStrict() {
+        List<Ignite> grid = startGrid();
+
+        createTable(grid);
+
+        Table tbl = grid.get(1).tables().table(TABLE);
+
+        ((TableImpl)tbl).schemaType(SchemaMode.LIVE_SCHEMA);
+
+        KeyValueBinaryView kvBinaryView = tbl.kvView();
+
+        Tuple key = kvBinaryView.tupleBuilder().set("key", 1L).build();
+        Tuple val = kvBinaryView.tupleBuilder().set("valStrNew", "111").set("valIntNew", 333).build();
+
+        kvBinaryView.put(key, val);
+
+        Tuple res = kvBinaryView.get(key);
+        assertEquals("111", res.value("valStrNew"));
+        assertEquals(Integer.valueOf(333), res.value("valIntNew"));
+
+
+        ((TableImpl)tbl).schemaType(SchemaMode.STRICT_SCHEMA);
+
+        Tuple anotherKey = kvBinaryView.tupleBuilder().set("key", 2L).build();
+        Tuple anotherVal = kvBinaryView.tupleBuilder().set("valStrNew", "111").set("valIntNew", 333).build();
+
+        kvBinaryView.put(anotherKey, anotherVal);
+
+        Tuple newRes = kvBinaryView.get(anotherKey);
+
+        assertEquals("111", newRes.value("valStrNew"));
+        assertEquals(Integer.valueOf(333), newRes.value("valIntNew"));
+
+        assertThrows(ColumnNotFoundException.class, () -> kvBinaryView.tupleBuilder().set("key", 1L).set("unknownColumn", 10).build());
+    }
+
+    /**
+     * Check upsert row of old schema with row of new schema.
+     */
+    @Test
+    public void testLiveSchemaUpsertOldSchemaRow() {
+        List<Ignite> grid = startGrid();
+
+        createTable(grid);
+
+        Table tbl = grid.get(1).tables().table(TABLE);
+
+        ((TableImpl)tbl).schemaType(SchemaMode.LIVE_SCHEMA);
+
+        KeyValueBinaryView view = tbl.kvView();
+
+        Tuple oldSchemaKey = view.tupleBuilder().set("key", 32L).build();
+        Tuple oldSchemaVal = view.tupleBuilder().set("valInt", 111).set("valStr", "str").build();
+
+        view.put(oldSchemaKey, oldSchemaVal);
+
+        Tuple upsertOldSchemaVal = view.tupleBuilder().set("valStrNew", "111").set("valIntNew", 333).build();
+
+        view.put(oldSchemaKey, upsertOldSchemaVal);
+
+        Tuple oldSchemaRes = view.get(oldSchemaKey);
+
+        assertEquals("111", oldSchemaRes.value("valStrNew"));
+        assertEquals(Integer.valueOf(333), oldSchemaRes.value("valIntNew"));
+    }
+
+    /**
+     * Check upsert row of old schema with row of new schema.
+     */
+    @Test
+    public void testLiveSchemaUpsertSchemaTwice() {
+        List<Ignite> grid = startGrid();
+
+        createTable(grid);
+
+        Table tbl = grid.get(1).tables().table(TABLE);
+
+        ((TableImpl)tbl).schemaType(SchemaMode.LIVE_SCHEMA);
+
+        KeyValueBinaryView view = tbl.kvView();
+
+        Tuple oldSchemaKey = view.tupleBuilder().set("key", 32L).build();
+
+        Tuple oldSchemaVal = view.tupleBuilder().set("valInt", 111).set("valStr", "str").build();
+        Tuple upsertOldSchemaVal = view.tupleBuilder().set("valStrNew", "111").set("valIntNew", 333).build();
+        Tuple secondUpsertOldSchemaVal = view.tupleBuilder().set("valStrNew", "111").set("valIntNew", 333).set("anotherNewVal", 48L).build();
+
+        view.put(oldSchemaKey, oldSchemaVal);
+        view.put(oldSchemaKey, upsertOldSchemaVal);
+        view.put(oldSchemaKey, secondUpsertOldSchemaVal);
+
+        Tuple oldSchemaRes = view.get(oldSchemaKey);
+
+        assertEquals("111", oldSchemaRes.value("valStrNew"));
+        assertEquals(Integer.valueOf(333), oldSchemaRes.value("valIntNew"));
+        assertEquals(Long.valueOf(48L), oldSchemaRes.value("anotherNewVal"));
+    }
+
+    /**
+     * Check inserting row of old schema will not lead to column removal.
+     */
+    @Test
+    public void testLiveSchemaInsertOldSchemaRow() {
+        List<Ignite> grid = startGrid();
+
+        createTable(grid);
+
+        Table tbl = grid.get(1).tables().table(TABLE);
+
+        ((TableImpl)tbl).schemaType(SchemaMode.LIVE_SCHEMA);
+
+        KeyValueBinaryView view = tbl.kvView();
+
+        Tuple oldSchemaKey = view.tupleBuilder().set("key", 32L).build();
+        Tuple oldSchemaVal = view.tupleBuilder().set("valInt", 111).set("valStr", "str").build();
+
+        Tuple newSchemaKey = view.tupleBuilder().set("key", 1L).build();
+        Tuple newSchemaVal = view.tupleBuilder().set("valStrNew", "111").set("valIntNew", 333).build();
+
+        view.put(newSchemaKey, newSchemaVal);
+        view.put(oldSchemaKey, oldSchemaVal);
+
+        Tuple res = view.get(newSchemaKey);
+
+        assertEquals("111", res.value("valStrNew"));
+        assertEquals(Integer.valueOf(333), res.value("valIntNew"));
+
+        TupleBuilder newVerBuilder = tbl.tupleBuilder();
+
+        SchemaDescriptor schema = ((TupleBuilderImpl)newVerBuilder).schema();
+
+        assertTrue(schema.columnNames().contains("valStrNew"));
+        assertTrue(schema.columnNames().contains("valIntNew"));
+    }
+}
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/LiveSchemaChangeTableTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/LiveSchemaChangeTableTest.java
new file mode 100644
index 0000000..f421e4f
--- /dev/null
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/LiveSchemaChangeTableTest.java
@@ -0,0 +1,325 @@
+/*
+ * 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.runner.app;
+
+import java.util.List;
+import java.util.UUID;
+import org.apache.ignite.app.Ignite;
+import org.apache.ignite.internal.schema.SchemaDescriptor;
+import org.apache.ignite.schema.SchemaMode;
+import org.apache.ignite.internal.table.ColumnNotFoundException;
+import org.apache.ignite.internal.table.TableImpl;
+import org.apache.ignite.internal.table.TupleBuilderImpl;
+import org.apache.ignite.table.Table;
+import org.apache.ignite.table.Tuple;
+import org.apache.ignite.table.TupleBuilder;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+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;
+
+/**
+ * Live schema tests.
+ */
+@Disabled("https://issues.apache.org/jira/browse/IGNITE-14581")
+class LiveSchemaChangeTableTest extends AbstractSchemaChangeTest {
+    /**
+     * Check exception for unknown column when STRICT_SCHEMA is enabled.
+     */
+    @Test
+    public void testStrictSchemaInsertRowOfNewSchema() {
+        List<Ignite> grid = startGrid();
+
+        createTable(grid);
+
+        Table tbl = grid.get(1).tables().table(TABLE);
+
+        assertThrows(ColumnNotFoundException.class, () -> tbl.tupleBuilder().set("key", 1L).set("unknownColumn", 10).build());
+    }
+
+    /**
+     * Check insert row of new schema.
+     */
+    @Test
+    public void testLiveSchemaInsertRowOfNewSchema() {
+        List<Ignite> grid = startGrid();
+
+        createTable(grid);
+
+        Table tbl = grid.get(1).tables().table(TABLE);
+
+        ((TableImpl)tbl).schemaType(SchemaMode.LIVE_SCHEMA);
+
+        Tuple row = tbl.tupleBuilder().set("key", 1L).set("valStrNew", "111").set("valIntNew", 333).build();
+
+        tbl.insert(row);
+
+        Tuple res = tbl.get(row);
+
+        assertEquals("111", res.value("valStrNew"));
+        assertEquals(Integer.valueOf(333), res.value("valIntNew"));
+    }
+
+    /**
+     * Check upsert row of old schema with row of new schema.
+     */
+    @Test
+    public void testLiveSchemaUpsertOldSchemaRow() {
+        List<Ignite> grid = startGrid();
+
+        createTable(grid);
+
+        Table tbl = grid.get(1).tables().table(TABLE);
+
+        Tuple oldSchemaTuple = tbl.tupleBuilder().set("key", 32L).set("valInt", 111).set("valStr", "str").build();
+
+        tbl.insert(oldSchemaTuple);
+
+        ((TableImpl)tbl).schemaType(SchemaMode.LIVE_SCHEMA);
+
+        Tuple upsertOldSchemaTuple = tbl.tupleBuilder().set("key", 32L).set("valStrNew", "111").set("valIntNew", 333).build();
+
+        tbl.upsert(upsertOldSchemaTuple);
+
+        Tuple oldSchemaRes = tbl.get(upsertOldSchemaTuple);
+
+        assertEquals("111", oldSchemaRes.value("valStrNew"));
+        assertEquals(Integer.valueOf(333), oldSchemaRes.value("valIntNew"));
+    }
+
+    /**
+     * Check inserting row of old schema will not lead to column removal.
+     */
+    @Test
+    public void testLiveSchemaInsertOldSchemaRow() {
+        List<Ignite> grid = startGrid();
+
+        createTable(grid);
+
+        Table tbl = grid.get(1).tables().table(TABLE);
+
+        Tuple oldSchemaTuple = tbl.tupleBuilder().set("key", 32L).set("valInt", 111).set("valStr", "str").build();
+
+        ((TableImpl)tbl).schemaType(SchemaMode.LIVE_SCHEMA);
+
+        Tuple row = tbl.tupleBuilder().set("key", 1L).set("valStrNew", "111").set("valIntNew", 333).build();
+
+        tbl.insert(row);
+        tbl.insert(oldSchemaTuple);
+
+        Tuple res = tbl.get(row);
+
+        assertEquals("111", res.value("valStrNew"));
+        assertEquals(Integer.valueOf(333), res.value("valIntNew"));
+
+        TupleBuilder newVerBuilder = tbl.tupleBuilder();
+
+        SchemaDescriptor schema = ((TupleBuilderImpl)newVerBuilder).schema();
+
+        assertTrue(schema.columnNames().contains("valStrNew"));
+        assertTrue(schema.columnNames().contains("valIntNew"));
+    }
+
+    /**
+     * Check strict schema works correctly after live schema
+     */
+    @Test
+    public void testLiveSchemaAddColumnsSwitchToStrict() {
+        List<Ignite> grid = startGrid();
+
+        createTable(grid);
+
+        Table tbl = grid.get(1).tables().table(TABLE);
+
+        ((TableImpl)tbl).schemaType(SchemaMode.LIVE_SCHEMA);
+
+        Tuple val = tbl.tupleBuilder().set("key", 1L).set("valStrNew", "111").set("valIntNew", 333).build();
+
+        tbl.insert(val);
+
+        Tuple res = tbl.get(val);
+        assertEquals("111", res.value("valStrNew"));
+        assertEquals(Integer.valueOf(333), res.value("valIntNew"));
+
+
+        ((TableImpl)tbl).schemaType(SchemaMode.STRICT_SCHEMA);
+
+        Tuple anotherKey = tbl.tupleBuilder().set("key", 2L).set("valStrNew", "111").set("valIntNew", 333).build();
+
+        tbl.insert(anotherKey);
+
+        Tuple newRes = tbl.get(anotherKey);
+
+        assertEquals("111", newRes.value("valStrNew"));
+        assertEquals(Integer.valueOf(333), newRes.value("valIntNew"));
+
+        assertThrows(ColumnNotFoundException.class, () -> tbl.tupleBuilder().set("key", 1L).set("unknownColumn", 10).build());
+    }
+
+    /**
+     * Check upsert row of old schema with row of new schema.
+     */
+    @Test
+    public void testLiveSchemaUpsertSchemaTwice() {
+        List<Ignite> grid = startGrid();
+
+        createTable(grid);
+
+        Table tbl = grid.get(1).tables().table(TABLE);
+
+        ((TableImpl)tbl).schemaType(SchemaMode.LIVE_SCHEMA);
+
+        Tuple oldSchemaVal = tbl.tupleBuilder().set("key", 32L).set("valInt", 111).set("valStr", "str").build();
+        Tuple upsertOldSchemaVal = tbl.tupleBuilder().set("key", 32L).set("valStrNew", "111").set("valIntNew", 333).build();
+        Tuple secondUpsertOldSchemaVal = tbl.tupleBuilder().set("key", 32L).set("valStrNew", "111").set("valIntNew", 333).set("anotherNewVal", 48L).build();
+
+        tbl.insert(oldSchemaVal);
+        tbl.upsert(upsertOldSchemaVal);
+        tbl.upsert(secondUpsertOldSchemaVal);
+
+        Tuple oldSchemaRes = tbl.get(secondUpsertOldSchemaVal);
+
+        assertEquals("111", oldSchemaRes.value("valStrNew"));
+        assertEquals(Integer.valueOf(333), oldSchemaRes.value("valIntNew"));
+        assertEquals(Long.valueOf(48L), oldSchemaRes.value("anotherNewVal"));
+    }
+
+    /**
+     * Check live schema tuple can handle different value types.
+     */
+    @Test
+    public void testLiveSchemaDifferentColumnTypes() {
+        List<Ignite> grid = startGrid();
+
+        createTable(grid);
+
+        Table tbl = grid.get(1).tables().table(TABLE);
+
+        ((TableImpl)tbl).schemaType(SchemaMode.LIVE_SCHEMA);
+
+        UUID uuid = UUID.randomUUID();
+
+        Tuple row = tbl.tupleBuilder()
+            .set("key", 1L)
+            .set("valByteNew", (byte)10)
+            .set("valShortNew", (short)48)
+            .set("valIntNew", 333)
+            .set("valLongNew", 55L)
+            .set("valFloatNew", 32.23f)
+            .set("valDoubleNew", 100.101d)
+            .set("valStrNew", "111")
+            .set("valUUIDNew", uuid)
+            .build();
+
+        tbl.insert(row);
+
+        Tuple res = tbl.get(row);
+
+        assertEquals(Byte.valueOf((byte)10), res.value("valByteNew"));
+        assertEquals(Short.valueOf((short)48), res.value("valShortNew"));
+        assertEquals(Integer.valueOf(333), res.value("valIntNew"));
+        assertEquals(Long.valueOf(55L), res.value("valLongNew"));
+        assertEquals(32.23f, res.value("valFloatNew"), 0.001f);
+        assertEquals(100.101d, res.value("valDoubleNew"), 0.001f);
+
+        assertEquals("111", res.value("valStrNew"));
+        assertEquals(uuid, res.value("valUUIDNew"));
+
+        Tuple secondRow = tbl.tupleBuilder().set("key", 2L).build();
+
+        tbl.insert(secondRow);
+
+        Tuple nullRes = tbl.get(secondRow);
+
+        assertNull(nullRes.value("valByteNew"));
+        assertNull(nullRes.value("valShortNew"));
+        assertNull(nullRes.value("valIntNew"));
+        assertNull(nullRes.value("valLongNew"));
+        assertNull(nullRes.value("valFloatNew"));
+        assertNull(nullRes.value("valDoubleNew"));
+        assertNull(nullRes.value("valUUIDNew"));
+
+        assertEquals("", nullRes.value("valStrNew"));
+    }
+
+    /**
+     * Check live schema tuple update schema only once.
+     */
+    @Test
+    public void testLiveSchemaBuilderUpdateSchemaOnlyOnce() {
+        List<Ignite> grid = startGrid();
+
+        createTable(grid);
+
+        Table tbl = grid.get(1).tables().table(TABLE);
+
+        ((TableImpl)tbl).schemaType(SchemaMode.LIVE_SCHEMA);
+
+        UUID uuid = UUID.randomUUID();
+
+        Tuple row = tbl.tupleBuilder()
+            .set("key", 1L)
+            .set("valByteNew", (byte)10)
+            .set("valShortNew", (short)48)
+            .set("valIntNew", 333)
+            .set("valLongNew", 55L)
+            .set("valFloatNew", 32.23f)
+            .set("valDoubleNew", 100.101d)
+            .set("valStrNew", "111")
+            .set("valUUIDNew", uuid)
+            .build();
+
+        tbl.insert(row);
+
+        TupleBuilder newVerBuilder = tbl.tupleBuilder();
+
+        SchemaDescriptor schema = ((TupleBuilderImpl)newVerBuilder).schema();
+
+        assertEquals(2, schema.version());
+    }
+
+    /**
+     * Check live schema tuple can handle unsupported values and null`s correctly.
+     */
+    @Test
+    public void testLiveSchemaNullAndUnsupportedTypes() {
+        List<Ignite> grid = startGrid();
+
+        createTable(grid);
+
+        Table tbl = grid.get(1).tables().table(TABLE);
+
+        ((TableImpl)tbl).schemaType(SchemaMode.LIVE_SCHEMA);
+
+        assertDoesNotThrow(() -> tbl.tupleBuilder().set("newBrokenColumn", new Object()));
+        assertThrows(UnsupportedOperationException.class, () -> tbl.tupleBuilder().set("newBrokenColumn", new Object()).build());
+
+        Tuple row = tbl.tupleBuilder().set("key", 1L).set("valStrNew", null).set("valIntNew", 333).build();
+
+        tbl.insert(row);
+
+        Tuple res = tbl.get(tbl.tupleBuilder().set("key", 1L).build());
+
+        assertThrows(ColumnNotFoundException.class, () -> res.value("valStrNew"));
+        assertEquals(Integer.valueOf(333), res.value("valIntNew"));
+    }
+}
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/configuration/SchemaConfigurationConverter.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/configuration/SchemaConfigurationConverter.java
index 60a1b7f..81958c0 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/configuration/SchemaConfigurationConverter.java
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/configuration/SchemaConfigurationConverter.java
@@ -24,6 +24,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.SortedMap;
 import java.util.TreeMap;
+import java.util.UUID;
 import org.apache.ignite.configuration.NamedListView;
 import org.apache.ignite.configuration.schemas.table.ColumnChange;
 import org.apache.ignite.configuration.schemas.table.ColumnTypeChange;
@@ -512,4 +513,51 @@ public class SchemaConfigurationConverter {
     public static TableChange dropColumn(String columnName, TableChange tblChange) {
         return tblChange.changeColumns(colChg -> colChg.delete(columnName));
     }
+
+    /**
+     * Gets ColumnType type for given class.
+     *
+     * @param cls Class.
+     * @return ColumnType type or null.
+     */
+    public static ColumnType columnType(Class<?> cls) {
+        assert cls != null;
+
+        // Primitives.
+        if (cls == byte.class)
+            return ColumnType.INT8;
+        else if (cls == short.class)
+            return ColumnType.INT16;
+        else if (cls == int.class)
+            return ColumnType.INT32;
+        else if (cls == long.class)
+            return ColumnType.INT64;
+        else if (cls == float.class)
+            return ColumnType.FLOAT;
+        else if (cls == double.class)
+            return ColumnType.DOUBLE;
+
+            // Boxed primitives.
+        else if (cls == Byte.class)
+            return ColumnType.INT8;
+        else if (cls == Short.class)
+            return ColumnType.INT16;
+        else if (cls == Integer.class)
+            return ColumnType.INT32;
+        else if (cls == Long.class)
+            return ColumnType.INT64;
+        else if (cls == Float.class)
+            return ColumnType.FLOAT;
+        else if (cls == Double.class)
+            return ColumnType.DOUBLE;
+
+            // Other types
+        else if (cls == String.class)
+            return ColumnType.string();
+        else if (cls == UUID.class)
+            return ColumnType.UUID;
+
+        return null;
+    }
+
 }
diff --git a/modules/table/src/integrationTest/java/org/apache/ignite/distributed/ITDistributedTableTest.java b/modules/table/src/integrationTest/java/org/apache/ignite/distributed/ITDistributedTableTest.java
index 9d3d79d..edb54be 100644
--- a/modules/table/src/integrationTest/java/org/apache/ignite/distributed/ITDistributedTableTest.java
+++ b/modules/table/src/integrationTest/java/org/apache/ignite/distributed/ITDistributedTableTest.java
@@ -265,7 +265,8 @@ public class ITDistributedTableTest {
             @Override public Row resolve(BinaryRow row) {
                 return new Row(SCHEMA, row);
             }
-        });
+        },
+            null);
 
         partitionedTableView(tbl, PARTS * 10);
 
diff --git a/modules/table/src/main/java/org/apache/ignite/internal/table/InternalTable.java b/modules/table/src/main/java/org/apache/ignite/internal/table/InternalTable.java
index 619fb67..05c8cce 100644
--- a/modules/table/src/main/java/org/apache/ignite/internal/table/InternalTable.java
+++ b/modules/table/src/main/java/org/apache/ignite/internal/table/InternalTable.java
@@ -21,6 +21,7 @@ import java.util.Collection;
 import java.util.UUID;
 import java.util.concurrent.CompletableFuture;
 import org.apache.ignite.internal.schema.BinaryRow;
+import org.apache.ignite.schema.SchemaMode;
 import org.jetbrains.annotations.NotNull;
 
 /**
@@ -43,6 +44,18 @@ public interface InternalTable {
     @NotNull String tableName();
 
     /**
+     * Gets a schema mode of the table.
+     *
+     * @return Schema mode.
+     */
+    @NotNull SchemaMode schemaMode();
+
+    /**
+     * Sets schema mode for the table.
+     */
+    void schema(SchemaMode schemaMode);
+
+    /**
      * Asynchronously gets a row with same key columns values as given one from the table.
      *
      * @param keyRow Row with key columns set.
diff --git a/modules/table/src/main/java/org/apache/ignite/internal/table/KVBinaryViewImpl.java b/modules/table/src/main/java/org/apache/ignite/internal/table/KVBinaryViewImpl.java
index 2ba0bbe..0ecb8a1 100644
--- a/modules/table/src/main/java/org/apache/ignite/internal/table/KVBinaryViewImpl.java
+++ b/modules/table/src/main/java/org/apache/ignite/internal/table/KVBinaryViewImpl.java
@@ -27,6 +27,7 @@ import org.apache.ignite.internal.schema.BinaryRow;
 import org.apache.ignite.internal.schema.Row;
 import org.apache.ignite.internal.schema.marshaller.TupleMarshaller;
 import org.apache.ignite.internal.schema.SchemaRegistry;
+import org.apache.ignite.internal.table.distributed.TableManager;
 import org.apache.ignite.table.InvokeProcessor;
 import org.apache.ignite.table.KeyValueBinaryView;
 import org.apache.ignite.table.Tuple;
@@ -40,6 +41,9 @@ public class KVBinaryViewImpl extends AbstractTableView implements KeyValueBinar
     /** Marshaller. */
     private final TupleMarshallerImpl marsh;
 
+    /** Table manager. */
+    private final TableManager tblMgr;
+
     /**
      * Constructor.
      *
@@ -50,6 +54,22 @@ public class KVBinaryViewImpl extends AbstractTableView implements KeyValueBinar
         super(tbl, schemaReg);
 
         marsh = new TupleMarshallerImpl(schemaReg);
+
+        tblMgr = null;
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param tbl Table storage.
+     * @param schemaReg Schema registry.
+     */
+    public KVBinaryViewImpl(InternalTable tbl, SchemaRegistry schemaReg, TableManager tblMgr) {
+        super(tbl, schemaReg);
+
+        marsh = new TupleMarshallerImpl(schemaReg);
+
+        this.tblMgr = tblMgr;
     }
 
     /** {@inheritDoc} */
@@ -288,7 +308,14 @@ public class KVBinaryViewImpl extends AbstractTableView implements KeyValueBinar
 
     /** {@inheritDoc} */
     @Override public TupleBuilder tupleBuilder() {
-        return new TupleBuilderImpl(schemaReg.schema());
+        switch (tbl.schemaMode()) {
+            case STRICT_SCHEMA:
+                return new TupleBuilderImpl(schemaReg.schema());
+            case LIVE_SCHEMA:
+                return new LiveSchemaTupleBuilderImpl(schemaReg, tbl.tableName(), tblMgr);
+        }
+        
+        throw new IllegalArgumentException("Unknown schema type: " + tbl.schemaMode());
     }
 
     /**
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
new file mode 100644
index 0000000..4f8ed1f
--- /dev/null
+++ b/modules/table/src/main/java/org/apache/ignite/internal/table/LiveSchemaTupleBuilderImpl.java
@@ -0,0 +1,145 @@
+/*
+ * 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.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import org.apache.ignite.internal.schema.Column;
+import org.apache.ignite.internal.schema.SchemaRegistry;
+import org.apache.ignite.internal.schema.configuration.SchemaConfigurationConverter;
+import org.apache.ignite.internal.table.distributed.TableManager;
+import org.apache.ignite.schema.ColumnType;
+import org.apache.ignite.schema.SchemaBuilders;
+import org.apache.ignite.table.Tuple;
+import org.apache.ignite.table.TupleBuilder;
+
+import static org.apache.ignite.internal.schema.configuration.SchemaConfigurationConverter.convert;
+
+/**
+ * Live schema buildable tuple.
+ *
+ * Allows to create columns implicitly by adding previously nonexistent columns during insert.
+ */
+public class LiveSchemaTupleBuilderImpl extends TupleBuilderImpl {
+    /** Live schema column values. */
+    private Map<String, Object> extraColumnsMap;
+
+    /** Schema registry. */
+    private final SchemaRegistry schemaRegistry;
+
+    /** Current table name. */
+    private final String tblName;
+
+    /** Table manager. */
+    private final TableManager mgr;
+
+    /**
+     * Constructor.
+     */
+    public LiveSchemaTupleBuilderImpl(SchemaRegistry schemaRegistry, String tblName, TableManager mgr) {
+        super(schemaRegistry == null ? null : schemaRegistry.schema());
+
+        Objects.requireNonNull(schemaRegistry);
+        Objects.requireNonNull(tblName);
+        Objects.requireNonNull(mgr);
+
+        this.schemaRegistry = schemaRegistry;
+        this.tblName = tblName;
+        this.mgr = mgr;
+
+        extraColumnsMap = null;
+    }
+
+    /** {@inheritDoc} */
+    @Override public TupleBuilder set(String colName, Object val) {
+        Column col = schema().column(colName);
+
+        if (col == null) {
+            if (val == null)
+                return this;
+
+            if (extraColumnsMap == null)
+                extraColumnsMap = new HashMap<>();
+                
+            extraColumnsMap.put(colName, val);
+            
+            return this;
+        }
+        super.set(colName, val);
+
+        return this;
+    }
+
+    /** {@inheritDoc} */
+    @Override public Tuple build() {
+        if (extraColumnsMap == null)
+            return this;
+
+        while (!extraColumnsMap.isEmpty()) {
+            createColumns(extraColumnsMap);
+
+            this.schema(schemaRegistry.schema());
+
+            Map<String, Object> colMap = map;
+            map = new HashMap<>();
+
+            extraColumnsMap.forEach(colMap::put);
+            extraColumnsMap.clear();
+
+            colMap.forEach(this::set);
+        }
+
+        return this;
+    }
+
+    /**
+     * Updates the schema, creates new columns.
+     * @param extraCols - map with column names and column values.
+     */
+    private void createColumns(Map<String, Object> extraCols) {
+        Map<String, ColumnType> colTypeMap = new HashMap<>();
+        
+        for (Map.Entry<String, Object> entry : extraCols.entrySet()) {
+            String colName = entry.getKey();
+            Object val = entry.getValue();
+
+            ColumnType type = SchemaConfigurationConverter.columnType(val.getClass());
+
+            if (type == null)
+                throw new UnsupportedOperationException("Live schema update for type [" + val.getClass() + "] is not supported yet.");
+
+            colTypeMap.put(colName, type);
+        }
+        List<org.apache.ignite.schema.Column> newCols = colTypeMap.entrySet().stream()
+            .map(entry -> SchemaBuilders.column(entry.getKey(), entry.getValue()).asNullable().build())
+            .collect(Collectors.toList());
+
+        mgr.alterTable(tblName, chng -> chng.changeColumns(cols -> {
+            int colIdx = chng.columns().size();
+            //TODO: avoid 'colIdx' or replace with correct last colIdx.
+
+            for (org.apache.ignite.schema.Column column : newCols) {
+                cols.create(String.valueOf(colIdx), colChg -> convert(column, colChg));
+                colIdx++;
+            }
+        }));
+    }
+}
diff --git a/modules/table/src/main/java/org/apache/ignite/internal/table/TableImpl.java b/modules/table/src/main/java/org/apache/ignite/internal/table/TableImpl.java
index d4bdc23..960e420 100644
--- a/modules/table/src/main/java/org/apache/ignite/internal/table/TableImpl.java
+++ b/modules/table/src/main/java/org/apache/ignite/internal/table/TableImpl.java
@@ -28,7 +28,9 @@ import java.util.stream.Collectors;
 import org.apache.ignite.internal.schema.BinaryRow;
 import org.apache.ignite.internal.schema.Row;
 import org.apache.ignite.internal.schema.SchemaRegistry;
+import org.apache.ignite.schema.SchemaMode;
 import org.apache.ignite.internal.schema.marshaller.TupleMarshaller;
+import org.apache.ignite.internal.table.distributed.TableManager;
 import org.apache.ignite.table.InvokeProcessor;
 import org.apache.ignite.table.KeyValueBinaryView;
 import org.apache.ignite.table.KeyValueView;
@@ -48,16 +50,21 @@ public class TableImpl extends AbstractTableView implements Table {
     /** Marshaller. */
     private final TupleMarshallerImpl marsh;
 
+    /** Table manager. */
+    private final TableManager tblMgr;
+
     /**
      * Constructor.
      *
      * @param tbl Table.
      * @param schemaReg Table schema registry.
      */
-    public TableImpl(InternalTable tbl, SchemaRegistry schemaReg) {
+    public TableImpl(InternalTable tbl, SchemaRegistry schemaReg, TableManager tblMgr) {
         super(tbl, schemaReg);
 
         marsh = new TupleMarshallerImpl(schemaReg);
+
+        this.tblMgr = tblMgr;
     }
 
     /**
@@ -95,7 +102,7 @@ public class TableImpl extends AbstractTableView implements Table {
 
     /** {@inheritDoc} */
     @Override public KeyValueBinaryView kvView() {
-        return new KVBinaryViewImpl(tbl, schemaReg);
+        return new KVBinaryViewImpl(tbl, schemaReg, tblMgr);
     }
 
     /** {@inheritDoc} */
@@ -376,7 +383,13 @@ public class TableImpl extends AbstractTableView implements Table {
 
     /** {@inheritDoc} */
     @Override public TupleBuilder tupleBuilder() {
-        return new TupleBuilderImpl(schemaReg.schema());
+        switch (tbl.schemaMode()) {
+            case STRICT_SCHEMA:
+                return new TupleBuilderImpl(schemaReg.schema());
+            case LIVE_SCHEMA:
+                return new LiveSchemaTupleBuilderImpl(schemaReg, tbl.tableName(), tblMgr);
+        }
+        throw new IllegalArgumentException("Unknown schema type: " + tbl.schemaMode());
     }
 
     /**
@@ -409,4 +422,11 @@ public class TableImpl extends AbstractTableView implements Table {
 
         return rows.stream().map(this::wrap).collect(Collectors.toSet());
     }
+
+    /**
+     * @param schemaMode New schema type.
+     */
+    public void schemaType(SchemaMode schemaMode) {
+        this.tbl.schema(schemaMode);
+    }
 }
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 c1be439..fb98e9b 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
@@ -34,10 +34,10 @@ import org.apache.ignite.table.TupleBuilder;
  */
 public class TupleBuilderImpl implements TupleBuilder, Tuple {
     /** Columns values. */
-    private final Map<String, Object> map;
+    protected Map<String, Object> map;
 
     /** Current schema descriptor. */
-    private final SchemaDescriptor schemaDesc;
+    private SchemaDescriptor schemaDesc;
 
     /**
      * Constructor.
@@ -50,15 +50,15 @@ public class TupleBuilderImpl implements TupleBuilder, Tuple {
     }
 
     /** {@inheritDoc} */
-    @Override public TupleBuilder set(String colName, Object value) {
-        Column col = schemaDesc.column(colName);
+    @Override public TupleBuilder set(String colName, Object val) {
+        Column col = schema().column(colName);
 
         if (col == null)
             throw new ColumnNotFoundException("Column not found [col=" + colName + "schema=" + schemaDesc + ']');
 
-        col.validate(value);
+        col.validate(val);
 
-        map.put(colName, value);
+        map.put(colName, val);
 
         return this;
     }
@@ -137,4 +137,11 @@ public class TupleBuilderImpl implements TupleBuilder, Tuple {
     public SchemaDescriptor schema() {
         return schemaDesc;
     }
+
+    /**
+     * @param schemaDesc New current schema descriptor.
+     */
+    protected void schema(SchemaDescriptor schemaDesc) {
+        this.schemaDesc = schemaDesc;
+    }
 }
diff --git a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/TableManager.java b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/TableManager.java
index d27adcc..a3d2ee7 100644
--- a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/TableManager.java
+++ b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/TableManager.java
@@ -164,7 +164,7 @@ public class TableManager extends Producer<TableEvent, TableEventParameters> imp
 
         InternalTableImpl internalTable = new InternalTableImpl(name, tblId, partitionMap, partitions);
 
-        var table = new TableImpl(internalTable, schemaReg);
+        var table = new TableImpl(internalTable, schemaReg, this);
 
         tables.put(name, table);
 
diff --git a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/storage/InternalTableImpl.java b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/storage/InternalTableImpl.java
index 13afd67..acb0efb 100644
--- a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/storage/InternalTableImpl.java
+++ b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/storage/InternalTableImpl.java
@@ -44,6 +44,7 @@ import org.apache.ignite.internal.table.distributed.command.UpsertCommand;
 import org.apache.ignite.internal.table.distributed.command.response.MultiRowsResponse;
 import org.apache.ignite.internal.table.distributed.command.response.SingleRowResponse;
 import org.apache.ignite.raft.client.service.RaftGroupService;
+import org.apache.ignite.schema.SchemaMode;
 import org.jetbrains.annotations.NotNull;
 
 /**
@@ -62,6 +63,9 @@ public class InternalTableImpl implements InternalTable {
     /** Table identifier. */
     private UUID tableId;
 
+    /** Table schema mode. */
+    private volatile SchemaMode schemaMode;
+
     /**
      * @param tableName Table name.
      * @param tableId Table id.
@@ -78,6 +82,8 @@ public class InternalTableImpl implements InternalTable {
         this.tableId = tableId;
         this.partitionMap = partMap;
         this.partitions = partitions;
+
+        this.schemaMode = SchemaMode.STRICT_SCHEMA;
     }
 
     /** {@inheritDoc} */
@@ -91,6 +97,16 @@ public class InternalTableImpl implements InternalTable {
     }
 
     /** {@inheritDoc} */
+    @Override public SchemaMode schemaMode() {
+        return schemaMode;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void schema(SchemaMode schemaMode) {
+        this.schemaMode = schemaMode;
+    }
+
+    /** {@inheritDoc} */
     @Override public @NotNull CompletableFuture<BinaryRow> get(BinaryRow keyRow) {
         return partitionMap.get(partId(keyRow)).<SingleRowResponse>run(new GetCommand(keyRow))
             .thenApply(SingleRowResponse::getValue);
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 d7f6feb..13c9a0c 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
@@ -43,7 +43,7 @@ public class Example {
      * @return Table implementation.
      */
     private static List<Table> tableFactory() {
-        return Collections.singletonList(new TableImpl(new DummyInternalTableImpl(), null));
+        return Collections.singletonList(new TableImpl(new DummyInternalTableImpl(), null, null));
     }
 
     /**
diff --git a/modules/table/src/test/java/org/apache/ignite/internal/table/StrictSchemaOperationsTest.java b/modules/table/src/test/java/org/apache/ignite/internal/table/StrictSchemaOperationsTest.java
index f9d2a9d..22bad4c 100644
--- a/modules/table/src/test/java/org/apache/ignite/internal/table/StrictSchemaOperationsTest.java
+++ b/modules/table/src/test/java/org/apache/ignite/internal/table/StrictSchemaOperationsTest.java
@@ -47,7 +47,7 @@ public class StrictSchemaOperationsTest {
             new Column[] {new Column("val", NativeTypes.INT64, false)}
         );
 
-        Table tbl = new TableImpl(new DummyInternalTableImpl(), new DummySchemaManagerImpl(schema));
+        Table tbl = new TableImpl(new DummyInternalTableImpl(), new DummySchemaManagerImpl(schema), null);
 
         assertThrows(ColumnNotFoundException.class, () -> tbl.tupleBuilder().set("invalidCol", 0));
     }
@@ -67,7 +67,7 @@ public class StrictSchemaOperationsTest {
             }
         );
 
-        Table tbl = new TableImpl(new DummyInternalTableImpl(), new DummySchemaManagerImpl(schema));
+        Table tbl = new TableImpl(new DummyInternalTableImpl(), new DummySchemaManagerImpl(schema), null);
 
         // Check not-nullable column.
         assertThrows(IllegalArgumentException.class, () -> tbl.tupleBuilder().set("id", null));
@@ -93,7 +93,7 @@ public class StrictSchemaOperationsTest {
             }
         );
 
-        Table tbl = new TableImpl(new DummyInternalTableImpl(), new DummySchemaManagerImpl(schema));
+        Table tbl = new TableImpl(new DummyInternalTableImpl(), new DummySchemaManagerImpl(schema), null);
 
         tbl.tupleBuilder().set("valString", "qwe");
         tbl.tupleBuilder().set("valString", "qw");
@@ -119,7 +119,7 @@ public class StrictSchemaOperationsTest {
                 new Column("valLimited", NativeTypes.blobOf(2), true)
             });
 
-        Table tbl = new TableImpl(new DummyInternalTableImpl(), new DummySchemaManagerImpl(schema));
+        Table tbl = new TableImpl(new DummyInternalTableImpl(), new DummySchemaManagerImpl(schema), null);
 
         tbl.tupleBuilder().set("valUnlimited", null);
         tbl.tupleBuilder().set("valLimited", null);
diff --git a/modules/table/src/test/java/org/apache/ignite/internal/table/TableBinaryViewOperationsTest.java b/modules/table/src/test/java/org/apache/ignite/internal/table/TableBinaryViewOperationsTest.java
index 82263fa..f24babd 100644
--- a/modules/table/src/test/java/org/apache/ignite/internal/table/TableBinaryViewOperationsTest.java
+++ b/modules/table/src/test/java/org/apache/ignite/internal/table/TableBinaryViewOperationsTest.java
@@ -57,7 +57,7 @@ public class TableBinaryViewOperationsTest {
             new Column[]{new Column("val", NativeTypes.INT64, false)}
         );
 
-        Table tbl = new TableImpl(new DummyInternalTableImpl(), new DummySchemaManagerImpl(schema));
+        Table tbl = new TableImpl(new DummyInternalTableImpl(), new DummySchemaManagerImpl(schema), null);
 
         final Tuple tuple = tbl.tupleBuilder().set("id", 1L).set("val", 11L).build();
         final Tuple newTuple = tbl.tupleBuilder().set("id", 1L).set("val", 22L).build();
@@ -92,7 +92,7 @@ public class TableBinaryViewOperationsTest {
             new Column[]{new Column("val", NativeTypes.INT64, false)}
         );
 
-        Table tbl = new TableImpl(new DummyInternalTableImpl(), new DummySchemaManagerImpl(schema));
+        Table tbl = new TableImpl(new DummyInternalTableImpl(), new DummySchemaManagerImpl(schema), null);
 
         final Tuple tuple = tbl.tupleBuilder().set("id", 1L).set("val", 11L).build();
         final Tuple newTuple = tbl.tupleBuilder().set("id", 1L).set("val", 22L).build();
@@ -128,7 +128,7 @@ public class TableBinaryViewOperationsTest {
             new Column[]{new Column("val", NativeTypes.INT64, false)}
         );
 
-        Table tbl = new TableImpl(new DummyInternalTableImpl(), new DummySchemaManagerImpl(schema));
+        Table tbl = new TableImpl(new DummyInternalTableImpl(), new DummySchemaManagerImpl(schema), null);
 
         final Tuple tuple = tbl.tupleBuilder().set("id", 1L).set("val", 11L).build();
         final Tuple newTuple = tbl.tupleBuilder().set("id", 1L).set("val", 22L).build();
@@ -161,7 +161,7 @@ public class TableBinaryViewOperationsTest {
             new Column[]{new Column("val", NativeTypes.INT64, false)}
         );
 
-        Table tbl = new TableImpl(new DummyInternalTableImpl(), new DummySchemaManagerImpl(schema));
+        Table tbl = new TableImpl(new DummyInternalTableImpl(), new DummySchemaManagerImpl(schema), null);
 
         final Tuple tuple = tbl.tupleBuilder().set("id", 1L).set("val", 11L).build();
 
@@ -192,7 +192,7 @@ public class TableBinaryViewOperationsTest {
             new Column[]{new Column("val", NativeTypes.INT64, false)}
         );
 
-        Table tbl = new TableImpl(new DummyInternalTableImpl(), new DummySchemaManagerImpl(schema));
+        Table tbl = new TableImpl(new DummyInternalTableImpl(), new DummySchemaManagerImpl(schema), null);
 
         final Tuple keyTuple = tbl.tupleBuilder().set("id", 1L).build();
         final Tuple tuple = tbl.tupleBuilder().set("id", 1L).set("val", 11L).build();
@@ -244,7 +244,7 @@ public class TableBinaryViewOperationsTest {
             new Column[]{new Column("val", NativeTypes.INT64, false)}
         );
 
-        Table tbl = new TableImpl(new DummyInternalTableImpl(), new DummySchemaManagerImpl(schema));
+        Table tbl = new TableImpl(new DummyInternalTableImpl(), new DummySchemaManagerImpl(schema), null);
 
         final Tuple keyTuple = tbl.tupleBuilder().set("id", 1L).build();
         final Tuple tuple = tbl.tupleBuilder().set("id", 1L).set("val", 11L).build();
@@ -280,7 +280,7 @@ public class TableBinaryViewOperationsTest {
             new Column[]{new Column("val", NativeTypes.INT64, false)}
         );
 
-        Table tbl = new TableImpl(new DummyInternalTableImpl(), new DummySchemaManagerImpl(schema));
+        Table tbl = new TableImpl(new DummyInternalTableImpl(), new DummySchemaManagerImpl(schema), null);
 
         final Tuple keyTuple = tbl.tupleBuilder().set("id", 1L).build();
         final Tuple tuple = tbl.tupleBuilder().set("id", 1L).set("val", 11L).build();
@@ -321,7 +321,7 @@ public class TableBinaryViewOperationsTest {
             }
         );
 
-        Table tbl = new TableImpl(new DummyInternalTableImpl(), new DummySchemaManagerImpl(schema));
+        Table tbl = new TableImpl(new DummyInternalTableImpl(), new DummySchemaManagerImpl(schema), null);
 
         final Tuple keyTuple0 = new TestTupleBuilder().set("id", 0).set("id1", 0).build();
         final Tuple keyTuple1 = new TestTupleBuilder().set("id1", 0).build();
@@ -357,7 +357,7 @@ public class TableBinaryViewOperationsTest {
             }
         );
 
-        Table tbl = new TableImpl(new DummyInternalTableImpl(), new DummySchemaManagerImpl(schema));
+        Table tbl = new TableImpl(new DummyInternalTableImpl(), new DummySchemaManagerImpl(schema), null);
 
         final Tuple keyTuple0 = tbl.tupleBuilder().set("id", 0L).build();
         final Tuple keyTuple1 = tbl.tupleBuilder().set("id", 1L).build();
diff --git a/modules/table/src/test/java/org/apache/ignite/internal/table/impl/DummyInternalTableImpl.java b/modules/table/src/test/java/org/apache/ignite/internal/table/impl/DummyInternalTableImpl.java
index ef1a901..4f4bf29 100644
--- a/modules/table/src/test/java/org/apache/ignite/internal/table/impl/DummyInternalTableImpl.java
+++ b/modules/table/src/test/java/org/apache/ignite/internal/table/impl/DummyInternalTableImpl.java
@@ -28,6 +28,7 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.Collectors;
 import org.apache.ignite.internal.schema.BinaryRow;
 import org.apache.ignite.internal.table.InternalTable;
+import org.apache.ignite.schema.SchemaMode;
 import org.jetbrains.annotations.NotNull;
 
 /**
@@ -88,6 +89,15 @@ public class DummyInternalTableImpl implements InternalTable {
     }
 
     /** {@inheritDoc} */
+    @Override public @NotNull SchemaMode schemaMode() {
+        return SchemaMode.STRICT_SCHEMA;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void schema(SchemaMode schemaMode) {
+    }
+
+    /** {@inheritDoc} */
     @Override public CompletableFuture<BinaryRow> get(@NotNull BinaryRow row) {
         assert row != null;