io.datarouter.gcp.spanner.ddl.SpannerTableAlterSchemaService Maven / Gradle / Ivy
The newest version!
/*
* Copyright © 2009 HotPads ([email protected])
*
* Licensed 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 io.datarouter.gcp.spanner.ddl;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import com.google.cloud.spanner.ResultSet;
import io.datarouter.bytes.KvString;
import io.datarouter.model.field.Field;
import io.datarouter.model.field.FieldKey;
import io.datarouter.scanner.Scanner;
import io.datarouter.storage.config.schema.SchemaUpdateOptions;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
@Singleton
public class SpannerTableAlterSchemaService{
@Inject
private SchemaUpdateOptions updateOptions;
public void generateUpdateStatementColumns(
String tableName,
List currentColumns,
List currentPkColumns,
ResultSet columnResult,
ResultSet primaryKeyResult,
SpannerUpdateStatements statements){
List primaryKeyColumns = extractColumns(primaryKeyResult);
if(!primaryKeyColumns.equals(currentPkColumns)){
throw new RuntimeException("Cannot modify primary key columns in spanner tableName=" + tableName);
}
List columns = extractColumns(columnResult);
Map columnsToCompareTypes = columnNameIntersection(currentColumns, columns);
columnsToCompareTypes.forEach((currCol, newCol) -> {
if(!currCol.getType().equals(newCol.getType())){
throw new RuntimeException("Do not change the type of a Spanner column, instead add a new column and "
+ "migrate the data."
+ new KvString()
.add("TableName", tableName)
.add("ColumnName", currCol.getName())
.add("CurrentColumnType", currCol.getType().toString())
.add("NewColumnType", newCol.getType().toString()));
}
});
List colToAdd = columnNameDifferences(currentColumns, columns);
List colToRemove = columnNameDifferences(columns, currentColumns);
List colToAlter = columnsToAlter(currentColumns, columns);
colToAdd.forEach(col -> statements.updateFunction(
SpannerTableOperationsTool.addColumns(tableName, col),
updateOptions::getAddColumns,
true));
colToRemove.forEach(col -> statements.updateFunction(
SpannerTableOperationsTool.dropColumns(tableName, col),
updateOptions::getDeleteColumns,
false));
colToAlter.forEach(col -> statements.updateFunction(
SpannerTableOperationsTool.alterColumns(tableName, col),
updateOptions::getModifyColumns,
false));
}
public Set getIndexes(ResultSet rs){
Set result = new HashSet<>();
while(rs.next()){
result.add(rs.getString("INDEX_NAME"));
}
result.remove("PRIMARY_KEY");
return result;
}
public boolean indexEqual(SpannerIndex index, ResultSet rs){
List indexKeyColumns = Scanner.of(index.getKeyFields())
.map(Field::getKey)
.map(FieldKey::getColumnName)
.list();
List nonKeyColumns = Scanner.of(index.getNonKeyFields())
.map(Field::getKey)
.map(FieldKey::getColumnName)
.list();
boolean runOnce = false;
while(rs.next()){
runOnce = true;
String columnName = rs.getString("COLUMN_NAME");
if(rs.isNull("ORDINAL_POSITION")){
if(!nonKeyColumns.contains(columnName)){
return false;
}
}else if(!indexKeyColumns.contains(columnName)){
return false;
}
}
return runOnce;
}
private List extractColumns(ResultSet rs){
List columns = new ArrayList<>();
while(rs.next()){
String columnName = rs.getString("COLUMN_NAME");
boolean isNullable = rs.getString("IS_NULLABLE").equalsIgnoreCase("YES");
SpannerColumnType type = SpannerColumnType.fromSchemaString(rs.getString("SPANNER_TYPE"));
columns.add(new SpannerColumn(columnName, type, isNullable));
}
return columns;
}
private List columnNameDifferences(List columns1, List columns2){
Map col1Map = Scanner.of(columns1)
.toMapSupplied(SpannerColumn::getName, LinkedHashMap::new);
columns2.forEach(col -> col1Map.remove(col.getName()));
return new ArrayList<>(col1Map.values());
}
private Map columnNameIntersection(
List columns1,
List columns2){
Map nameToColumns2 = Scanner.of(columns2)
.toMapSupplied(SpannerColumn::getName, LinkedHashMap::new);
Map intersectionMap = Scanner.of(columns1)
.toMap(col1 -> col1, col1 -> nameToColumns2.get(col1.getName()));
intersectionMap.values().removeIf(Objects::isNull);
return intersectionMap;
}
private List columnsToAlter(List currentColumns, List existingColumns){
Map columnMap = Scanner.of(existingColumns)
.toMapSupplied(SpannerColumn::getName, LinkedHashMap::new);
return Scanner.of(currentColumns)
.include(col -> columnMap.containsKey(col.getName()))
.exclude(col -> columnMap.get(col.getName()).getType() == col.getType())
.list();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy