blob: f45772f3da44da868ff210c1a1299b61ea089341 [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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.hadoop.hive.metastore;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hive.metastore.api.FieldSchema;
import org.apache.hadoop.hive.metastore.api.InvalidOperationException;
import org.apache.hadoop.hive.metastore.api.SerDeInfo;
import org.apache.hadoop.hive.metastore.api.Table;
import org.apache.hadoop.hive.metastore.conf.MetastoreConf;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Default incompatible table change handler. This is invoked by the {@link
* HiveAlterHandler} when a table is altered to check if the column type changes if any
* are allowed or not.
*/
public class DefaultIncompatibleTableChangeHandler implements
IMetaStoreIncompatibleChangeHandler {
private static final Logger LOG = LoggerFactory
.getLogger(DefaultIncompatibleTableChangeHandler.class);
private static final DefaultIncompatibleTableChangeHandler INSTANCE =
new DefaultIncompatibleTableChangeHandler();
private DefaultIncompatibleTableChangeHandler() {
}
public static DefaultIncompatibleTableChangeHandler get() {
return INSTANCE;
}
/**
* Checks if the column type changes in the oldTable and newTable are allowed or not. In
* addition to checking if the incompatible changes are allowed or not, this also checks
* if the table serde library belongs to a list of table serdes which support making any
* column type changes.
*
* @param conf The configuration which if incompatible col type changes are allowed
* or not.
* @param oldTable The instance of the table being altered.
* @param newTable The new instance of the table which represents the altered state of
* the table.
* @throws InvalidOperationException
*/
@Override
public void allowChange(Configuration conf, Table oldTable, Table newTable)
throws InvalidOperationException {
if (!MetastoreConf.getBoolVar(conf,
MetastoreConf.ConfVars.DISALLOW_INCOMPATIBLE_COL_TYPE_CHANGES)) {
// incompatible column changes are allowed for all
return;
}
if (oldTable.getTableType().equals(TableType.VIRTUAL_VIEW.toString())) {
// Views derive the column type from the base table definition. So the view
// definition can be altered to change the column types. The column type
// compatibility checks should be done only for non-views.
return;
}
checkColTypeChangeCompatible(conf, oldTable, newTable);
}
private void checkColTypeChangeCompatible(Configuration conf, Table oldTable,
Table newTable) throws InvalidOperationException {
List<FieldSchema> oldCols = oldTable.getSd().getCols();
List<FieldSchema> newCols = newTable.getSd().getCols();
List<String> incompatibleCols = new ArrayList<>();
int maxCols = Math.min(oldCols.size(), newCols.size());
for (int i = 0; i < maxCols; i++) {
if (!ColumnType.areColTypesCompatible(
ColumnType.getTypeName(oldCols.get(i).getType()),
ColumnType.getTypeName(newCols.get(i).getType()))) {
incompatibleCols.add(newCols.get(i).getName());
}
}
if (!incompatibleCols.isEmpty()) {
Collection<String> exceptedTableSerdes = MetastoreConf.getStringCollection(conf,
MetastoreConf.ConfVars.ALLOW_INCOMPATIBLE_COL_TYPE_CHANGES_TABLE_SERDES);
SerDeInfo serDeInfo = oldTable.getSd().getSerdeInfo();
String serializationLib =
serDeInfo == null ? null : serDeInfo.getSerializationLib();
if (exceptedTableSerdes.contains(serializationLib)) {
LOG.info(
"Allowing incompatible column type change of {} for table {}"
+ " since the table serde {} is in excepted list of serdes",
incompatibleCols, (oldTable.getDbName() + "." + oldTable.getTableName()),
serializationLib);
return;
}
throw new InvalidOperationException(
"The following columns have types incompatible with the existing " +
"columns in their respective positions :\n" +
org.apache.commons.lang.StringUtils.join(incompatibleCols, ',')
);
}
}
}