From d696b7275720c53c4985d2e829f4bf2a8748c7fa 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 | 16 ++++++++ .../procedure/TestTableDDLProcedureBase.java | 7 ++++ 3 files changed, 66 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..0dd92e2 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 @@ -30,11 +30,13 @@ 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.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 +109,20 @@ public class TestCreateTableProcedure extends TestTableDDLProcedureBase { } @Test(timeout=60000) + public void testCreateExistingIgnoreCase() throws Exception { + if (!Shell.MAC) { + return; + } + try { + 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."); + } + } + + @Test(timeout=60000) public void testRecoveryAndDoubleExecution() throws Exception { final TableName tableName = TableName.valueOf("testRecoveryAndDoubleExecution"); diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestTableDDLProcedureBase.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestTableDDLProcedureBase.java index a0b69b9..1146bea 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestTableDDLProcedureBase.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestTableDDLProcedureBase.java @@ -21,10 +21,12 @@ package org.apache.hadoop.hbase.master.procedure; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.HBaseConfiguration; import org.apache.hadoop.hbase.HBaseTestingUtility; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.procedure2.ProcedureExecutor; import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility; +import org.apache.hadoop.hdfs.MiniDFSCluster; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; @@ -37,10 +39,15 @@ public abstract class TestTableDDLProcedureBase { private static void setupConf(Configuration conf) { conf.setInt(MasterProcedureConstants.MASTER_PROCEDURE_THREADS, 1); + conf.set("fs.defaultFS", "file:///"); } @BeforeClass public static void setupCluster() throws Exception { + Configuration dfsConf = HBaseConfiguration.create(); + dfsConf.set("fs.defaultFS", "file:///"); + MiniDFSCluster.Builder dfsClusterBuilder = new MiniDFSCluster.Builder(dfsConf); + UTIL.setDFSCluster(dfsClusterBuilder.build()); setupConf(UTIL.getConfiguration()); UTIL.startMiniCluster(1); } -- 2.10.0