All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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