blob: 1fda646d11ad50877fd6766f515e934b8d670e67 [file] [log] [blame]
/*
* 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.catalog.commands;
import static org.apache.ignite.internal.catalog.CatalogParamsValidationUtils.validateIdentifier;
import static org.apache.ignite.internal.catalog.commands.CatalogUtils.schemaOrThrow;
import static org.apache.ignite.internal.catalog.commands.CatalogUtils.tableOrThrow;
import static org.apache.ignite.internal.lang.IgniteStringFormatter.format;
import java.util.List;
import java.util.Objects;
import org.apache.ignite.internal.catalog.Catalog;
import org.apache.ignite.internal.catalog.CatalogCommand;
import org.apache.ignite.internal.catalog.CatalogValidationException;
import org.apache.ignite.internal.catalog.descriptors.CatalogSchemaDescriptor;
import org.apache.ignite.internal.catalog.descriptors.CatalogTableColumnDescriptor;
import org.apache.ignite.internal.catalog.descriptors.CatalogTableDescriptor;
import org.apache.ignite.internal.catalog.storage.AlterColumnEntry;
import org.apache.ignite.internal.catalog.storage.UpdateEntry;
import org.apache.ignite.sql.ColumnType;
import org.jetbrains.annotations.Nullable;
/**
* A command that changes a column in a particular table.
*/
public class AlterTableAlterColumnCommand extends AbstractTableCommand {
/** Returns command that changes a column in a particular table. */
public static AlterTableAlterColumnCommandBuilder builder() {
return new Builder();
}
private static final TypeChangeValidationListener TYPE_CHANGE_VALIDATION_HANDLER = (pattern, originalType, newType) -> {
throw new CatalogValidationException(format(pattern, originalType, newType));
};
private final String columnName;
private final @Nullable ColumnType type;
private final @Nullable Integer precision;
private final @Nullable Integer length;
private final @Nullable Integer scale;
private final @Nullable Boolean nullable;
private final @Nullable DeferredDefaultValue deferredDefault;
private AlterTableAlterColumnCommand(
String tableName,
String schemaName,
boolean ifTableExists,
String columnName,
@Nullable ColumnType type,
@Nullable Integer precision,
@Nullable Integer length,
@Nullable Integer scale,
@Nullable Boolean nullable,
@Nullable DeferredDefaultValue deferredDefault
) {
super(schemaName, tableName, ifTableExists);
this.columnName = columnName;
this.type = type;
this.precision = precision;
this.length = length;
this.scale = scale;
this.nullable = nullable;
this.deferredDefault = deferredDefault;
validate();
}
@Override
public List<UpdateEntry> get(Catalog catalog) {
CatalogSchemaDescriptor schema = schemaOrThrow(catalog, schemaName);
CatalogTableDescriptor table = tableOrThrow(schema, tableName);
CatalogTableColumnDescriptor origin = table.column(columnName);
if (origin == null) {
throw new CatalogValidationException(format(
"Column with name '{}' not found in table '{}.{}'", columnName, schemaName, tableName));
}
if (table.isPrimaryKeyColumn(origin.name())) {
validatePkColumnChange(origin);
} else {
validateValueColumnChange(origin);
}
validateColumnChange(origin);
CatalogTableColumnDescriptor target = createNewTableColumn(origin);
if (origin.equals(target)) {
// No modifications required.
return List.of();
}
return List.of(
new AlterColumnEntry(table.id(), target)
);
}
private void validate() {
validateIdentifier(columnName, "Name of the column");
}
private CatalogTableColumnDescriptor createNewTableColumn(CatalogTableColumnDescriptor origin) {
return new CatalogTableColumnDescriptor(
origin.name(),
Objects.requireNonNullElse(type, origin.type()),
Objects.requireNonNullElse(nullable, origin.nullable()),
Objects.requireNonNullElse(precision, origin.precision()),
Objects.requireNonNullElse(scale, origin.scale()),
Objects.requireNonNullElse(length, origin.length()),
deferredDefault != null ? deferredDefault.derive(origin.type()) : origin.defaultValue()
);
}
private void validatePkColumnChange(CatalogTableColumnDescriptor origin) {
if (type != null && type != origin.type()) {
throw new CatalogValidationException("Changing the type of key column is not allowed");
}
if (precision != null && precision != origin.precision()) {
throw new CatalogValidationException("Changing the precision of key column is not allowed");
}
if (scale != null && scale != origin.scale()) {
throw new CatalogValidationException("Changing the scale of key column is not allowed");
}
if (nullable != null && nullable) {
throw new CatalogValidationException("Dropping NOT NULL constraint on key column is not allowed");
}
if (deferredDefault != null) {
DefaultValue defaultValue = deferredDefault.derive(origin.type());
CatalogUtils.ensureSupportedDefault(columnName, defaultValue);
}
}
private void validateValueColumnChange(CatalogTableColumnDescriptor origin) {
if (deferredDefault != null) {
DefaultValue defaultValue = deferredDefault.derive(origin.type());
CatalogUtils.ensureNonFunctionalDefault(columnName, defaultValue);
}
}
private void validateColumnChange(CatalogTableColumnDescriptor origin) {
CatalogUtils.validateColumnChange(origin, type, precision, scale, length, TYPE_CHANGE_VALIDATION_HANDLER);
if (nullable != null && !nullable && origin.nullable()) {
throw new CatalogValidationException("Adding NOT NULL constraint is not allowed");
}
}
private static class Builder implements AlterTableAlterColumnCommandBuilder {
private String tableName;
private String schemaName;
private boolean ifTableExists;
private String columnName;
private @Nullable ColumnType type;
private @Nullable Integer precision;
private @Nullable Integer length;
private @Nullable Integer scale;
private @Nullable Boolean nullable;
private @Nullable DeferredDefaultValue deferredDefault;
@Override
public Builder tableName(String tableName) {
this.tableName = tableName;
return this;
}
@Override
public Builder schemaName(String schemaName) {
this.schemaName = schemaName;
return this;
}
@Override
public AlterTableAlterColumnCommandBuilder ifTableExists(boolean ifTableExists) {
this.ifTableExists = ifTableExists;
return this;
}
@Override
public Builder columnName(String columnName) {
this.columnName = columnName;
return this;
}
@Override
public Builder type(ColumnType type) {
this.type = type;
return this;
}
@Override
public Builder precision(int precision) {
this.precision = precision;
return this;
}
@Override
public Builder length(int length) {
this.length = length;
return this;
}
@Override
public Builder scale(int scale) {
this.scale = scale;
return this;
}
@Override
public Builder nullable(boolean nullable) {
this.nullable = nullable;
return this;
}
@Override
public Builder deferredDefaultValue(DeferredDefaultValue deferredDefault) {
this.deferredDefault = deferredDefault;
return this;
}
@Override
public CatalogCommand build() {
return new AlterTableAlterColumnCommand(
tableName,
schemaName,
ifTableExists,
columnName,
type,
precision,
length,
scale,
nullable,
deferredDefault
);
}
}
}