AMashenkov commented on a change in pull request #212: URL: https://github.com/apache/ignite-3/pull/212#discussion_r667777697
########## File path: modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/LiveSchemaChangeTableTest.java ########## @@ -0,0 +1,177 @@ +/* + * 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.SchemaType; +import org.apache.ignite.internal.table.ColumnNotFoundException; +import org.apache.ignite.internal.table.TableImpl; +import org.apache.ignite.table.KeyValueBinaryView; +import org.apache.ignite.table.Table; +import org.apache.ignite.table.Tuple; +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.assertThrows; + +/** + * Live schema tests. + */ +@Disabled("https://issues.apache.org/jira/browse/IGNITE-14581") +class LiveSchemaChangeTableTest extends AbstractSchemaChangeTest { + /** + * Check live schema add columns + */ + @Test + public void testLiveSchemaAddColumns() { + List<Ignite> grid = startGrid(); + + createTable(grid); + + Table tbl = grid.get(1).tables().table(TABLE); + + ((TableImpl)tbl).schemaType(SchemaType.LIVE_SCHEMA); + + Tuple row = tbl.tupleBuilder().set("key", 1L).set("valStrNew", "111").set("valIntNew", 333).build(); + + tbl.insert(row); Review comment: Looks like both "inserts" cheks the same. Let's check 1. a row of old schema can be successfully updated with row of new schema. 2. a row of new schema can be successfully inserted. 3. Inserting row of old schema will not lead to column removal. ########## File path: modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/LiveSchemaChangeTableTest.java ########## @@ -0,0 +1,177 @@ +/* + * 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.SchemaType; +import org.apache.ignite.internal.table.ColumnNotFoundException; +import org.apache.ignite.internal.table.TableImpl; +import org.apache.ignite.table.KeyValueBinaryView; +import org.apache.ignite.table.Table; +import org.apache.ignite.table.Tuple; +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.assertThrows; + +/** + * Live schema tests. + */ +@Disabled("https://issues.apache.org/jira/browse/IGNITE-14581") +class LiveSchemaChangeTableTest extends AbstractSchemaChangeTest { + /** + * Check live schema add columns + */ + @Test + public void testLiveSchemaAddColumns() { + List<Ignite> grid = startGrid(); + + createTable(grid); + + Table tbl = grid.get(1).tables().table(TABLE); + + ((TableImpl)tbl).schemaType(SchemaType.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")); + + Tuple secondRow = tbl.tupleBuilder().set("key", 2L).set("valStrNew", "222").set("valIntNew", 42).build(); + + tbl.insert(secondRow); + + Tuple res2 = tbl.get(secondRow); + + assertEquals("222", res2.value("valStrNew")); + assertEquals(Integer.valueOf(42), res2.value("valIntNew")); + } + + /** + * Check live schema kvBinaryView add columns + */ + @Test + public void testLiveSchemaAddColumnsKVBinaryView() { + List<Ignite> grid = startGrid(); + + createTable(grid); + + Table tbl = grid.get(1).tables().table(TABLE); + + ((TableImpl)tbl).schemaType(SchemaType.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 key2 = kvBinaryView.tupleBuilder().set("key", 2L).build(); + Tuple val2 = kvBinaryView.tupleBuilder().set("valStrNew", "222").set("valIntNew", 42).build(); + + kvBinaryView.put(key2, val2); + + Tuple res = kvBinaryView.get(key); + assertEquals("111", res.value("valStrNew")); + assertEquals(Integer.valueOf(333), res.value("valIntNew")); + + + Tuple res2 = kvBinaryView.get(key2); + + assertEquals("222", res2.value("valStrNew")); + assertEquals(Integer.valueOf(42), res2.value("valIntNew")); + } + + /** + * Check live schema tuple can handle different value types + */ + @Test + public void testLiveSchemaDifferentColumnTypes() { Review comment: I'd check all new columns are nullable and 'null' is a default value. Note that 'null' can't be the default value for the string for now, due to a bug (config doesn't support nulls). ########## File path: modules/table/src/main/java/org/apache/ignite/internal/table/LiveSchemaTupleBuilderImpl.java ########## @@ -0,0 +1,127 @@ +/* + * 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.Map; +import java.util.Objects; +import org.apache.ignite.internal.schema.Column; +import org.apache.ignite.internal.schema.SchemaDescriptor; +import org.apache.ignite.internal.schema.SchemaRegistry; +import org.apache.ignite.internal.schema.marshaller.MarshallerUtil; +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 final Map<String, Object> liveSchemaColMap; + + /** 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; + + liveSchemaColMap = new HashMap<>(); + } + + /** {@inheritDoc} */ + @Override public TupleBuilder set(String colName, Object val) { + Column col = schemaRegistry.schema().column(colName); + + if (col == null) { + if (val == null) + return this; + + liveSchemaColMap.put(colName, val); + return this; + } + + super.set(colName, val); + + return this; + } + + /** {@inheritDoc} */ + @Override public Tuple build() { + for (Map.Entry<String, Object> entry : liveSchemaColMap.entrySet()) { + String colName = entry.getKey(); + Object val = entry.getValue(); + + ColumnType type = MarshallerUtil.columnType(val.getClass()); + + if (type == null) + throw new UnsupportedOperationException("Live schema update for type [" + val.getClass() + "] is not supported yet."); + + createColumn(colName, type); Review comment: Create columns is a costly distributed operation to be done in a loop. Let's change schema only once. ########## File path: modules/schema/src/main/java/org/apache/ignite/internal/schema/SchemaType.java ########## @@ -0,0 +1,29 @@ +/* + * 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.schema; + +/** + * Schema types. + */ +public enum SchemaType { Review comment: Let's move this to the public package of ignite-api modue. ########## File path: modules/table/src/main/java/org/apache/ignite/internal/table/KVBinaryViewImpl.java ########## @@ -288,7 +314,13 @@ public KVBinaryViewImpl(InternalTable tbl, SchemaRegistry schemaReg) { /** {@inheritDoc} */ @Override public TupleBuilder tupleBuilder() { - return new TupleBuilderImpl(schemaReg.schema()); + switch (schemaType) { + case STRICT_SCHEMA: + return new TupleBuilderImpl(schemaReg.schema()); + case LIVE_SCHEMA: + return new LiveSchemaTupleBuilderImpl(schemaReg, tbl.tableName(), tblMgr); + } Review comment: ```suggestion } ``` ########## File path: modules/api/src/main/java/org/apache/ignite/schema/SchemaMode.java ########## @@ -0,0 +1,29 @@ +/* + * 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 types. Review comment: ```suggestion * Schema mode. * * Defines the way inserting data will be validated against the schema and schema evolution capabilities. ``` ########## File path: modules/table/src/main/java/org/apache/ignite/internal/table/LiveSchemaTupleBuilderImpl.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.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.marshaller.MarshallerUtil; +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 final Map<String, Object> liveSchemaColMap; + + /** 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; + + liveSchemaColMap = new HashMap<>(); Review comment: Let's instantiate it lazily. ########## File path: modules/api/src/main/java/org/apache/ignite/schema/SchemaMode.java ########## @@ -0,0 +1,29 @@ +/* + * 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 types. + */ +public enum SchemaMode { + /** Default schema allows to create columns only explicitly by calling the ALTER_TABLE command. */ + STRICT_SCHEMA, + + /** Live schema allows to create columns implicitly by adding previously nonexistent columns during insert. */ Review comment: ```suggestion /** 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. */ ``` ########## File path: modules/table/src/main/java/org/apache/ignite/internal/table/distributed/storage/InternalTableImpl.java ########## @@ -62,6 +63,9 @@ /** Table identifier. */ private UUID tableId; + /** Table schema mode. May be STRICT or LIVE */ + private SchemaMode schemaMode; Review comment: ```suggestion private volatile SchemaMode schemaMode; ``` Does this make sense? ########## File path: modules/table/src/main/java/org/apache/ignite/internal/table/LiveSchemaTupleBuilderImpl.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.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.marshaller.MarshallerUtil; +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 final Map<String, Object> liveSchemaColMap; + + /** 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; + + liveSchemaColMap = new HashMap<>(); + } + + /** {@inheritDoc} */ + @Override public TupleBuilder set(String colName, Object val) { + Column col = schema().column(colName); + + if (col == null) { + if (val == null) + return this; + + liveSchemaColMap.put(colName, val); + return this; + } + super.set(colName, val); + + return this; + } + + /** {@inheritDoc} */ + @Override public Tuple build() { + Map<String, ColumnType> colTypeMap = new HashMap<>(); Review comment: Let's instantiate it lazily. liveSchemaColMap and colTypeMap will be empty most of time. ########## File path: modules/api/src/main/java/org/apache/ignite/schema/SchemaMode.java ########## @@ -0,0 +1,29 @@ +/* + * 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 types. + */ +public enum SchemaMode { + /** Default schema allows to create columns only explicitly by calling the ALTER_TABLE command. */ Review comment: ```suggestion /** Normal mode offers strong validation for the inserting data. Explicit schema changes only are allowed. */ ``` ########## File path: modules/table/src/main/java/org/apache/ignite/internal/table/LiveSchemaTupleBuilderImpl.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.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.marshaller.MarshallerUtil; +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 final Map<String, Object> liveSchemaColMap; + + /** 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; + + liveSchemaColMap = new HashMap<>(); + } + + /** {@inheritDoc} */ + @Override public TupleBuilder set(String colName, Object val) { + Column col = schema().column(colName); + + if (col == null) { + if (val == null) + return this; + + liveSchemaColMap.put(colName, val); + return this; Review comment: ```suggestion if (liveSchemaColMap == null) liveSchemaColMap = new HashMap(); liveSchemaColMap.put(colName, val); return this; ``` ########## File path: modules/table/src/main/java/org/apache/ignite/internal/table/distributed/storage/InternalTableImpl.java ########## @@ -62,6 +63,9 @@ /** Table identifier. */ private UUID tableId; + /** Table schema mode. May be STRICT or LIVE */ Review comment: ```suggestion /** Table schema mode. */ ``` ########## File path: modules/table/src/main/java/org/apache/ignite/internal/table/LiveSchemaTupleBuilderImpl.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.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.marshaller.MarshallerUtil; +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 final Map<String, Object> liveSchemaColMap; + + /** 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; + + liveSchemaColMap = new HashMap<>(); + } + + /** {@inheritDoc} */ + @Override public TupleBuilder set(String colName, Object val) { + Column col = schema().column(colName); + + if (col == null) { + if (val == null) + return this; + + liveSchemaColMap.put(colName, val); + return this; + } + super.set(colName, val); + + return this; + } + + /** {@inheritDoc} */ + @Override public Tuple build() { + Map<String, ColumnType> colTypeMap = new HashMap<>(); + + for (Map.Entry<String, Object> entry : liveSchemaColMap.entrySet()) { + String colName = entry.getKey(); + Object val = entry.getValue(); + + ColumnType type = MarshallerUtil.columnType(val.getClass()); + + if (type == null) + throw new UnsupportedOperationException("Live schema update for type [" + val.getClass() + "] is not supported yet."); + + colTypeMap.put(colName, type); + } + + if (!colTypeMap.isEmpty()) { + createColumns(colTypeMap); + this.schema(schemaRegistry.schema()); + rebuildTupleWithNewSchema(); + } + + liveSchemaColMap.forEach(super::set); + + return this; Review comment: As the method 'build()' should return valid Tuple, you can simply rewrite this in a next way returning a new Tuple instance: ``` if (colTypeMap.isEmpty()) return this; createColumns(colTypeMap); return rebuildTupleWithNewSchema(); ``` It looks ok to insert a row of a-bit-old-version in case of concurrent schema changing. The result will be as if the row was added just before the foreign schema update and the row will be upgraded in the background anyway. The only we should bother is the schema fit the data. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
