blob: ea16a0a260419b35b1a110e477800db91bd96ffc [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.phoenix.coprocessor;
import com.google.common.collect.Lists;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.ExtendedCellBuilder;
import org.apache.hadoop.hbase.client.Delete;
import org.apache.hadoop.hbase.client.Mutation;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.regionserver.Region;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.Pair;
import org.apache.phoenix.compile.ColumnResolver;
import org.apache.phoenix.compile.FromCompiler;
import org.apache.phoenix.compile.StatementContext;
import org.apache.phoenix.compile.WhereCompiler;
import org.apache.phoenix.coprocessor.MetaDataProtocol.MetaDataMutationResult;
import org.apache.phoenix.expression.Expression;
import org.apache.phoenix.hbase.index.util.ImmutableBytesPtr;
import org.apache.phoenix.jdbc.PhoenixConnection;
import org.apache.phoenix.jdbc.PhoenixDatabaseMetaData;
import org.apache.phoenix.jdbc.PhoenixStatement;
import org.apache.phoenix.parse.ParseNode;
import org.apache.phoenix.parse.SQLParser;
import org.apache.phoenix.schema.ColumnFamilyNotFoundException;
import org.apache.phoenix.schema.ColumnNotFoundException;
import org.apache.phoenix.schema.ColumnRef;
import org.apache.phoenix.schema.PColumn;
import org.apache.phoenix.schema.PTable;
import org.apache.phoenix.schema.PTableType;
import org.apache.phoenix.schema.TableRef;
import org.apache.phoenix.schema.types.PInteger;
import org.apache.phoenix.util.EnvironmentEdgeManager;
import org.apache.phoenix.util.MetaDataUtil;
import org.apache.phoenix.util.QueryUtil;
import org.apache.phoenix.util.SchemaUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import java.util.ListIterator;
import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.COLUMN_NAME_INDEX;
import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.FAMILY_NAME_INDEX;
import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.LINK_TYPE_BYTES;
import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SCHEMA_NAME_INDEX;
import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES;
import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TABLE_NAME_INDEX;
import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TENANT_ID_INDEX;
import static org.apache.phoenix.query.QueryConstants.DIVERGED_VIEW_BASE_COLUMN_COUNT;
import static org.apache.phoenix.util.SchemaUtil.getVarChars;
public class DropColumnMutator implements ColumnMutator {
private List<Pair<PTable, PColumn>> tableAndDroppedColPairs;
private Configuration conf;
private static final Logger logger = LoggerFactory.getLogger(DropColumnMutator.class);
public DropColumnMutator(Configuration conf) {
this.tableAndDroppedColPairs = Lists.newArrayList();
this.conf = conf;
}
@Override
public MutateColumnType getMutateColumnType() {
return MutateColumnType.DROP_COLUMN;
}
/**
* Checks to see if the column being dropped is required by a child view
*/
@Override
public MetaDataMutationResult validateWithChildViews(PTable table, List<PTable> childViews,
List<Mutation> tableMetadata,
byte[] schemaName, byte[] tableName)
throws IOException, SQLException {
List<Delete> columnDeletesForBaseTable = Lists.newArrayListWithExpectedSize(5);
for (Mutation m : tableMetadata) {
if (m instanceof Delete) {
byte[][] rkmd = new byte[5][];
int pkCount = getVarChars(m.getRow(), rkmd);
if (pkCount > COLUMN_NAME_INDEX
&& Bytes.compareTo(schemaName, rkmd[SCHEMA_NAME_INDEX]) == 0
&& Bytes.compareTo(tableName, rkmd[TABLE_NAME_INDEX]) == 0) {
columnDeletesForBaseTable.add((Delete) m);
}
}
}
for (PTable view : childViews) {
for (Delete columnDeleteForBaseTable : columnDeletesForBaseTable) {
PColumn existingViewColumn = null;
byte[][] rkmd = new byte[5][];
getVarChars(columnDeleteForBaseTable.getRow(), rkmd);
String columnName = Bytes.toString(rkmd[COLUMN_NAME_INDEX]);
String columnFamily =
rkmd[FAMILY_NAME_INDEX] == null ? null : Bytes
.toString(rkmd[FAMILY_NAME_INDEX]);
try {
existingViewColumn = columnFamily == null ?
view.getColumnForColumnName(columnName) :
view.getColumnFamily(columnFamily).getPColumnForColumnName(columnName);
} catch (ColumnFamilyNotFoundException e) {
// ignore since it means that the column family is not present for the column to
// be added.
} catch (ColumnNotFoundException e) {
// ignore since it means the column is not present in the view
}
// check if the view where expression contains the column being dropped and prevent
// it
if (existingViewColumn != null && view.getViewStatement() != null) {
ParseNode viewWhere =
new SQLParser(view.getViewStatement()).parseQuery().getWhere();
PhoenixConnection conn = QueryUtil.getConnectionOnServer(conf).unwrap(
PhoenixConnection.class);
PhoenixStatement statement = new PhoenixStatement(conn);
TableRef baseTableRef = new TableRef(view);
ColumnResolver columnResolver = FromCompiler.getResolver(baseTableRef);
StatementContext context = new StatementContext(statement, columnResolver);
Expression whereExpression = WhereCompiler.compile(context, viewWhere);
Expression colExpression =
new ColumnRef(baseTableRef, existingViewColumn.getPosition())
.newColumnExpression();
MetaDataEndpointImpl.ColumnFinder columnFinder =
new MetaDataEndpointImpl.ColumnFinder(colExpression);
whereExpression.accept(columnFinder);
if (columnFinder.getColumnFound()) {
return new MetaDataProtocol.MetaDataMutationResult(
MetaDataProtocol.MutationCode.UNALLOWED_TABLE_MUTATION,
EnvironmentEdgeManager.currentTimeMillis(), table);
}
}
if (existingViewColumn != null) {
tableAndDroppedColPairs.add(new Pair(view, existingViewColumn));
}
}
}
return null;
}
@Override
public MetaDataMutationResult validateAndAddMetadata(PTable table,
byte[][] rowKeyMetaData,
List<Mutation> tableMetaData,
Region region,
List<ImmutableBytesPtr> invalidateList,
List<Region.RowLock> locks,
long clientTimeStamp, ExtendedCellBuilder extendedCellBuilder)
throws SQLException {
byte[] tenantId = rowKeyMetaData[TENANT_ID_INDEX];
byte[] schemaName = rowKeyMetaData[SCHEMA_NAME_INDEX];
byte[] tableName = rowKeyMetaData[TABLE_NAME_INDEX];
boolean isView = table.getType() == PTableType.VIEW;
boolean deletePKColumn = false;
List<Mutation> additionalTableMetaData = Lists.newArrayList();
ListIterator<Mutation> iterator = tableMetaData.listIterator();
while (iterator.hasNext()) {
Mutation mutation = iterator.next();
byte[] key = mutation.getRow();
int pkCount = getVarChars(key, rowKeyMetaData);
if (isView && mutation instanceof Put) {
PColumn column = MetaDataUtil.getColumn(pkCount, rowKeyMetaData, table);
if (column == null)
continue;
// ignore any puts that modify the ordinal positions of columns
iterator.remove();
}
else if (mutation instanceof Delete) {
if (pkCount > COLUMN_NAME_INDEX
&& Bytes.compareTo(schemaName, rowKeyMetaData[SCHEMA_NAME_INDEX]) == 0
&& Bytes.compareTo(tableName, rowKeyMetaData[TABLE_NAME_INDEX]) == 0) {
PColumn columnToDelete = null;
try {
columnToDelete = MetaDataUtil.getColumn(pkCount, rowKeyMetaData, table);
if (columnToDelete == null)
continue;
deletePKColumn = columnToDelete.getFamilyName() == null;
if (isView) {
// if we are dropping a derived column add it to the excluded
// column list
if (columnToDelete.isDerived()) {
mutation = MetaDataUtil.cloneDeleteToPutAndAddColumn((Delete)
mutation, TABLE_FAMILY_BYTES, LINK_TYPE_BYTES,
PTable.LinkType.EXCLUDED_COLUMN.
getSerializedValueAsByteArray());
iterator.set(mutation);
}
if (table.getBaseColumnCount() != DIVERGED_VIEW_BASE_COLUMN_COUNT
&& columnToDelete.isDerived()) {
// If the column being dropped is inherited from the base table,
// then the view is about to diverge itself from the base table.
// The consequence of this divergence is that that any further
// meta-data changes made to the base table will not be
// propagated to the hierarchy of views where this view is the root.
byte[] viewKey = SchemaUtil.getTableKey(tenantId, schemaName,
tableName);
Put updateBaseColumnCountPut = new Put(viewKey);
byte[] baseColumnCountPtr =
new byte[PInteger.INSTANCE.getByteSize()];
PInteger.INSTANCE.getCodec().encodeInt(
DIVERGED_VIEW_BASE_COLUMN_COUNT, baseColumnCountPtr, 0);
updateBaseColumnCountPut.addColumn(
PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
PhoenixDatabaseMetaData.BASE_COLUMN_COUNT_BYTES,
clientTimeStamp,
baseColumnCountPtr);
additionalTableMetaData.add(updateBaseColumnCountPut);
}
}
if (columnToDelete.isViewReferenced()) {
// Disallow deletion of column referenced in WHERE clause of view
return new MetaDataProtocol.MetaDataMutationResult(
MetaDataProtocol.MutationCode.UNALLOWED_TABLE_MUTATION,
EnvironmentEdgeManager.currentTimeMillis(), table,
columnToDelete);
}
// drop any indexes that need the column that is going to be dropped
tableAndDroppedColPairs.add(new Pair(table, columnToDelete));
} catch (ColumnFamilyNotFoundException e) {
return new MetaDataProtocol.MetaDataMutationResult(
MetaDataProtocol.MutationCode.COLUMN_NOT_FOUND,
EnvironmentEdgeManager.currentTimeMillis(), table, columnToDelete);
} catch (ColumnNotFoundException e) {
return new MetaDataProtocol.MetaDataMutationResult(
MetaDataProtocol.MutationCode.COLUMN_NOT_FOUND,
EnvironmentEdgeManager.currentTimeMillis(), table, columnToDelete);
}
}
}
}
tableMetaData.addAll(additionalTableMetaData);
if (deletePKColumn) {
if (table.getPKColumns().size() == 1) {
return new MetaDataProtocol.MetaDataMutationResult(
MetaDataProtocol.MutationCode.NO_PK_COLUMNS,
EnvironmentEdgeManager.currentTimeMillis(), null);
}
}
long currentTime = MetaDataUtil.getClientTimeStamp(tableMetaData);
return new MetaDataProtocol.MetaDataMutationResult(
MetaDataProtocol.MutationCode.TABLE_ALREADY_EXISTS, currentTime, null);
}
@Override
public List<Pair<PTable, PColumn>> getTableAndDroppedColumnPairs() {
return tableAndDroppedColPairs;
}
}