| /* |
| * 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.configuration; |
| |
| import java.math.BigDecimal; |
| import java.math.BigInteger; |
| import java.time.Instant; |
| import java.time.LocalDate; |
| import java.time.LocalDateTime; |
| import java.time.LocalTime; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.LinkedHashMap; |
| 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; |
| import org.apache.ignite.configuration.schemas.table.ColumnTypeView; |
| import org.apache.ignite.configuration.schemas.table.ColumnView; |
| import org.apache.ignite.configuration.schemas.table.IndexColumnChange; |
| import org.apache.ignite.configuration.schemas.table.IndexColumnView; |
| import org.apache.ignite.configuration.schemas.table.TableChange; |
| import org.apache.ignite.configuration.schemas.table.TableConfiguration; |
| import org.apache.ignite.configuration.schemas.table.TableIndexChange; |
| import org.apache.ignite.configuration.schemas.table.TableIndexView; |
| import org.apache.ignite.configuration.schemas.table.TableView; |
| import org.apache.ignite.configuration.schemas.table.TablesChange; |
| import org.apache.ignite.internal.schema.ColumnImpl; |
| import org.apache.ignite.internal.schema.HashIndexImpl; |
| import org.apache.ignite.internal.schema.PartialIndexImpl; |
| import org.apache.ignite.internal.schema.PrimaryIndexImpl; |
| import org.apache.ignite.internal.schema.SchemaTableImpl; |
| import org.apache.ignite.internal.schema.SortedIndexColumnImpl; |
| import org.apache.ignite.internal.schema.SortedIndexImpl; |
| import org.apache.ignite.schema.Column; |
| import org.apache.ignite.schema.ColumnType; |
| import org.apache.ignite.schema.HashIndex; |
| import org.apache.ignite.schema.IndexColumn; |
| import org.apache.ignite.schema.PartialIndex; |
| import org.apache.ignite.schema.PrimaryIndex; |
| import org.apache.ignite.schema.SchemaTable; |
| import org.apache.ignite.schema.SortOrder; |
| import org.apache.ignite.schema.SortedIndex; |
| import org.apache.ignite.schema.SortedIndexColumn; |
| import org.apache.ignite.schema.TableIndex; |
| |
| /** |
| * Configuration to schema and vice versa converter. |
| */ |
| public class SchemaConfigurationConverter { |
| /** Hash index type. */ |
| private static final String HASH_TYPE = "HASH"; |
| |
| /** Sorted index type. */ |
| private static final String SORTED_TYPE = "SORTED"; |
| |
| /** Partial index type. */ |
| private static final String PARTIAL_TYPE = "PARTIAL"; |
| |
| /** Primary key index type. */ |
| private static final String PK_TYPE = "PK"; |
| |
| /** Types map. */ |
| private static final Map<String, ColumnType> types = new HashMap<>(); |
| |
| static { |
| putType(ColumnType.INT8); |
| putType(ColumnType.INT16); |
| putType(ColumnType.INT32); |
| putType(ColumnType.INT64); |
| putType(ColumnType.UINT8); |
| putType(ColumnType.UINT16); |
| putType(ColumnType.UINT32); |
| putType(ColumnType.UINT64); |
| putType(ColumnType.FLOAT); |
| putType(ColumnType.DOUBLE); |
| putType(ColumnType.UUID); |
| putType(ColumnType.DATE); |
| } |
| |
| /** |
| * @param type Column type. |
| */ |
| private static void putType(ColumnType type) { |
| types.put(type.typeSpec().name(), type); |
| } |
| |
| /** |
| * Convert SortedIndexColumn to IndexColumnChange. |
| * |
| * @param col IndexColumnChange. |
| * @param colInit IndexColumnChange to fulfill. |
| * @return IndexColumnChange to get result from. |
| */ |
| public static IndexColumnChange convert(SortedIndexColumn col, IndexColumnChange colInit) { |
| colInit.changeName(col.name()); |
| |
| colInit.changeAsc(col.sortOrder() == SortOrder.ASC); |
| |
| return colInit; |
| } |
| |
| /** |
| * Convert IndexColumnView to SortedIndexColumn. |
| * |
| * @param colCfg IndexColumnView. |
| * @return SortedIndexColumn. |
| */ |
| public static SortedIndexColumn convert(IndexColumnView colCfg) { |
| return new SortedIndexColumnImpl(colCfg.name(), colCfg.asc() ? SortOrder.ASC : SortOrder.DESC); |
| } |
| |
| /** |
| * Convert TableIndex to TableIndexChange. |
| * |
| * @param idx TableIndex. |
| * @param idxChg TableIndexChange to fulfill. |
| * @return TableIndexChange to get result from. |
| */ |
| public static TableIndexChange convert(TableIndex idx, TableIndexChange idxChg) { |
| idxChg.changeName(idx.name()); |
| idxChg.changeType(idx.type()); |
| |
| switch (idx.type().toUpperCase()) { |
| case HASH_TYPE: |
| HashIndex hashIdx = (HashIndex)idx; |
| |
| String[] colNames = hashIdx.columns().stream().map(IndexColumn::name).toArray(String[]::new); |
| |
| idxChg.changeColNames(colNames); |
| |
| break; |
| |
| case PARTIAL_TYPE: |
| PartialIndex partIdx = (PartialIndex)idx; |
| |
| idxChg.changeUniq(partIdx.unique()); |
| idxChg.changeExpr(partIdx.expr()); |
| |
| idxChg.changeColumns(colsChg -> { |
| int colIdx = 0; |
| |
| for (SortedIndexColumn col : partIdx.columns()) |
| colsChg.create(String.valueOf(colIdx++), colInit -> convert(col, colInit)); |
| }); |
| |
| break; |
| |
| case SORTED_TYPE: |
| SortedIndex sortIdx = (SortedIndex)idx; |
| idxChg.changeUniq(sortIdx.unique()); |
| |
| idxChg.changeColumns(colsInit -> { |
| int colIdx = 0; |
| |
| for (SortedIndexColumn col : sortIdx.columns()) |
| colsInit.create(String.valueOf(colIdx++), colInit -> convert(col, colInit)); |
| }); |
| |
| break; |
| |
| case PK_TYPE: |
| PrimaryIndex primIdx = (PrimaryIndex)idx; |
| |
| idxChg.changeColumns(colsInit -> { |
| int colIdx = 0; |
| |
| for (SortedIndexColumn col : primIdx.columns()) |
| colsInit.create(String.valueOf(colIdx++), colInit -> convert(col, colInit)); |
| }); |
| |
| idxChg.changeAffinityColumns(primIdx.affinityColumns().toArray( |
| new String[primIdx.affinityColumns().size()])); |
| |
| break; |
| |
| default: |
| throw new IllegalArgumentException("Unknown index type " + idx.type()); |
| } |
| |
| return idxChg; |
| } |
| |
| /** |
| * Convert TableIndexView into TableIndex. |
| * |
| * @param idxView TableIndexView. |
| * @return TableIndex. |
| */ |
| public static TableIndex convert(TableIndexView idxView) { |
| String name = idxView.name(); |
| String type = idxView.type(); |
| |
| switch (type.toUpperCase()) { |
| case HASH_TYPE: |
| String[] hashCols = idxView.colNames(); |
| |
| return new HashIndexImpl(name, hashCols); |
| |
| case SORTED_TYPE: |
| boolean sortedUniq = idxView.uniq(); |
| |
| SortedMap<Integer, SortedIndexColumn> sortedCols = new TreeMap<>(); |
| |
| for (String key : idxView.columns().namedListKeys()) { |
| SortedIndexColumn col = convert(idxView.columns().get(key)); |
| |
| sortedCols.put(Integer.valueOf(key), col); |
| } |
| |
| return new SortedIndexImpl(name, new ArrayList<>(sortedCols.values()), sortedUniq); |
| |
| case PARTIAL_TYPE: |
| boolean partialUniq = idxView.uniq(); |
| String expr = idxView.expr(); |
| |
| NamedListView<? extends IndexColumnView> colsView = idxView.columns(); |
| SortedMap<Integer, SortedIndexColumn> partialCols = new TreeMap<>(); |
| |
| for (String key : idxView.columns().namedListKeys()) { |
| SortedIndexColumn col = convert(colsView.get(key)); |
| |
| partialCols.put(Integer.valueOf(key), col); |
| } |
| |
| return new PartialIndexImpl(name, new ArrayList<>(partialCols.values()), partialUniq, expr); |
| |
| case PK_TYPE: |
| SortedMap<Integer, SortedIndexColumn> cols = new TreeMap<>(); |
| |
| for (String key : idxView.columns().namedListKeys()) { |
| SortedIndexColumn col = convert(idxView.columns().get(key)); |
| |
| cols.put(Integer.valueOf(key), col); |
| } |
| |
| String[] affCols = idxView.affinityColumns(); |
| |
| return new PrimaryIndexImpl(new ArrayList<>(cols.values()), List.of(affCols)); |
| |
| default: |
| throw new IllegalArgumentException("Unknown type " + type); |
| } |
| } |
| |
| /** |
| * Convert ColumnType to ColumnTypeChange. |
| * |
| * @param colType ColumnType. |
| * @param colTypeChg ColumnTypeChange to fulfill. |
| * @return ColumnTypeChange to get result from |
| */ |
| public static ColumnTypeChange convert(ColumnType colType, ColumnTypeChange colTypeChg) { |
| String typeName = colType.typeSpec().name().toUpperCase(); |
| |
| if (types.containsKey(typeName)) |
| colTypeChg.changeType(typeName); |
| else { |
| colTypeChg.changeType(typeName); |
| |
| switch (typeName) { |
| case "BITMASK": |
| case "BLOB": |
| case "STRING": |
| ColumnType.VarLenColumnType varLenColType = (ColumnType.VarLenColumnType)colType; |
| |
| colTypeChg.changeLength(varLenColType.length()); |
| |
| break; |
| |
| case "DECIMAL": |
| ColumnType.DecimalColumnType numColType = (ColumnType.DecimalColumnType)colType; |
| |
| colTypeChg.changePrecision(numColType.precision()); |
| colTypeChg.changeScale(numColType.scale()); |
| |
| break; |
| |
| case "NUMBER": |
| ColumnType.NumberColumnType numType = (ColumnType.NumberColumnType)colType; |
| |
| colTypeChg.changePrecision(numType.precision()); |
| |
| break; |
| |
| case "TIME": |
| case "DATETIME": |
| case "TIMESTAMP": |
| ColumnType.TemporalColumnType temporalColType = (ColumnType.TemporalColumnType)colType; |
| |
| colTypeChg.changePrecision(temporalColType.precision()); |
| |
| break; |
| |
| default: |
| throw new IllegalArgumentException("Unknown type " + colType.typeSpec().name()); |
| } |
| } |
| |
| return colTypeChg; |
| } |
| |
| /** |
| * Convert ColumnTypeView to ColumnType. |
| * |
| * @param colTypeView ColumnTypeView. |
| * @return ColumnType. |
| */ |
| public static ColumnType convert(ColumnTypeView colTypeView) { |
| String typeName = colTypeView.type().toUpperCase(); |
| ColumnType res = types.get(typeName); |
| |
| if (res != null) |
| return res; |
| else { |
| switch (typeName) { |
| case "BITMASK": |
| int bitmaskLen = colTypeView.length(); |
| |
| return ColumnType.bitmaskOf(bitmaskLen); |
| |
| case "STRING": |
| int strLen = colTypeView.length(); |
| |
| return ColumnType.stringOf(strLen); |
| |
| case "BLOB": |
| int blobLen = colTypeView.length(); |
| |
| return ColumnType.blobOf(blobLen); |
| |
| case "DECIMAL": |
| int prec = colTypeView.precision(); |
| int scale = colTypeView.scale(); |
| |
| return ColumnType.decimalOf(prec, scale); |
| |
| case "NUMBER": |
| return ColumnType.numberOf(colTypeView.precision()); |
| |
| case "TIME": |
| return ColumnType.time(colTypeView.precision()); |
| |
| case "DATETIME": |
| return ColumnType.datetime(colTypeView.precision()); |
| |
| case "TIMESTAMP": |
| return ColumnType.timestamp(colTypeView.precision()); |
| |
| default: |
| throw new IllegalArgumentException("Unknown type " + typeName); |
| } |
| } |
| } |
| |
| /** |
| * Convert column to column change. |
| * |
| * @param col Column to convert. |
| * @param colChg Column |
| * @return ColumnChange to get result from. |
| */ |
| public static ColumnChange convert(Column col, ColumnChange colChg) { |
| colChg.changeName(col.name()); |
| colChg.changeType(colTypeInit -> convert(col.type(), colTypeInit)); |
| |
| if (col.defaultValue() != null) |
| colChg.changeDefaultValue(col.defaultValue().toString()); |
| |
| colChg.changeNullable(col.nullable()); |
| |
| return colChg; |
| } |
| |
| /** |
| * Convert column view to Column. |
| * |
| * @param colView Column view. |
| * @return Column. |
| */ |
| public static Column convert(ColumnView colView) { |
| return new ColumnImpl( |
| colView.name(), |
| convert(colView.type()), |
| colView.nullable(), |
| colView.defaultValue()); |
| } |
| |
| /** |
| * Convert schema table to schema table change. |
| * |
| * @param tbl Schema table to convert. |
| * @param tblChg Change to fulfill. |
| * @return TableChange to get result from. |
| */ |
| public static TableChange convert(SchemaTable tbl, TableChange tblChg) { |
| tblChg.changeName(tbl.canonicalName()); |
| |
| tblChg.changeIndices(idxsChg -> { |
| int idxIdx = 0; |
| |
| for (TableIndex idx : tbl.indices()) |
| idxsChg.create(String.valueOf(idxIdx++), idxInit -> convert(idx, idxInit)); |
| }); |
| |
| tblChg.changeColumns(colsChg -> { |
| int colIdx = 0; |
| |
| for (Column col : tbl.keyColumns()) |
| colsChg.create(String.valueOf(colIdx++), colChg -> convert(col, colChg)); |
| |
| for (Column col : tbl.valueColumns()) |
| colsChg.create(String.valueOf(colIdx++), colChg -> convert(col, colChg)); |
| }); |
| |
| return tblChg; |
| } |
| |
| /** |
| * Convert TableConfiguration to SchemaTable. |
| * |
| * @param tblCfg TableConfiguration to convert. |
| * @return SchemaTable. |
| */ |
| public static SchemaTable convert(TableConfiguration tblCfg) { |
| return convert(tblCfg.value()); |
| } |
| |
| /** |
| * Convert configuration to SchemaTable. |
| * |
| * @param tblView TableView to convert. |
| * @return SchemaTable. |
| */ |
| public static SchemaTableImpl convert(TableView tblView) { |
| String canonicalName = tblView.name(); |
| int sepPos = canonicalName.indexOf('.'); |
| String schemaName = canonicalName.substring(0, sepPos); |
| String tableName = canonicalName.substring(sepPos + 1); |
| |
| NamedListView<? extends ColumnView> colsView = tblView.columns(); |
| |
| SortedMap<Integer, Column> columns = new TreeMap<>(); |
| |
| for (String key : colsView.namedListKeys()) { |
| ColumnView colView = colsView.get(key); |
| |
| if (colView != null) { |
| Column col = convert(colView); |
| |
| columns.put(Integer.valueOf(key), col); |
| } |
| } |
| |
| NamedListView<? extends TableIndexView> idxsView = tblView.indices(); |
| |
| Map<String, TableIndex> indices = new HashMap<>(idxsView.size()); |
| |
| for (String key : idxsView.namedListKeys()) { |
| TableIndexView idxView = idxsView.get(key); |
| TableIndex idx = convert(idxView); |
| |
| indices.put(idx.name(), idx); |
| } |
| |
| LinkedHashMap<String, Column> colsMap = new LinkedHashMap<>(colsView.size()); |
| |
| columns.forEach((i, v) -> colsMap.put(v.name(), v)); |
| |
| return new SchemaTableImpl(schemaName, tableName, colsMap, indices); |
| } |
| |
| /** |
| * Create table. |
| * |
| * @param tbl Table to create. |
| * @param tblsChange Tables change to fulfill. |
| * @return TablesChange to get result from. |
| */ |
| public static TablesChange createTable(SchemaTable tbl, TablesChange tblsChange) { |
| return tblsChange.changeTables(tblsChg -> tblsChg.create(tbl.canonicalName(), tblChg -> convert(tbl, tblChg))); |
| } |
| |
| /** |
| * Drop table. |
| * |
| * @param tbl table to drop. |
| * @param tblsChange TablesChange change to fulfill. |
| * @return TablesChange to get result from. |
| */ |
| public static TablesChange dropTable(SchemaTable tbl, TablesChange tblsChange) { |
| return tblsChange.changeTables(schmTblChange -> schmTblChange.delete(tbl.canonicalName())); |
| } |
| |
| /** |
| * Add index. |
| * |
| * @param idx Index to add. |
| * @param tblChange TableChange to fulfill. |
| * @return TableChange to get result from. |
| */ |
| public static TableChange addIndex(TableIndex idx, TableChange tblChange) { |
| return tblChange.changeIndices(idxsChg -> idxsChg.create(idx.name(), idxChg -> convert(idx, idxChg))); |
| } |
| |
| /** |
| * Drop index. |
| * |
| * @param indexName Index name to drop. |
| * @param tblChange Table change to fulfill. |
| * @return TableChange to get result from. |
| */ |
| public static TableChange dropIndex(String indexName, TableChange tblChange) { |
| return tblChange.changeIndices(idxChg -> idxChg.delete(indexName)); |
| } |
| |
| /** |
| * Add table column. |
| * |
| * @param column Column to add. |
| * @param tblChange TableChange to fulfill. |
| * @return TableChange to get result from. |
| */ |
| public static TableChange addColumn(Column column, TableChange tblChange) { |
| return tblChange.changeColumns(colsChg -> colsChg.create(column.name(), colChg -> convert(column, colChg))); |
| } |
| |
| /** |
| * Drop table column. |
| * |
| * @param columnName column name to drop. |
| * @param tblChange TableChange to fulfill. |
| * @return TableChange to get result from. |
| */ |
| 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; |
| |
| // Temporal types. |
| else if (cls == LocalDate.class) |
| return ColumnType.DATE; |
| else if (cls == LocalTime.class) |
| return ColumnType.time(ColumnType.TemporalColumnType.DEFAULT_PRECISION); |
| else if (cls == LocalDateTime.class) |
| return ColumnType.datetime(ColumnType.TemporalColumnType.DEFAULT_PRECISION); |
| else if (cls == Instant.class) |
| return ColumnType.timestamp(ColumnType.TemporalColumnType.DEFAULT_PRECISION); |
| |
| // Other types |
| else if (cls == String.class) |
| return ColumnType.string(); |
| else if (cls == UUID.class) |
| return ColumnType.UUID; |
| else if (cls == BigInteger.class) |
| return ColumnType.numberOf(); |
| else if (cls == BigDecimal.class) |
| return ColumnType.decimalOf(); |
| |
| return null; |
| } |
| |
| } |