From 181088b6e092915bc474f307c1bd745f2ea56f2d Mon Sep 17 00:00:00 2001 From: Xuesen Liang Date: Sat, 7 Jan 2017 14:09:33 +0800 Subject: [PATCH] HBASE-13652 Case-insensitivity of file system affects table creation --- .../master/procedure/CreateTableProcedure.java | 44 +++++++++++++++++++++- .../master/procedure/TestCreateTableProcedure.java | 29 ++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/CreateTableProcedure.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/CreateTableProcedure.java index 0d24f51..a7952fc 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/CreateTableProcedure.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/CreateTableProcedure.java @@ -24,18 +24,23 @@ import java.io.OutputStream; import java.util.ArrayList; import java.util.List; +import org.apache.commons.lang.mutable.MutableBoolean; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.fs.LocalFileSystem; import org.apache.hadoop.fs.Path; import org.apache.hadoop.hbase.DoNotRetryIOException; import org.apache.hadoop.hbase.HRegionInfo; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.MetaTableAccessor; +import org.apache.hadoop.hbase.MetaTableAccessor.QueryType; +import org.apache.hadoop.hbase.MetaTableAccessor.Visitor; import org.apache.hadoop.hbase.TableExistsException; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.classification.InterfaceAudience; import org.apache.hadoop.hbase.client.RegionReplicaUtil; +import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.client.TableState; import org.apache.hadoop.hbase.master.AssignmentManager; import org.apache.hadoop.hbase.master.MasterCoprocessorHost; @@ -44,10 +49,12 @@ import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos; import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos; import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.CreateTableState; +import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.FSTableDescriptors; import org.apache.hadoop.hbase.util.FSUtils; import org.apache.hadoop.hbase.util.ModifyRegionUtils; import org.apache.hadoop.hbase.util.ServerRegionReplicaUtil; +import org.apache.hadoop.util.Shell; import com.google.common.collect.Lists; @@ -237,7 +244,42 @@ public class CreateTableProcedure return false; } - return true; + return !tableExistOnCaseInsensitiveFs(env); + } + + private boolean tableExistOnCaseInsensitiveFs(MasterProcedureEnv env) + throws IOException { + + FileSystem masterFileSystem = env.getMasterServices().getMasterFileSystem().getFileSystem(); + if (!(masterFileSystem instanceof LocalFileSystem && Shell.MAC)) { + return false; + } + + final MutableBoolean tableExistIgnoreCase = new MutableBoolean(false); + String tableNameString = getTableName().getNameAsString(); + MetaTableAccessor.scanMeta(env.getMasterServices().getConnection(), + Bytes.toBytes(tableNameString.toUpperCase()), + Bytes.toBytes(tableNameString.toLowerCase()), + QueryType.ALL, + new Visitor() { + @Override + public boolean visit(Result rowResult) { + HRegionInfo info = MetaTableAccessor.getHRegionInfo(rowResult); + if (info != null && tableNameString.equalsIgnoreCase(info.getTable().getNameAsString())) { + tableExistIgnoreCase.setValue(true);; + return false; + } + return true; + } + }); + + if (tableExistIgnoreCase.isTrue()) { + setFailure("master-create-table", + new TableExistsException(getTableName() + " (ignore case) already exists.")); + return true; + } + + return false; } private void preCreate(final MasterProcedureEnv env) diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestCreateTableProcedure.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestCreateTableProcedure.java index 5c3d913..e6f67c8 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestCreateTableProcedure.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestCreateTableProcedure.java @@ -18,7 +18,10 @@ package org.apache.hadoop.hbase.master.procedure; +import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.DoNotRetryIOException; +import org.apache.hadoop.hbase.HBaseConfiguration; +import org.apache.hadoop.hbase.HBaseTestingUtility; import org.apache.hadoop.hbase.HRegionInfo; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.ProcedureInfo; @@ -30,11 +33,14 @@ import org.apache.hadoop.hbase.testclassification.MasterTests; import org.apache.hadoop.hbase.testclassification.MediumTests; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.ModifyRegionUtils; +import org.apache.hadoop.hdfs.MiniDFSCluster; +import org.apache.hadoop.util.Shell; import org.junit.Test; import org.junit.experimental.categories.Category; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; @Category({MasterTests.class, MediumTests.class}) public class TestCreateTableProcedure extends TestTableDDLProcedureBase { @@ -107,6 +113,29 @@ public class TestCreateTableProcedure extends TestTableDDLProcedureBase { } @Test(timeout=60000) + public void testCreateExistingIgnoreCase() throws Exception { + if (!Shell.MAC) { + return; + } + HBaseTestingUtility UTIL = new HBaseTestingUtility(); + try { + Configuration dfsConf = HBaseConfiguration.create(); + dfsConf.set("fs.defaultFS", "file:///"); + MiniDFSCluster.Builder dfsClusterBuilder = new MiniDFSCluster.Builder(dfsConf); + UTIL.setDFSCluster(dfsClusterBuilder.build()); + UTIL.getConfiguration().set("fs.defaultFS", "file:///"); + UTIL.startMiniCluster(1); + UTIL.createTable(TableName.valueOf("testCaseInsensitive"), "f"); + UTIL.createTable(TableName.valueOf("testCaseInsensiTIVE"), "f"); + fail("Should throw TableExistsException."); + } catch (TableExistsException e) { + assertEquals(e.getMessage(), "testCaseInsensiTIVE (ignore case) already exists."); + } finally { + UTIL.shutdownMiniCluster(); + } + } + + @Test(timeout=60000) public void testRecoveryAndDoubleExecution() throws Exception { final TableName tableName = TableName.valueOf("testRecoveryAndDoubleExecution"); -- 2.10.0