diff --git a/src/main/java/com/alipay/oceanbase/hbase/OHTable.java b/src/main/java/com/alipay/oceanbase/hbase/OHTable.java index 96fd2a62..40c54d52 100644 --- a/src/main/java/com/alipay/oceanbase/hbase/OHTable.java +++ b/src/main/java/com/alipay/oceanbase/hbase/OHTable.java @@ -427,8 +427,9 @@ public Configuration getConfiguration() { } @Override - public HTableDescriptor getTableDescriptor() { - throw new FeatureNotSupportedException("not supported yet."); + public HTableDescriptor getTableDescriptor() throws IOException { + OHTableDescriptorExecutor executor = new OHTableDescriptorExecutor(tableNameString, obTableClient); + return executor.getTableDescriptor(); } /** diff --git a/src/main/java/com/alipay/oceanbase/hbase/util/OHConnectionImpl.java b/src/main/java/com/alipay/oceanbase/hbase/util/OHConnectionImpl.java index 7fd9ffd2..199a6b18 100644 --- a/src/main/java/com/alipay/oceanbase/hbase/util/OHConnectionImpl.java +++ b/src/main/java/com/alipay/oceanbase/hbase/util/OHConnectionImpl.java @@ -153,7 +153,7 @@ public RegionLocator getRegionLocator(TableName tableName) throws IOException { // to avoid change the database in original param url by namespace in tableName OHConnectionConfiguration connectionConf = new OHConnectionConfiguration(conf); ObTableClient obTableClient = ObTableClientManager.getOrCreateObTableClientByTableName(tableName, connectionConf); - OHRegionLocatorExecutor executor = new OHRegionLocatorExecutor(obTableClient); + OHRegionLocatorExecutor executor = new OHRegionLocatorExecutor(tableName.toString(), obTableClient); return executor.getRegionLocator(String.valueOf(tableName)); } diff --git a/src/main/java/com/alipay/oceanbase/hbase/util/OHCreateTableExecutor.java b/src/main/java/com/alipay/oceanbase/hbase/util/OHCreateTableExecutor.java new file mode 100644 index 00000000..4660d092 --- /dev/null +++ b/src/main/java/com/alipay/oceanbase/hbase/util/OHCreateTableExecutor.java @@ -0,0 +1,68 @@ +/*- + * #%L + * OBKV HBase Client Framework + * %% + * Copyright (C) 2025 OceanBase Group + * %% + * OBKV HBase Client Framework is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * #L% + */ + +package com.alipay.oceanbase.hbase.util; + +import com.alibaba.fastjson.JSON; +import com.alipay.oceanbase.hbase.execute.AbstractObTableMetaExecutor; +import com.alipay.oceanbase.rpc.ObTableClient; +import com.alipay.oceanbase.rpc.meta.ObTableMetaRequest; +import com.alipay.oceanbase.rpc.meta.ObTableMetaResponse; +import com.alipay.oceanbase.rpc.meta.ObTableRpcMetaType; +import org.apache.hadoop.hbase.HColumnDescriptor; +import org.apache.hadoop.hbase.HTableDescriptor; + +import java.io.IOException; +import java.util.*; + +public class OHCreateTableExecutor extends AbstractObTableMetaExecutor { + private final ObTableClient client; + + OHCreateTableExecutor(ObTableClient client) { + this.client = client; + } + + @Override + public ObTableRpcMetaType getMetaType() { + return ObTableRpcMetaType.HTABLE_CREATE_TABLE; + } + + @Override + public Void parse(ObTableMetaResponse response) throws IOException { + // success, do nothing + + return null; + } + + public void createTable(HTableDescriptor tableDescriptor, byte[][] splitKeys) throws IOException { + final ObTableMetaRequest request = new ObTableMetaRequest(); + request.setMetaType(getMetaType()); + Map requestData = new HashMap<>(); + requestData.put("htable_name", tableDescriptor.getTableName().getName()); + Map> columnFamilies = new HashMap<>(); + for (HColumnDescriptor columnDescriptor : tableDescriptor.getFamilies()) { + Map columnFamily = new HashMap<>(); + columnFamily.put("ttl", columnDescriptor.getTimeToLive()); + columnFamily.put("max_version", columnDescriptor.getMaxVersions()); + columnFamilies.put(columnDescriptor.getNameAsString(), columnFamily); + } + requestData.put("column_families", columnFamilies); + String jsonData = JSON.toJSONString(requestData); + request.setData(jsonData); + execute(client, request); + } +} diff --git a/src/main/java/com/alipay/oceanbase/hbase/util/OHRegionLocator.java b/src/main/java/com/alipay/oceanbase/hbase/util/OHRegionLocator.java index f4474d92..f21a153d 100644 --- a/src/main/java/com/alipay/oceanbase/hbase/util/OHRegionLocator.java +++ b/src/main/java/com/alipay/oceanbase/hbase/util/OHRegionLocator.java @@ -11,23 +11,44 @@ import java.util.List; public class OHRegionLocator implements RegionLocator { - public OHRegionLocator(byte[][] startKeys, byte[][] endKeys) { - + private byte[][] startKeys; + private byte[][] endKeys; + private TableName tableName; + + private List regionLocations; + + public OHRegionLocator(byte[][] startKeys, byte[][] endKeys, List regionLocations) { + this.startKeys = startKeys; + this.endKeys = endKeys; + this.regionLocations = regionLocations; } @Override public HRegionLocation getRegionLocation(byte[] bytes) throws IOException { + // check if bytes is in the range of startKeys and endKeys + for (HRegionLocation regionLocation : regionLocations) { + if (regionLocation.getRegionInfo().containsRow(bytes)) { + return regionLocation; + } + } return null; } @Override public HRegionLocation getRegionLocation(byte[] bytes, boolean b) throws IOException { - return null; + if (b) { + OHRegionLocatorExecutor executor = new OHRegionLocatorExecutor(tableName.toString(), tableClient); + RegionLocator location = executor.getRegionLocator(tableName.toString()); + this.startKeys = location.getStartKeys(); + this.endKeys = location.getEndKeys(); + this.regionLocations = location.getAllRegionLocations(); + } + return getRegionLocation(bytes); } @Override public List getAllRegionLocations() throws IOException { - return Collections.emptyList(); + return regionLocations; } /** @@ -40,7 +61,7 @@ public List getAllRegionLocations() throws IOException { */ @Override public byte[][] getStartKeys() throws IOException { - return null; + return startKeys; } /** @@ -53,7 +74,7 @@ public byte[][] getStartKeys() throws IOException { */ @Override public byte[][] getEndKeys() throws IOException { - return null; + return endKeys; } /** @@ -67,18 +88,18 @@ public byte[][] getEndKeys() throws IOException { */ @Override public Pair getStartEndKeys() throws IOException { - return null; + return Pair.newPair(startKeys, endKeys); } @Override public TableName getName() { - return null; + return tableName; } private ObTableClient tableClient; @Override public void close() throws IOException { - + return; } -} +} \ No newline at end of file diff --git a/src/main/java/com/alipay/oceanbase/hbase/util/OHRegionLocatorExecutor.java b/src/main/java/com/alipay/oceanbase/hbase/util/OHRegionLocatorExecutor.java index 0f896827..0b366e6a 100644 --- a/src/main/java/com/alipay/oceanbase/hbase/util/OHRegionLocatorExecutor.java +++ b/src/main/java/com/alipay/oceanbase/hbase/util/OHRegionLocatorExecutor.java @@ -1,49 +1,212 @@ package com.alipay.oceanbase.hbase.util; import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; import com.alipay.oceanbase.hbase.execute.AbstractObTableMetaExecutor; import com.alipay.oceanbase.rpc.ObTableClient; +import com.alipay.oceanbase.rpc.constant.Constants; import com.alipay.oceanbase.rpc.exception.ObTableException; import com.alipay.oceanbase.rpc.location.model.TableEntry; import com.alipay.oceanbase.rpc.meta.ObTableMetaRequest; import com.alipay.oceanbase.rpc.meta.ObTableMetaResponse; import com.alipay.oceanbase.rpc.meta.ObTableRpcMetaType; +import org.apache.hadoop.hbase.*; import java.io.IOException; -import java.util.HashMap; -import java.util.Map; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; public class OHRegionLocatorExecutor extends AbstractObTableMetaExecutor { - OHRegionLocatorExecutor(ObTableClient client) { + private final String tableName; + private final ObTableClient client; + + OHRegionLocatorExecutor(String tableName, ObTableClient client) { + this.tableName = tableName; this.client = client; } - + @Override public ObTableRpcMetaType getMetaType() { return ObTableRpcMetaType.HTABLE_REGION_LOCATOR; } + /** + * Parses the response and creates a region locator + * @param response response from the server + * @return OHRegionLocator + * @throws IOException if failed to parse the response + */ @Override public OHRegionLocator parse(ObTableMetaResponse response) throws IOException { try { - String jsonData = response.getData(); - // process json - return new OHRegionLocator(null, null); + final String jsonData = response.getData(); + final JSONObject jsonMap = Optional.ofNullable(JSON.parseObject(jsonData)) + .orElseThrow(() -> new IOException("jsonMap is null")); + /* + { + "table_id_dict": [1001, 1002], + "replica_dict": [ + ["127.0.0.1", 2881], + ["127.0.0.2", 2882], + ["127.0.0.3", 2883] + ], + "partitions": [ + // 表1001的3个分区,每个分区3副本 + [0, 50001, "rowkey_1", 0, 1], // leader + [0, 50001, "rowkey_1", 1, 0], // follower + [0, 50001, "rowkey_1", 2, 0], // follower + [0, 50002, "rowkey_2", 0, 1], + [0, 50002, "rowkey_2", 1, 0], + [0, 50002, "rowkey_2", 2, 0], + [0, 50003, "rowkey_3", 0, 1], + [0, 50003, "rowkey_3", 1, 0], + [0, 50003, "rowkey_3", 2, 0], + + // 表1002的3个分区,每个分区3副本 + [1, 50004, "rowkey_1", 0, 1], + [1, 50004, "rowkey_1", 1, 0], + [1, 50004, "rowkey_1", 2, 0], + [1, 50005, "rowkey_2", 0, 1], + [1, 50005, "rowkey_2", 1, 0], + [1, 50005, "rowkey_2", 2, 0], + [1, 50006, "rowkey_3", 0, 1], + [1, 50006, "rowkey_3", 1, 0], + [1, 50006, "rowkey_3", 2, 0] + ] + } + */ + + final List partitions = Optional.>ofNullable(jsonMap.getJSONArray("partitions")) + .orElseThrow(() -> new IOException("partitions is null")); + + final List tableIdDict = Optional.>ofNullable(jsonMap.getJSONArray("table_id_dict")) + .orElseThrow(() -> new IOException("tableIdDict is null")); + final List replicaDict = Optional.>ofNullable(jsonMap.getJSONArray("replica_dict")) + .orElseThrow(() -> new IOException("replicaDict is null")); + + final boolean isHashLikePartition = partitions.stream() + .map(obj -> (List) obj) + .filter(partition -> { + if (partition.size() <= 3) { + throw new IllegalArgumentException("partition size is not 3"); + } + return true; + }) + .allMatch(partition -> { + final byte[] highBound = partition.get(2).toString().getBytes(); + return Arrays.equals(highBound, Constants.EMPTY_STRING.getBytes()); + }); + return isHashLikePartition ? + createHashPartitionLocator(tableIdDict, replicaDict, partitions) : + createRangePartitionLocator(tableIdDict, replicaDict, partitions); } catch (IllegalArgumentException e) { - throw new IOException("msg", e); + throw new IOException("Invalid partition data: " + e.getMessage(), e); } } - public OHRegionLocator getRegionLocator(String tableName) throws IOException { - ObTableMetaRequest request = new ObTableMetaRequest(); + /** + * Creates a region locator for range partitions + * @param tableIdDict table ID dictionary + * @param replicaDict replica dictionary + * @param partitions list of partition data + * @return OHRegionLocator for range partitions + */ + private OHRegionLocator createRangePartitionLocator( + final List tableIdDict, + final List replicaDict, + final List partitions) { + final int partitionCount = partitions.size(); + final int regionCount = partitionCount + 1; + final byte[][] startKeys = new byte[regionCount][]; + final byte[][] endKeys = new byte[regionCount][]; + + for (int i = 0; i < regionCount; i++) { + if (i == 0) { + startKeys[i] = HConstants.EMPTY_BYTE_ARRAY; + endKeys[i] = ((List) partitions.get(i)).get(2).toString().getBytes(); + } else if (i == regionCount - 1) { + startKeys[i] = ((List) partitions.get(i - 1)).get(2).toString().getBytes(); + endKeys[i] = HConstants.EMPTY_BYTE_ARRAY; + } else { + startKeys[i] = ((List) partitions.get(i - 1)).get(2).toString().getBytes(); + endKeys[i] = ((List) partitions.get(i)).get(2).toString().getBytes(); + } + } + + // Create region locations for all regions + final List regionLocations = IntStream.range(0, regionCount) + .mapToObj(i -> { + final List partition = (List) partitions.get(Math.min(i, partitionCount - 1)); + final int replicationIdx = (int) partition.get(3); + final List hostInfo = (List) replicaDict.get(replicationIdx); + + final ServerName serverName = ServerName.valueOf( + hostInfo.get(0).toString(), + (int) hostInfo.get(1), + i + ); + final HRegionInfo regionInfo = new HRegionInfo( + TableName.valueOf(tableName), + startKeys[i], + endKeys[i] + ); + return new HRegionLocation(regionInfo, serverName, i); + }) + .collect(Collectors.toList()); + + return new OHRegionLocator(startKeys, endKeys, regionLocations); + } + + /** + * Creates a region locator for hash partitions + * @param tableIdDict table ID dictionary + * @param replicaDict replica dictionary + * @param partitions list of partition data + * @return OHRegionLocator for hash partitions + */ + private OHRegionLocator createHashPartitionLocator( + final List tableIdDict, + final List replicaDict, + final List partitions) { + + final byte[][] startKeys = new byte[1][]; + final byte[][] endKeys = new byte[1][]; + startKeys[0] = HConstants.EMPTY_BYTE_ARRAY; + endKeys[0] = HConstants.EMPTY_BYTE_ARRAY; + + final List regionLocations = IntStream.range(0, partitions.size()) + .mapToObj(i -> { + final List partition = (List) partitions.get(i); + final int replicationIdx = (int) partition.get(3); + final List hostInfo = (List) replicaDict.get(replicationIdx); + + final ServerName serverName = ServerName.valueOf( + hostInfo.get(0).toString(), + (int) hostInfo.get(1), + i + ); + final HRegionInfo regionInfo = new HRegionInfo( + TableName.valueOf(tableName), + startKeys[0], + endKeys[0] + ); + return new HRegionLocation(regionInfo, serverName, i); + }) + .collect(Collectors.toList()); + + return new OHRegionLocator(startKeys, endKeys, regionLocations); + } + + public OHRegionLocator getRegionLocator(final String tableName) throws IOException { + final ObTableMetaRequest request = new ObTableMetaRequest(); request.setMetaType(getMetaType()); - Map requestData = new HashMap<>(); + final Map requestData = new HashMap<>(); requestData.put("table_name", tableName); - String jsonData = JSON.toJSONString(requestData); + + final String jsonData = JSON.toJSONString(requestData); request.setData(jsonData); return execute(client, request); } - - private final ObTableClient client; } diff --git a/src/main/java/com/alipay/oceanbase/hbase/util/OHTableDescriptorExecutor.java b/src/main/java/com/alipay/oceanbase/hbase/util/OHTableDescriptorExecutor.java new file mode 100644 index 00000000..68bcbd13 --- /dev/null +++ b/src/main/java/com/alipay/oceanbase/hbase/util/OHTableDescriptorExecutor.java @@ -0,0 +1,86 @@ +package com.alipay.oceanbase.hbase.util; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.alipay.oceanbase.hbase.execute.AbstractObTableMetaExecutor; +import com.alipay.oceanbase.rpc.ObTableClient; +import com.alipay.oceanbase.rpc.meta.ObTableMetaRequest; +import com.alipay.oceanbase.rpc.meta.ObTableMetaResponse; +import com.alipay.oceanbase.rpc.meta.ObTableRpcMetaType; +import org.apache.hadoop.hbase.HColumnDescriptor; +import org.apache.hadoop.hbase.HTableDescriptor; +import org.apache.hadoop.hbase.TableName; +import sun.font.SunFontManager; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +public class OHTableDescriptorExecutor extends AbstractObTableMetaExecutor { + private final String tableName; + private final ObTableClient client; + + public OHTableDescriptorExecutor(String tableName, ObTableClient client) { + this.tableName = tableName; + this.client = client; + } + + @Override + public HTableDescriptor parse(ObTableMetaResponse response) throws IOException { + try { + final String jsonData = response.getData(); + final JSONObject jsonMap = Optional.ofNullable(JSON.parseObject(jsonData)) + .orElseThrow(() -> new IOException("jsonMap is null")); + /* + { + "cfDesc": { + "cf1": { + "TTL":604800 + }, + "cf2": { + "TTL":259200 + } + }, + "tbDesc": { + "name":"test" + } + } + */ + HTableDescriptor tableDescriptor = new HTableDescriptor(TableName.valueOf(tableName)); + JSONObject cfDesc = jsonMap.getJSONObject("cfDescs"); + if (cfDesc != null) { + for (Map.Entry entry : cfDesc.entrySet()) { + String cfName = entry.getKey(); + JSONObject attributes = (JSONObject) entry.getValue(); + HColumnDescriptor cf = new HColumnDescriptor(cfName); + cf.setTimeToLive(attributes.getIntValue("TTL")); + tableDescriptor.addFamily(cf); + } + } else { + throw new IOException("cfDesc is null"); + } + return tableDescriptor; + } catch (IllegalArgumentException e) { + throw new IOException("Failed to parse response", e); + } + } + + @Override + public ObTableRpcMetaType getMetaType() throws IOException { + return ObTableRpcMetaType.HTABLE_GET_DESC; + } + + + public HTableDescriptor getTableDescriptor() throws IOException { + final ObTableMetaRequest request = new ObTableMetaRequest(); + request.setMetaType(getMetaType()); + final Map requestData = new HashMap<>(); + requestData.put("table_name", tableName); + + final String jsonData = JSON.toJSONString(requestData); + request.setData(jsonData); + + return execute(client, request); + } +} diff --git a/src/test/java/com/alipay/oceanbase/hbase/OHConnectionTest.java b/src/test/java/com/alipay/oceanbase/hbase/OHConnectionTest.java index 1fa79204..fba40f6b 100644 --- a/src/test/java/com/alipay/oceanbase/hbase/OHConnectionTest.java +++ b/src/test/java/com/alipay/oceanbase/hbase/OHConnectionTest.java @@ -17,11 +17,16 @@ package com.alipay.oceanbase.hbase; +import com.alibaba.fastjson.JSON; import com.alipay.oceanbase.hbase.util.ObHTableTestUtil; import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.HColumnDescriptor; +import org.apache.hadoop.hbase.HTableDescriptor; +import org.apache.hadoop.hbase.HRegionLocation; import org.apache.hadoop.hbase.KeyValue; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.client.*; +import org.apache.hadoop.hbase.client.RegionLocator; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.Threads; import org.junit.*; @@ -30,9 +35,12 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.StreamSupport; +import java.util.stream.Stream; +import java.util.Optional; -import static com.alipay.oceanbase.hbase.constants.OHConstants.SOCKET_TIMEOUT; -import static org.apache.hadoop.hbase.ipc.RpcClient.SOCKET_TIMEOUT_CONNECT; import static org.apache.hadoop.hbase.util.Bytes.toBytes; import static org.junit.Assert.*; @@ -44,7 +52,7 @@ public class OHConnectionTest { public void testConnectionBySet() throws Exception { Configuration c = ObHTableTestUtil.newConfiguration(); c.set(ClusterConnection.HBASE_CLIENT_CONNECTION_IMPL, - "com.alipay.oceanbase.hbase.util.OHConnectionImpl"); + "com.alipay.oceanbase.hbase.util.OHConnectionImpl"); c.set("rs.list.acquire.read.timeout", "10000"); // test set rpc connection timeout, the first one is the latest version c.set(SOCKET_TIMEOUT_CONNECT, "15000"); @@ -100,7 +108,9 @@ public void testNew() throws Exception { @After public void after() throws IOException { - hTable.close(); + if (hTable != null) { + hTable.close(); + } } private void testBasic() throws Exception { @@ -775,4 +785,172 @@ public void testBufferedMutatorConcurrent() throws Exception { } } } + + /* + CREATE TABLEGROUP test_region_locator SHARDING = 'ADAPTIVE'; + CREATE TABLE `test_region_locator$family_region_locator` ( + `K` varbinary(1024) NOT NULL, + `Q` varbinary(256) NOT NULL, + `T` bigint(20) NOT NULL, + `V` varbinary(1024) DEFAULT NULL, + PRIMARY KEY (`K`, `Q`, `T`) + ) TABLEGROUP = test_region_locator PARTITION BY RANGE COLUMNS(K) ( + PARTITION p1 VALUES LESS THAN ('c'), + PARTITION p2 VALUES LESS THAN ('e'), + PARTITION p3 VALUES LESS THAN ('g'), + PARTITION p4 VALUES LESS THAN ('i'), + PARTITION p5 VALUES LESS THAN ('l'), + PARTITION p6 VALUES LESS THAN ('n'), + PARTITION p7 VALUES LESS THAN ('p'), + PARTITION p8 VALUES LESS THAN ('s'), + PARTITION p9 VALUES LESS THAN ('v'), + PARTITION p10 VALUES LESS THAN (MAXVALUE) + ); + */ + @Test + public void testRangePartitionWithRegionLocator() throws Exception { + final String tableNameStr = "test_region_locator"; + final String family = "family_region_locator"; + final int regionCount = 10; + final int rowsPerRegion = 5; + + final byte[][] splitPoints = new byte[][] { + Bytes.toBytes("c"), // p1: < 'c' + Bytes.toBytes("e"), // p2: < 'e' + Bytes.toBytes("g"), // p3: < 'g' + Bytes.toBytes("i"), // p4: < 'i' + Bytes.toBytes("l"), // p5: < 'l' + Bytes.toBytes("n"), // p6: < 'n' + Bytes.toBytes("p"), // p7: < 'p' + Bytes.toBytes("s"), // p8: < 's' + Bytes.toBytes("v") // p9: < 'v' + }; + + final TableName tableName = TableName.valueOf(tableNameStr); + final Configuration conf = ObHTableTestUtil.newConfiguration(); + connection = ConnectionFactory.createConnection(conf); + hTable = connection.getTable(tableName); + + try { + for (int i = 0; i < regionCount; i++) { + for (int j = 0; j < rowsPerRegion; j++) { + String rowKey; + if (i == 0) { + rowKey = "a" + j; + } else if (i == regionCount - 1) { + rowKey = "v" + j; + } else { + String baseKey = Bytes.toString(splitPoints[i - 1]); + rowKey = baseKey + j; + } + + Put put = new Put(Bytes.toBytes(rowKey)); + String value = String.format("value_%d_%d", i, j); + put.addColumn( + Bytes.toBytes(family), + Bytes.toBytes("q"), + Bytes.toBytes(value) + ); + hTable.put(put); + } + } + + try (RegionLocator locator = connection.getRegionLocator(tableName)) { + byte[][] startKeys = locator.getStartKeys(); + byte[][] endKeys = locator.getEndKeys(); + + Assert.assertEquals("Should have " + regionCount + " regions", + regionCount, startKeys.length); + + for (int i = 0; i < startKeys.length; i++) { + String startKeyStr = startKeys[i].length == 0 ? "-∞" : + Bytes.toString(startKeys[i]); + String endKeyStr = endKeys[i].length == 0 ? "+∞" : + Bytes.toString(endKeys[i]); + // 验证region边界 + if (i > 0) { + // 验证startKey与前一个endKey相同 + Assert.assertArrayEquals("Region " + i + " startKey should match previous endKey", + endKeys[i-1], startKeys[i]); + } + } + + for (int i = 0; i < startKeys.length; i++) { + Scan scan = new Scan(); + if (startKeys[i].length > 0) { + scan.setStartRow(startKeys[i]); + } + if (endKeys[i].length > 0) { + scan.setStopRow(endKeys[i]); + } + + String startKeyStr = startKeys[i].length == 0 ? "-∞" : + Bytes.toString(startKeys[i]); + String endKeyStr = endKeys[i].length == 0 ? "+∞" : + Bytes.toString(endKeys[i]); + + try (ResultScanner scanner = hTable.getScanner(scan)) { + List results = new ArrayList<>(); + for (Result result : scanner) { + results.add(result); + } + + Assert.assertEquals("Region " + i + " should have " + rowsPerRegion + " rows", + rowsPerRegion, results.size()); + + for (Result result : results) { + String rowKey = Bytes.toString(result.getRow()); + String value = Bytes.toString(result.getValue( + Bytes.toBytes(family), Bytes.toBytes("q"))); + if (startKeys[i].length > 0) { + Assert.assertTrue("Row key " + rowKey + " should be >= " + + Bytes.toString(startKeys[i]), + rowKey.compareTo(Bytes.toString(startKeys[i])) >= 0); + } + if (endKeys[i].length > 0) { + Assert.assertTrue("Row key " + rowKey + " should be < " + + Bytes.toString(endKeys[i]), + rowKey.compareTo(Bytes.toString(endKeys[i])) < 0); + } + } + } + } + } + } finally { + Optional.ofNullable(hTable).ifPresent(table -> { + try { + table.close(); + } catch (IOException e) { + e.printStackTrace(); + } + }); + Optional.ofNullable(connection).ifPresent(conn -> { + try { + conn.close(); + } catch (IOException e) { + e.printStackTrace(); + } + }); + } + } + + + @Test + public void testCreateTable() throws Exception { + final Configuration conf = ObHTableTestUtil.newConfiguration(); + connection = ConnectionFactory.createConnection(conf); + Admin admin = connection.getAdmin(); + HTableDescriptor descriptor = new HTableDescriptor(); + descriptor.setName("test".getBytes()); + HColumnDescriptor cf1Desc = new HColumnDescriptor("family1".getBytes()); + cf1Desc.setTimeToLive(100); + cf1Desc.setMaxVersions(20); + HColumnDescriptor cf2Desc = new HColumnDescriptor("family2".getBytes()); + HColumnDescriptor cf3Desc = new HColumnDescriptor("family3".getBytes()); + cf3Desc.setTimeToLive(30); + descriptor.addFamily(cf1Desc); + descriptor.addFamily(cf2Desc); + descriptor.addFamily(cf3Desc); + admin.createTable(descriptor); + } } diff --git a/src/test/java/com/alipay/oceanbase/hbase/OHTableClientTest.java b/src/test/java/com/alipay/oceanbase/hbase/OHTableClientTest.java index 76ddce12..abeffdf9 100644 --- a/src/test/java/com/alipay/oceanbase/hbase/OHTableClientTest.java +++ b/src/test/java/com/alipay/oceanbase/hbase/OHTableClientTest.java @@ -18,6 +18,7 @@ package com.alipay.oceanbase.hbase; import com.alipay.oceanbase.hbase.util.ObHTableTestUtil; +import org.apache.hadoop.hbase.HTableDescriptor; import org.junit.*; import java.io.IOException; @@ -76,6 +77,64 @@ public void testNew() throws Exception { assertTrue(true); } + + /* + CREATE TABLEGROUP test_desc SHARDING = 'ADAPTIVE'; + CREATE TABLE `test_desc$family1` ( + `K` varbinary(1024) NOT NULL, + `Q` varbinary(256) NOT NULL, + `T` bigint(20) NOT NULL, + `V` varbinary(1024) DEFAULT NULL, + PRIMARY KEY (`K`, `Q`, `T`) + ) TABLEGROUP = test_desc PARTITION BY RANGE COLUMNS(K) ( + PARTITION p1 VALUES LESS THAN ('c'), + PARTITION p2 VALUES LESS THAN ('e'), + PARTITION p3 VALUES LESS THAN ('g'), + PARTITION p4 VALUES LESS THAN ('i'), + PARTITION p5 VALUES LESS THAN ('l'), + PARTITION p6 VALUES LESS THAN ('n'), + PARTITION p7 VALUES LESS THAN ('p'), + PARTITION p8 VALUES LESS THAN ('s'), + PARTITION p9 VALUES LESS THAN ('v'), + PARTITION p10 VALUES LESS THAN (MAXVALUE) + ); + + CREATE TABLE `test_desc$family2` ( + `K` varbinary(1024) NOT NULL, + `Q` varbinary(256) NOT NULL, + `T` bigint(20) NOT NULL, + `V` varbinary(1024) DEFAULT NULL, + PRIMARY KEY (`K`, `Q`, `T`) + ) TABLEGROUP = test_desc PARTITION BY RANGE COLUMNS(K) ( + PARTITION p1 VALUES LESS THAN ('c'), + PARTITION p2 VALUES LESS THAN ('e'), + PARTITION p3 VALUES LESS THAN ('g'), + PARTITION p4 VALUES LESS THAN ('i'), + PARTITION p5 VALUES LESS THAN ('l'), + PARTITION p6 VALUES LESS THAN ('n'), + PARTITION p7 VALUES LESS THAN ('p'), + PARTITION p8 VALUES LESS THAN ('s'), + PARTITION p9 VALUES LESS THAN ('v'), + PARTITION p10 VALUES LESS THAN (MAXVALUE) + ); + */ + @Test + public void testGetTableDescriptor() throws Exception { + final String tableNameStr = "test_desc"; + + OHTableClient hTable2 = ObHTableTestUtil.newOHTableClient(tableNameStr); + hTable2.init(); + try { + HTableDescriptor descriptor = hTable2.getTableDescriptor(); + Assert.assertNotNull(descriptor); + Assert.assertTrue(descriptor.hasFamily("family1".getBytes())); + Assert.assertTrue(descriptor.hasFamily("family2".getBytes())); + Assert.assertFalse(descriptor.hasFamily("family".getBytes())); + } finally { + hTable2.close(); + } + } + @AfterClass public static void finish() throws Exception { try { diff --git a/src/test/java/unit_test_db.sql b/src/test/java/unit_test_db.sql index 7ddd9c33..9ebee6eb 100644 --- a/src/test/java/unit_test_db.sql +++ b/src/test/java/unit_test_db.sql @@ -286,3 +286,62 @@ CREATE TABLE `n1:test$partitionFamily1` ( PRIMARY KEY (`K`, `Q`, `T`) ) partition by key(`K`) partitions 17; +CREATE TABLEGROUP test_region_locator SHARDING = 'ADAPTIVE'; +CREATE TABLE `test_region_locator$family_region_locator` ( + `K` varbinary(1024) NOT NULL, + `Q` varbinary(256) NOT NULL, + `T` bigint(20) NOT NULL, + `V` varbinary(1024) DEFAULT NULL, + PRIMARY KEY (`K`, `Q`, `T`) +) TABLEGROUP = test_region_locator PARTITION BY RANGE COLUMNS(K) ( + PARTITION p1 VALUES LESS THAN ('c'), + PARTITION p2 VALUES LESS THAN ('e'), + PARTITION p3 VALUES LESS THAN ('g'), + PARTITION p4 VALUES LESS THAN ('i'), + PARTITION p5 VALUES LESS THAN ('l'), + PARTITION p6 VALUES LESS THAN ('n'), + PARTITION p7 VALUES LESS THAN ('p'), + PARTITION p8 VALUES LESS THAN ('s'), + PARTITION p9 VALUES LESS THAN ('v'), + PARTITION p10 VALUES LESS THAN (MAXVALUE) +); + +CREATE TABLEGROUP test_desc SHARDING = 'ADAPTIVE'; + +CREATE TABLE `test_desc$family1` ( + `K` varbinary(1024) NOT NULL, + `Q` varbinary(256) NOT NULL, + `T` bigint(20) NOT NULL, + `V` varbinary(1024) DEFAULT NULL, + PRIMARY KEY (`K`, `Q`, `T`) +) TABLEGROUP = test_desc PARTITION BY RANGE COLUMNS(K) ( + PARTITION p1 VALUES LESS THAN ('c'), + PARTITION p2 VALUES LESS THAN ('e'), + PARTITION p3 VALUES LESS THAN ('g'), + PARTITION p4 VALUES LESS THAN ('i'), + PARTITION p5 VALUES LESS THAN ('l'), + PARTITION p6 VALUES LESS THAN ('n'), + PARTITION p7 VALUES LESS THAN ('p'), + PARTITION p8 VALUES LESS THAN ('s'), + PARTITION p9 VALUES LESS THAN ('v'), + PARTITION p10 VALUES LESS THAN (MAXVALUE) +); + +CREATE TABLE `test_desc$family2` ( + `K` varbinary(1024) NOT NULL, + `Q` varbinary(256) NOT NULL, + `T` bigint(20) NOT NULL, + `V` varbinary(1024) DEFAULT NULL, + PRIMARY KEY (`K`, `Q`, `T`) +) TABLEGROUP = test_desc PARTITION BY RANGE COLUMNS(K) ( + PARTITION p1 VALUES LESS THAN ('c'), + PARTITION p2 VALUES LESS THAN ('e'), + PARTITION p3 VALUES LESS THAN ('g'), + PARTITION p4 VALUES LESS THAN ('i'), + PARTITION p5 VALUES LESS THAN ('l'), + PARTITION p6 VALUES LESS THAN ('n'), + PARTITION p7 VALUES LESS THAN ('p'), + PARTITION p8 VALUES LESS THAN ('s'), + PARTITION p9 VALUES LESS THAN ('v'), + PARTITION p10 VALUES LESS THAN (MAXVALUE) +);