Repository: phoenix Updated Branches: refs/heads/4.x-HBase-0.98 2a867a84a -> d1b2e1696
PHOENIX-4259 Split up IndexExtendedIT Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/d1b2e169 Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/d1b2e169 Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/d1b2e169 Branch: refs/heads/4.x-HBase-0.98 Commit: d1b2e16963085fe070f52091ab2b8d1c63d87bd9 Parents: 2a867a8 Author: Thomas D'Silva <[email protected]> Authored: Fri Sep 29 12:34:26 2017 -0700 Committer: Thomas D'Silva <[email protected]> Committed: Fri Sep 29 14:18:03 2017 -0700 ---------------------------------------------------------------------- .../apache/phoenix/end2end/IndexExtendedIT.java | 425 +------------------ .../org/apache/phoenix/end2end/IndexToolIT.java | 269 ++++++++++++ .../phoenix/end2end/LocalIndexSplitMergeIT.java | 276 ++++++++++++ 3 files changed, 568 insertions(+), 402 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/phoenix/blob/d1b2e169/phoenix-core/src/it/java/org/apache/phoenix/end2end/IndexExtendedIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/IndexExtendedIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/IndexExtendedIT.java index a6b7249..5b6f753 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/IndexExtendedIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/IndexExtendedIT.java @@ -26,36 +26,21 @@ import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; -import java.sql.SQLException; import java.sql.Statement; -import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Properties; -import java.util.UUID; -import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.hbase.HRegionInfo; -import org.apache.hadoop.hbase.TableName; -import org.apache.hadoop.hbase.catalog.CatalogTracker; -import org.apache.hadoop.hbase.catalog.MetaReader; -import org.apache.hadoop.hbase.client.HBaseAdmin; -import org.apache.hadoop.hbase.util.Bytes; -import org.apache.phoenix.jdbc.PhoenixConnection; import org.apache.phoenix.mapreduce.index.IndexTool; import org.apache.phoenix.query.BaseTest; import org.apache.phoenix.query.QueryServices; import org.apache.phoenix.query.QueryServicesOptions; -import org.apache.phoenix.util.ByteUtil; import org.apache.phoenix.util.PropertiesUtil; import org.apache.phoenix.util.QueryUtil; import org.apache.phoenix.util.ReadOnlyProps; import org.apache.phoenix.util.SchemaUtil; -import org.apache.phoenix.util.TestUtil; -import org.junit.AfterClass; import org.junit.BeforeClass; -import org.junit.Ignore; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; @@ -72,15 +57,13 @@ import com.google.common.collect.Maps; @Category(NeedsOwnMiniClusterTest.class) public class IndexExtendedIT extends BaseTest { private final boolean localIndex; - private final boolean transactional; private final boolean directApi; private final String tableDDLOptions; private final boolean mutable; private final boolean useSnapshot; - public IndexExtendedIT(boolean transactional, boolean mutable, boolean localIndex, boolean directApi, boolean useSnapshot) { + public IndexExtendedIT( boolean mutable, boolean localIndex, boolean directApi, boolean useSnapshot) { this.localIndex = localIndex; - this.transactional = transactional; this.directApi = directApi; this.mutable = mutable; this.useSnapshot = useSnapshot; @@ -88,12 +71,6 @@ public class IndexExtendedIT extends BaseTest { if (!mutable) { optionBuilder.append(" IMMUTABLE_ROWS=true "); } - if (transactional) { - if (!(optionBuilder.length()==0)) { - optionBuilder.append(","); - } - optionBuilder.append(" TRANSACTIONAL=true "); - } optionBuilder.append(" SPLIT ON(1,2)"); this.tableDDLOptions = optionBuilder.toString(); } @@ -103,29 +80,25 @@ public class IndexExtendedIT extends BaseTest { Map<String, String> serverProps = Maps.newHashMapWithExpectedSize(2); serverProps.put(QueryServices.EXTRA_JDBC_ARGUMENTS_ATTRIB, QueryServicesOptions.DEFAULT_EXTRA_JDBC_ARGUMENTS); Map<String, String> clientProps = Maps.newHashMapWithExpectedSize(2); - /* Commenting out due to potential issue in PHOENIX-3448 and general flappiness - clientProps.put(QueryServices.TRANSACTIONS_ENABLED, Boolean.TRUE.toString()); - */ clientProps.put(QueryServices.FORCE_ROW_KEY_ORDER_ATTRIB, Boolean.TRUE.toString()); setUpTestDriver(new ReadOnlyProps(serverProps.entrySet().iterator()), new ReadOnlyProps(clientProps.entrySet() .iterator())); } - @Parameters(name="transactional = {0} , mutable = {1} , localIndex = {2}, directApi = {3}") + @Parameters(name="mutable = {0} , localIndex = {1}, directApi = {2}, useSnapshot = {3}") public static Collection<Boolean[]> data() { - return Arrays.asList(new Boolean[][] { - { false, false, false, false, false }, { false, false, false, true, false }, { false, false, true, false, false }, { false, false, true, true, false }, - { false, true, false, false, false }, { false, true, false, true, false }, { false, true, true, false, false }, { false, true, true, true, false }, - /* Commenting out due to potential issue in PHOENIX-3448 and general flappiness - { true, false, false, false, false }, { true, false, false, true, false }, { true, false, true, false, false }, { true, false, true, true, false }, - { true, true, false, false, false }, { true, true, false, true, false }, { true, true, true, false, false }, { true, true, true, true, false }, - */ - { false, true, false, false, true }, { false, true, false, true, true }, { false, true, true, false, true }, { false, true, true, true, true }, - /* - { true, false, false, false, true }, { true, false, false, true, true }, { true, false, true, false, true }, { true, false, true, true, true }, - { true, true, false, false, true }, { true, true, false, true, true }, { true, true, true, false, true }, { true, true, true, true, true } - */ - }); + List<Boolean[]> list = Lists.newArrayListWithExpectedSize(16); + boolean[] Booleans = new boolean[]{false, true}; + for (boolean mutable : Booleans ) { + for (boolean localIndex : Booleans ) { + for (boolean directApi : Booleans ) { + for (boolean useSnapshot : Booleans ) { + list.add(new Boolean[]{ mutable, localIndex, directApi, useSnapshot}); + } + } + } + } + return list; } /** @@ -135,7 +108,7 @@ public class IndexExtendedIT extends BaseTest { */ @Test public void testMutableIndexWithUpdates() throws Exception { - if (!mutable || transactional) { + if (!mutable) { return; } if (localIndex) { // FIXME: remove once this test works for local indexes @@ -150,14 +123,14 @@ public class IndexExtendedIT extends BaseTest { Connection conn = DriverManager.getConnection(getUrl(), props); Statement stmt = conn.createStatement(); try { - stmt.execute(String.format("CREATE TABLE %s (ID INTEGER NOT NULL PRIMARY KEY, NAME VARCHAR, ZIP INTEGER)",dataTableFullName)); + stmt.execute(String.format("CREATE TABLE %s (ID INTEGER NOT NULL PRIMARY KEY, NAME VARCHAR, ZIP INTEGER) %s",dataTableFullName, tableDDLOptions)); String upsertQuery = String.format("UPSERT INTO %s VALUES(?, ?, ?)",dataTableFullName); PreparedStatement stmt1 = conn.prepareStatement(upsertQuery); int id = 1; // insert two rows - IndexExtendedIT.upsertRow(stmt1, id++); - IndexExtendedIT.upsertRow(stmt1, id++); + IndexToolIT.upsertRow(stmt1, id++); + IndexToolIT.upsertRow(stmt1, id++); conn.commit(); stmt.execute(String.format("CREATE " + (localIndex ? "LOCAL" : "") + " INDEX %s ON %s (UPPER(NAME)) ASYNC ", indexTableName,dataTableFullName)); @@ -184,13 +157,13 @@ public class IndexExtendedIT extends BaseTest { assertFalse(rs.next()); //run the index MR job. - runIndexTool(schemaName, dataTableName, indexTableName); + IndexToolIT.runIndexTool(directApi, useSnapshot, schemaName, dataTableName, indexTableName); //assert we are pulling from index table. rs = conn.createStatement().executeQuery("EXPLAIN " + selectSql); actualExplainPlan = QueryUtil.getExplainPlan(rs); // TODO: why is it a 1-WAY parallel scan only for !transactional && mutable && localIndex - assertExplainPlan(actualExplainPlan, dataTableFullName, indexTableFullName); + IndexToolIT.assertExplainPlan(localIndex, actualExplainPlan, dataTableFullName, indexTableFullName); rs = stmt.executeQuery(selectSql); assertTrue(rs.next()); @@ -200,147 +173,10 @@ public class IndexExtendedIT extends BaseTest { conn.close(); } } - - private void runIndexTool(String schemaName, String dataTableName, String indexTableName) throws Exception { - IndexTool indexingTool = new IndexTool(); - Configuration conf = new Configuration(getUtility().getConfiguration()); - conf.set(QueryServices.TRANSACTIONS_ENABLED, Boolean.TRUE.toString()); - indexingTool.setConf(conf); - final String[] cmdArgs = getArgValues(schemaName, dataTableName, indexTableName); - int status = indexingTool.run(cmdArgs); - assertEquals(0, status); - } - - @Test - public void testSecondaryIndex() throws Exception { - if (localIndex) { // FIXME: remove once this test works for local indexes - return; - } - String schemaName = generateUniqueName(); - String dataTableName = generateUniqueName(); - String dataTableFullName = SchemaUtil.getTableName(schemaName, dataTableName); - String indexTableName = generateUniqueName(); - String indexTableFullName = SchemaUtil.getTableName(schemaName, indexTableName); - Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); - Connection conn = DriverManager.getConnection(getUrl(), props); - try { - String stmString1 = "CREATE TABLE " + dataTableFullName + " (ID INTEGER NOT NULL PRIMARY KEY, NAME VARCHAR, ZIP INTEGER) " + tableDDLOptions; - conn.createStatement().execute(stmString1); - String upsertQuery = String.format("UPSERT INTO %s VALUES(?, ?, ?)", dataTableFullName); - PreparedStatement stmt1 = conn.prepareStatement(upsertQuery); - - // insert two rows - upsertRow(stmt1, 1); - upsertRow(stmt1, 2); - conn.commit(); - - if (transactional) { - // insert two rows in another connection without committing so that they are not visible to other transactions - try (Connection conn2 = DriverManager.getConnection(getUrl(), props)) { - conn2.setAutoCommit(false); - PreparedStatement stmt2 = conn2.prepareStatement(upsertQuery); - upsertRow(stmt2, 5); - upsertRow(stmt2, 6); - ResultSet rs = conn.createStatement().executeQuery("SELECT count(*) from "+dataTableFullName); - assertTrue(rs.next()); - assertEquals("Unexpected row count ", 2, rs.getInt(1)); - assertFalse(rs.next()); - rs = conn2.createStatement().executeQuery("SELECT count(*) from "+dataTableFullName); - assertTrue(rs.next()); - assertEquals("Unexpected row count ", 4, rs.getInt(1)); - assertFalse(rs.next()); - } - } - - String stmtString2 = String.format("CREATE %s INDEX %s ON %s (LPAD(UPPER(NAME),8,'x')||'_xyz') ASYNC ", (localIndex ? "LOCAL" : ""), indexTableName, dataTableFullName); - conn.createStatement().execute(stmtString2); - - //verify rows are fetched from data table. - String selectSql = String.format("SELECT ID FROM %s WHERE LPAD(UPPER(NAME),8,'x')||'_xyz' = 'xxUNAME2_xyz'", dataTableFullName); - ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " + selectSql); - String actualExplainPlan = QueryUtil.getExplainPlan(rs); - - //assert we are pulling from data table. - assertEquals( - String.format("CLIENT PARALLEL 1-WAY FULL SCAN OVER %s\n" - + " SERVER FILTER BY (LPAD(UPPER(NAME), 8, 'x') || '_xyz') = 'xxUNAME2_xyz'", dataTableFullName), actualExplainPlan); - - rs = stmt1.executeQuery(selectSql); - assertTrue(rs.next()); - assertEquals(2, rs.getInt(1)); - assertFalse(rs.next()); - conn.commit(); - - //run the index MR job. - runIndexTool(schemaName, dataTableName, indexTableName); - - // insert two more rows - upsertRow(stmt1, 3); - upsertRow(stmt1, 4); - conn.commit(); - - //assert we are pulling from index table. - rs = conn.createStatement().executeQuery("EXPLAIN " + selectSql); - actualExplainPlan = QueryUtil.getExplainPlan(rs); - assertExplainPlan(actualExplainPlan, dataTableFullName, indexTableFullName); - - rs = conn.createStatement().executeQuery(selectSql); - assertTrue(rs.next()); - assertEquals(2, rs.getInt(1)); - assertFalse(rs.next()); - } finally { - conn.close(); - } - } - private void assertExplainPlan(final String actualExplainPlan, String dataTableFullName, String indexTableFullName) { - String expectedExplainPlan; - if(localIndex) { - expectedExplainPlan = String.format(" RANGE SCAN OVER %s [1,", - dataTableFullName); - } else { - expectedExplainPlan = String.format(" RANGE SCAN OVER %s", - indexTableFullName); - } - assertTrue(actualExplainPlan + "\n expected to contain \n" + expectedExplainPlan, actualExplainPlan.contains(expectedExplainPlan)); - } - - private String[] getArgValues(String schemaName, String dataTable, String indxTable) { - final List<String> args = Lists.newArrayList(); - if (schemaName!=null) { - args.add("-s"); - args.add(schemaName); - } - args.add("-dt"); - args.add(dataTable); - args.add("-it"); - args.add(indxTable); - if(directApi) { - args.add("-direct"); - // Need to run this job in foreground for the test to be deterministic - args.add("-runfg"); - } - - if(useSnapshot) { - args.add("-snap"); - } - - args.add("-op"); - args.add("/tmp/"+UUID.randomUUID().toString()); - return args.toArray(new String[0]); - } - - private static void upsertRow(PreparedStatement stmt, int i) throws SQLException { - // insert row - stmt.setInt(1, i); - stmt.setString(2, "uname" + String.valueOf(i)); - stmt.setInt(3, 95050 + i); - stmt.executeUpdate(); - } - @Test public void testDeleteFromImmutable() throws Exception { - if (transactional || mutable) { + if (mutable) { return; } if (localIndex) { // TODO: remove this return once PHOENIX-3292 is fixed @@ -363,7 +199,7 @@ public class IndexExtendedIT extends BaseTest { " pk2,\n" + " pk3\n" + " )\n" + - " ) IMMUTABLE_ROWS=true"); + " ) " + tableDDLOptions); conn.createStatement().execute("upsert into " + dataTableFullName + " (pk1, pk2, pk3) values ('a', '1', '1')"); conn.createStatement().execute("upsert into " + dataTableFullName + " (pk1, pk2, pk3) values ('b', '2', '2')"); conn.commit(); @@ -374,7 +210,7 @@ public class IndexExtendedIT extends BaseTest { conn.commit(); //run the index MR job. - runIndexTool(schemaName, dataTableName, indexTableName); + IndexToolIT.runIndexTool(directApi, useSnapshot, schemaName, dataTableName, indexTableName); // upsert two more rows conn.createStatement().execute( @@ -402,219 +238,4 @@ public class IndexExtendedIT extends BaseTest { } } - private static Connection getConnectionForLocalIndexTest() throws SQLException{ - Properties props = PropertiesUtil.deepCopy(TestUtil.TEST_PROPERTIES); - props.setProperty(QueryServices.FORCE_ROW_KEY_ORDER_ATTRIB, Boolean.TRUE.toString()); - return DriverManager.getConnection(getUrl(),props); - } - - private void createBaseTable(String tableName, String splits) throws SQLException { - Connection conn = getConnectionForLocalIndexTest(); - String ddl = "CREATE TABLE " + tableName + " (t_id VARCHAR NOT NULL,\n" + - "k1 INTEGER NOT NULL,\n" + - "k2 INTEGER NOT NULL,\n" + - "k3 INTEGER,\n" + - "v1 VARCHAR,\n" + - "CONSTRAINT pk PRIMARY KEY (t_id, k1, k2))\n" - + (splits != null ? (" split on " + splits) : ""); - conn.createStatement().execute(ddl); - conn.close(); - } - - // Moved from LocalIndexIT because it was causing parallel runs to hang - @Ignore - @Test - public void testLocalIndexScanAfterRegionSplit() throws Exception { - // This test just needs be run once - if (!localIndex || transactional || mutable || directApi) { - return; - } - String schemaName = generateUniqueName(); - String tableName = schemaName + "." + generateUniqueName(); - String indexName = "IDX_" + generateUniqueName(); - TableName physicalTableName = SchemaUtil.getPhysicalTableName(tableName.getBytes(), false); - String indexPhysicalTableName = physicalTableName.getNameAsString(); - - createBaseTable(tableName, "('e','j','o')"); - Connection conn1 = getConnectionForLocalIndexTest(); - try{ - String[] strings = {"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"}; - for (int i = 0; i < 26; i++) { - conn1.createStatement().execute( - "UPSERT INTO " + tableName + " values('"+strings[i]+"'," + i + "," - + (i + 1) + "," + (i + 2) + ",'" + strings[25 - i] + "')"); - } - conn1.commit(); - conn1.createStatement().execute("CREATE LOCAL INDEX " + indexName + " ON " + tableName + "(v1)"); - conn1.createStatement().execute("CREATE LOCAL INDEX " + indexName + "_2 ON " + tableName + "(k3)"); - - ResultSet rs = conn1.createStatement().executeQuery("SELECT * FROM " + tableName); - assertTrue(rs.next()); - - HBaseAdmin admin = conn1.unwrap(PhoenixConnection.class).getQueryServices().getAdmin(); - for (int i = 1; i < 5; i++) { - CatalogTracker ct = new CatalogTracker(admin.getConfiguration()); - admin.split(physicalTableName.getName(), ByteUtil.concat(Bytes.toBytes(strings[3*i]))); - List<HRegionInfo> regionsOfUserTable = - MetaReader.getTableRegions(ct, physicalTableName, false); - - while (regionsOfUserTable.size() != (4+i)) { - Thread.sleep(100); - regionsOfUserTable = MetaReader.getTableRegions(ct, physicalTableName, false); - } - assertEquals(4+i, regionsOfUserTable.size()); - String[] tIdColumnValues = new String[26]; - String[] v1ColumnValues = new String[26]; - int[] k1ColumnValue = new int[26]; - String query = "SELECT t_id,k1,v1 FROM " + tableName; - rs = conn1.createStatement().executeQuery(query); - Thread.sleep(1000); - for (int j = 0; j < 26; j++) { - assertTrue("No row found at " + j, rs.next()); - tIdColumnValues[j] = rs.getString("t_id"); - k1ColumnValue[j] = rs.getInt("k1"); - v1ColumnValues[j] = rs.getString("V1"); - } - Arrays.sort(tIdColumnValues); - Arrays.sort(v1ColumnValues); - Arrays.sort(k1ColumnValue); - assertTrue(Arrays.equals(strings, tIdColumnValues)); - assertTrue(Arrays.equals(strings, v1ColumnValues)); - for(int m=0;m<26;m++) { - assertEquals(m, k1ColumnValue[m]); - } - - rs = conn1.createStatement().executeQuery("EXPLAIN " + query); - assertEquals( - "CLIENT PARALLEL " + (4 + i) + "-WAY RANGE SCAN OVER " - + indexPhysicalTableName + " [1]\n" - + " SERVER FILTER BY FIRST KEY ONLY\n" - + "CLIENT MERGE SORT", QueryUtil.getExplainPlan(rs)); - - query = "SELECT t_id,k1,k3 FROM " + tableName; - rs = conn1.createStatement().executeQuery("EXPLAIN "+query); - assertEquals( - "CLIENT PARALLEL " - + ((strings[3 * i].compareTo("j") < 0) ? (4 + i) : (4 + i - 1)) - + "-WAY RANGE SCAN OVER " - + indexPhysicalTableName + " [2]\n" - + " SERVER FILTER BY FIRST KEY ONLY\n" - + "CLIENT MERGE SORT", QueryUtil.getExplainPlan(rs)); - rs = conn1.createStatement().executeQuery(query); - Thread.sleep(1000); - int[] k3ColumnValue = new int[26]; - for (int j = 0; j < 26; j++) { - assertTrue(rs.next()); - tIdColumnValues[j] = rs.getString("t_id"); - k1ColumnValue[j] = rs.getInt("k1"); - k3ColumnValue[j] = rs.getInt("k3"); - } - Arrays.sort(tIdColumnValues); - Arrays.sort(k1ColumnValue); - Arrays.sort(k3ColumnValue); - assertTrue(Arrays.equals(strings, tIdColumnValues)); - for(int m=0;m<26;m++) { - assertEquals(m, k1ColumnValue[m]); - assertEquals(m+2, k3ColumnValue[m]); - } - } - } finally { - conn1.close(); - } - } - - // Moved from LocalIndexIT because it was causing parallel runs to hang - @Ignore - @Test - public void testLocalIndexScanAfterRegionsMerge() throws Exception { - // This test just needs be run once - if (!localIndex || transactional || mutable || directApi) { - return; - } - String schemaName = generateUniqueName(); - String tableName = schemaName + "." + generateUniqueName(); - String indexName = "IDX_" + generateUniqueName(); - TableName physicalTableName = SchemaUtil.getPhysicalTableName(tableName.getBytes(), false); - String indexPhysicalTableName = physicalTableName.getNameAsString(); - - createBaseTable(tableName, "('e','j','o')"); - Connection conn1 = getConnectionForLocalIndexTest(); - try{ - String[] strings = {"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"}; - for (int i = 0; i < 26; i++) { - conn1.createStatement().execute( - "UPSERT INTO " + tableName + " values('"+strings[i]+"'," + i + "," - + (i + 1) + "," + (i + 2) + ",'" + strings[25 - i] + "')"); - } - conn1.commit(); - conn1.createStatement().execute("CREATE LOCAL INDEX " + indexName + " ON " + tableName + "(v1)"); - conn1.createStatement().execute("CREATE LOCAL INDEX " + indexName + "_2 ON " + tableName + "(k3)"); - - ResultSet rs = conn1.createStatement().executeQuery("SELECT * FROM " + tableName); - assertTrue(rs.next()); - - HBaseAdmin admin = conn1.unwrap(PhoenixConnection.class).getQueryServices().getAdmin(); - CatalogTracker ct = new CatalogTracker(admin.getConfiguration()); - List<HRegionInfo> regionsOfUserTable = - MetaReader.getTableRegions(ct, - physicalTableName, false); - admin.mergeRegions(regionsOfUserTable.get(0).getEncodedNameAsBytes(), - regionsOfUserTable.get(1).getEncodedNameAsBytes(), false); - regionsOfUserTable = - MetaReader.getTableRegions(ct, - physicalTableName, false); - - while (regionsOfUserTable.size() != 3) { - Thread.sleep(100); - regionsOfUserTable = - MetaReader.getTableRegions(ct, - physicalTableName, false); - } - assertEquals(3, regionsOfUserTable.size()); - TableName indexTable = - TableName.valueOf(indexPhysicalTableName); - List<HRegionInfo> regionsOfIndexTable = - MetaReader.getTableRegions(ct, indexTable, false); - - while (regionsOfIndexTable.size() != 3) { - Thread.sleep(100); - regionsOfIndexTable = MetaReader.getTableRegions(ct, indexTable, false); - } - assertEquals(3, regionsOfIndexTable.size()); - String query = "SELECT t_id,k1,v1 FROM " + tableName; - rs = conn1.createStatement().executeQuery(query); - Thread.sleep(1000); - for (int j = 0; j < 26; j++) { - assertTrue(rs.next()); - assertEquals(strings[25 - j], rs.getString("t_id")); - assertEquals(25 - j, rs.getInt("k1")); - assertEquals(strings[j], rs.getString("V1")); - } - rs = conn1.createStatement().executeQuery("EXPLAIN " + query); - assertEquals( - "CLIENT PARALLEL " + 3 + "-WAY RANGE SCAN OVER " - + indexPhysicalTableName - + " [1]\n" + " SERVER FILTER BY FIRST KEY ONLY\n" - + "CLIENT MERGE SORT", QueryUtil.getExplainPlan(rs)); - - query = "SELECT t_id,k1,k3 FROM " + tableName; - rs = conn1.createStatement().executeQuery("EXPLAIN " + query); - assertEquals( - "CLIENT PARALLEL " + 3 + "-WAY RANGE SCAN OVER " - + indexPhysicalTableName - + " [2]\n" + " SERVER FILTER BY FIRST KEY ONLY\n" - + "CLIENT MERGE SORT", QueryUtil.getExplainPlan(rs)); - - rs = conn1.createStatement().executeQuery(query); - Thread.sleep(1000); - for (int j = 0; j < 26; j++) { - assertTrue(rs.next()); - assertEquals(strings[j], rs.getString("t_id")); - assertEquals(j, rs.getInt("k1")); - assertEquals(j + 2, rs.getInt("k3")); - } - } finally { - conn1.close(); - } - } } http://git-wip-us.apache.org/repos/asf/phoenix/blob/d1b2e169/phoenix-core/src/it/java/org/apache/phoenix/end2end/IndexToolIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/IndexToolIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/IndexToolIT.java new file mode 100644 index 0000000..913a147 --- /dev/null +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/IndexToolIT.java @@ -0,0 +1,269 @@ +/* + * 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.end2end; + +import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.UUID; + +import org.apache.hadoop.conf.Configuration; +import org.apache.phoenix.mapreduce.index.IndexTool; +import org.apache.phoenix.query.BaseTest; +import org.apache.phoenix.query.QueryServices; +import org.apache.phoenix.query.QueryServicesOptions; +import org.apache.phoenix.util.PropertiesUtil; +import org.apache.phoenix.util.QueryUtil; +import org.apache.phoenix.util.ReadOnlyProps; +import org.apache.phoenix.util.SchemaUtil; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +@RunWith(Parameterized.class) +@Category(NeedsOwnMiniClusterTest.class) +public class IndexToolIT extends BaseTest { + + private final boolean localIndex; + private final boolean transactional; + private final boolean directApi; + private final String tableDDLOptions; + private final boolean mutable; + private final boolean useSnapshot; + + public IndexToolIT(boolean transactional, boolean mutable, boolean localIndex, + boolean directApi, boolean useSnapshot) { + this.localIndex = localIndex; + this.transactional = transactional; + this.directApi = directApi; + this.mutable = mutable; + this.useSnapshot = useSnapshot; + StringBuilder optionBuilder = new StringBuilder(); + if (!mutable) { + optionBuilder.append(" IMMUTABLE_ROWS=true "); + } + if (transactional) { + if (!(optionBuilder.length() == 0)) { + optionBuilder.append(","); + } + optionBuilder.append(" TRANSACTIONAL=true "); + } + optionBuilder.append(" SPLIT ON(1,2)"); + this.tableDDLOptions = optionBuilder.toString(); + } + + @BeforeClass + public static void doSetup() throws Exception { + Map<String, String> serverProps = Maps.newHashMapWithExpectedSize(2); + serverProps.put(QueryServices.EXTRA_JDBC_ARGUMENTS_ATTRIB, + QueryServicesOptions.DEFAULT_EXTRA_JDBC_ARGUMENTS); + Map<String, String> clientProps = Maps.newHashMapWithExpectedSize(2); + clientProps.put(QueryServices.TRANSACTIONS_ENABLED, Boolean.TRUE.toString()); + clientProps.put(QueryServices.FORCE_ROW_KEY_ORDER_ATTRIB, Boolean.TRUE.toString()); + setUpTestDriver(new ReadOnlyProps(serverProps.entrySet().iterator()), + new ReadOnlyProps(clientProps.entrySet().iterator())); + } + + @Parameters( + name = "transactional = {0} , mutable = {1} , localIndex = {2}, directApi = {3}, useSnapshot = {4}") + public static Collection<Boolean[]> data() { + List<Boolean[]> list = Lists.newArrayListWithExpectedSize(16); + boolean[] Booleans = new boolean[] { false, true }; + for (boolean transactional : Booleans) { + for (boolean mutable : Booleans) { + for (boolean localIndex : Booleans) { + for (boolean directApi : Booleans) { + for (boolean useSnapshot : Booleans) { + list.add(new Boolean[] { transactional, mutable, localIndex, directApi, useSnapshot }); + } + } + } + } + } + return list; + } + + @Test + public void testSecondaryIndex() throws Exception { + String schemaName = generateUniqueName(); + String dataTableName = generateUniqueName(); + String dataTableFullName = SchemaUtil.getTableName(schemaName, dataTableName); + String indexTableName = generateUniqueName(); + String indexTableFullName = SchemaUtil.getTableName(schemaName, indexTableName); + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + Connection conn = DriverManager.getConnection(getUrl(), props); + try { + String stmString1 = + "CREATE TABLE " + dataTableFullName + + " (ID INTEGER NOT NULL PRIMARY KEY, NAME VARCHAR, ZIP INTEGER) " + + tableDDLOptions; + conn.createStatement().execute(stmString1); + String upsertQuery = String.format("UPSERT INTO %s VALUES(?, ?, ?)", dataTableFullName); + PreparedStatement stmt1 = conn.prepareStatement(upsertQuery); + + // insert two rows + upsertRow(stmt1, 1); + upsertRow(stmt1, 2); + conn.commit(); + + if (transactional) { + // insert two rows in another connection without committing so that they are not + // visible to other transactions + try (Connection conn2 = DriverManager.getConnection(getUrl(), props)) { + conn2.setAutoCommit(false); + PreparedStatement stmt2 = conn2.prepareStatement(upsertQuery); + upsertRow(stmt2, 5); + upsertRow(stmt2, 6); + ResultSet rs = + conn.createStatement() + .executeQuery("SELECT count(*) from " + dataTableFullName); + assertTrue(rs.next()); + assertEquals("Unexpected row count ", 2, rs.getInt(1)); + assertFalse(rs.next()); + rs = + conn2.createStatement() + .executeQuery("SELECT count(*) from " + dataTableFullName); + assertTrue(rs.next()); + assertEquals("Unexpected row count ", 4, rs.getInt(1)); + assertFalse(rs.next()); + } + } + + String stmtString2 = + String.format( + "CREATE %s INDEX %s ON %s (LPAD(UPPER(NAME),8,'x')||'_xyz') ASYNC ", + (localIndex ? "LOCAL" : ""), indexTableName, dataTableFullName); + conn.createStatement().execute(stmtString2); + + // verify rows are fetched from data table. + String selectSql = + String.format( + "SELECT ID FROM %s WHERE LPAD(UPPER(NAME),8,'x')||'_xyz' = 'xxUNAME2_xyz'", + dataTableFullName); + ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " + selectSql); + String actualExplainPlan = QueryUtil.getExplainPlan(rs); + + // assert we are pulling from data table. + assertEquals(String.format( + "CLIENT PARALLEL 1-WAY FULL SCAN OVER %s\n" + + " SERVER FILTER BY (LPAD(UPPER(NAME), 8, 'x') || '_xyz') = 'xxUNAME2_xyz'", + dataTableFullName), actualExplainPlan); + + rs = stmt1.executeQuery(selectSql); + assertTrue(rs.next()); + assertEquals(2, rs.getInt(1)); + assertFalse(rs.next()); + conn.commit(); + + // run the index MR job. + runIndexTool(directApi, useSnapshot, schemaName, dataTableName, indexTableName); + + // insert two more rows + upsertRow(stmt1, 3); + upsertRow(stmt1, 4); + conn.commit(); + + // assert we are pulling from index table. + rs = conn.createStatement().executeQuery("EXPLAIN " + selectSql); + actualExplainPlan = QueryUtil.getExplainPlan(rs); + assertExplainPlan(localIndex, actualExplainPlan, dataTableFullName, indexTableFullName); + + rs = conn.createStatement().executeQuery(selectSql); + assertTrue(rs.next()); + assertEquals(2, rs.getInt(1)); + assertFalse(rs.next()); + } finally { + conn.close(); + } + } + + public static void assertExplainPlan(boolean localIndex, String actualExplainPlan, + String dataTableFullName, String indexTableFullName) { + String expectedExplainPlan; + if (localIndex) { + expectedExplainPlan = String.format(" RANGE SCAN OVER %s [1,", dataTableFullName); + } else { + expectedExplainPlan = String.format(" RANGE SCAN OVER %s", indexTableFullName); + } + assertTrue(actualExplainPlan + "\n expected to contain \n" + expectedExplainPlan, + actualExplainPlan.contains(expectedExplainPlan)); + } + + public static String[] getArgValues(boolean directApi, boolean useSnapshot, String schemaName, + String dataTable, String indxTable) { + final List<String> args = Lists.newArrayList(); + if (schemaName != null) { + args.add("-s"); + args.add(schemaName); + } + args.add("-dt"); + args.add(dataTable); + args.add("-it"); + args.add(indxTable); + if (directApi) { + args.add("-direct"); + // Need to run this job in foreground for the test to be deterministic + args.add("-runfg"); + } + + if (useSnapshot) { + args.add("-snap"); + } + + args.add("-op"); + args.add("/tmp/" + UUID.randomUUID().toString()); + return args.toArray(new String[0]); + } + + public static void upsertRow(PreparedStatement stmt, int i) throws SQLException { + // insert row + stmt.setInt(1, i); + stmt.setString(2, "uname" + String.valueOf(i)); + stmt.setInt(3, 95050 + i); + stmt.executeUpdate(); + } + + public static void runIndexTool(boolean directApi, boolean useSnapshot, String schemaName, + String dataTableName, String indexTableName) throws Exception { + IndexTool indexingTool = new IndexTool(); + Configuration conf = new Configuration(getUtility().getConfiguration()); + conf.set(QueryServices.TRANSACTIONS_ENABLED, Boolean.TRUE.toString()); + indexingTool.setConf(conf); + final String[] cmdArgs = + getArgValues(directApi, useSnapshot, schemaName, dataTableName, indexTableName); + int status = indexingTool.run(cmdArgs); + assertEquals(0, status); + } +} http://git-wip-us.apache.org/repos/asf/phoenix/blob/d1b2e169/phoenix-core/src/it/java/org/apache/phoenix/end2end/LocalIndexSplitMergeIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/LocalIndexSplitMergeIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/LocalIndexSplitMergeIT.java new file mode 100644 index 0000000..5feea09 --- /dev/null +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/LocalIndexSplitMergeIT.java @@ -0,0 +1,276 @@ +/* + * 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.end2end; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.apache.hadoop.hbase.HRegionInfo; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.catalog.CatalogTracker; +import org.apache.hadoop.hbase.catalog.MetaReader; +import org.apache.hadoop.hbase.client.HBaseAdmin; +import org.apache.hadoop.hbase.util.Bytes; +import org.apache.phoenix.jdbc.PhoenixConnection; +import org.apache.phoenix.query.BaseTest; +import org.apache.phoenix.query.QueryServices; +import org.apache.phoenix.query.QueryServicesOptions; +import org.apache.phoenix.util.ByteUtil; +import org.apache.phoenix.util.PropertiesUtil; +import org.apache.phoenix.util.QueryUtil; +import org.apache.phoenix.util.ReadOnlyProps; +import org.apache.phoenix.util.SchemaUtil; +import org.apache.phoenix.util.TestUtil; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +import com.google.common.collect.Maps; + +@Category(NeedsOwnMiniClusterTest.class) +public class LocalIndexSplitMergeIT extends BaseTest { + + @BeforeClass + public static void doSetup() throws Exception { + Map<String, String> serverProps = Maps.newHashMapWithExpectedSize(2); + serverProps.put(QueryServices.EXTRA_JDBC_ARGUMENTS_ATTRIB, + QueryServicesOptions.DEFAULT_EXTRA_JDBC_ARGUMENTS); + Map<String, String> clientProps = Maps.newHashMapWithExpectedSize(2); + clientProps.put(QueryServices.TRANSACTIONS_ENABLED, Boolean.TRUE.toString()); + clientProps.put(QueryServices.FORCE_ROW_KEY_ORDER_ATTRIB, Boolean.TRUE.toString()); + setUpTestDriver(new ReadOnlyProps(serverProps.entrySet().iterator()), + new ReadOnlyProps(clientProps.entrySet().iterator())); + } + + private Connection getConnectionForLocalIndexTest() throws SQLException { + Properties props = PropertiesUtil.deepCopy(TestUtil.TEST_PROPERTIES); + props.setProperty(QueryServices.FORCE_ROW_KEY_ORDER_ATTRIB, Boolean.TRUE.toString()); + return DriverManager.getConnection(getUrl(), props); + } + + private void createBaseTable(String tableName, String splits) throws SQLException { + Connection conn = getConnectionForLocalIndexTest(); + String ddl = + "CREATE TABLE " + tableName + " (t_id VARCHAR NOT NULL,\n" + + "k1 INTEGER NOT NULL,\n" + "k2 INTEGER NOT NULL,\n" + "k3 INTEGER,\n" + + "v1 VARCHAR,\n" + "CONSTRAINT pk PRIMARY KEY (t_id, k1, k2))\n" + + (splits != null ? (" split on " + splits) : ""); + conn.createStatement().execute(ddl); + conn.close(); + } + + // Moved from LocalIndexIT because it was causing parallel runs to hang + @Ignore + @Test + public void testLocalIndexScanAfterRegionSplit() throws Exception { + String schemaName = generateUniqueName(); + String tableName = schemaName + "." + generateUniqueName(); + String indexName = "IDX_" + generateUniqueName(); + TableName physicalTableName = SchemaUtil.getPhysicalTableName(tableName.getBytes(), false); + String indexPhysicalTableName = physicalTableName.getNameAsString(); + + createBaseTable(tableName, "('e','j','o')"); + Connection conn1 = getConnectionForLocalIndexTest(); + try{ + String[] strings = {"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"}; + for (int i = 0; i < 26; i++) { + conn1.createStatement().execute( + "UPSERT INTO " + tableName + " values('"+strings[i]+"'," + i + "," + + (i + 1) + "," + (i + 2) + ",'" + strings[25 - i] + "')"); + } + conn1.commit(); + conn1.createStatement().execute("CREATE LOCAL INDEX " + indexName + " ON " + tableName + "(v1)"); + conn1.createStatement().execute("CREATE LOCAL INDEX " + indexName + "_2 ON " + tableName + "(k3)"); + + ResultSet rs = conn1.createStatement().executeQuery("SELECT * FROM " + tableName); + assertTrue(rs.next()); + + HBaseAdmin admin = conn1.unwrap(PhoenixConnection.class).getQueryServices().getAdmin(); + for (int i = 1; i < 5; i++) { + CatalogTracker ct = new CatalogTracker(admin.getConfiguration()); + admin.split(physicalTableName.getName(), ByteUtil.concat(Bytes.toBytes(strings[3*i]))); + List<HRegionInfo> regionsOfUserTable = + MetaReader.getTableRegions(ct, physicalTableName, false); + + while (regionsOfUserTable.size() != (4+i)) { + Thread.sleep(100); + regionsOfUserTable = MetaReader.getTableRegions(ct, physicalTableName, false); + } + assertEquals(4+i, regionsOfUserTable.size()); + String[] tIdColumnValues = new String[26]; + String[] v1ColumnValues = new String[26]; + int[] k1ColumnValue = new int[26]; + String query = "SELECT t_id,k1,v1 FROM " + tableName; + rs = conn1.createStatement().executeQuery(query); + Thread.sleep(1000); + for (int j = 0; j < 26; j++) { + assertTrue("No row found at " + j, rs.next()); + tIdColumnValues[j] = rs.getString("t_id"); + k1ColumnValue[j] = rs.getInt("k1"); + v1ColumnValues[j] = rs.getString("V1"); + } + Arrays.sort(tIdColumnValues); + Arrays.sort(v1ColumnValues); + Arrays.sort(k1ColumnValue); + assertTrue(Arrays.equals(strings, tIdColumnValues)); + assertTrue(Arrays.equals(strings, v1ColumnValues)); + for(int m=0;m<26;m++) { + assertEquals(m, k1ColumnValue[m]); + } + + rs = conn1.createStatement().executeQuery("EXPLAIN " + query); + assertEquals( + "CLIENT PARALLEL " + (4 + i) + "-WAY RANGE SCAN OVER " + + indexPhysicalTableName + " [1]\n" + + " SERVER FILTER BY FIRST KEY ONLY\n" + + "CLIENT MERGE SORT", QueryUtil.getExplainPlan(rs)); + + query = "SELECT t_id,k1,k3 FROM " + tableName; + rs = conn1.createStatement().executeQuery("EXPLAIN "+query); + assertEquals( + "CLIENT PARALLEL " + + ((strings[3 * i].compareTo("j") < 0) ? (4 + i) : (4 + i - 1)) + + "-WAY RANGE SCAN OVER " + + indexPhysicalTableName + " [2]\n" + + " SERVER FILTER BY FIRST KEY ONLY\n" + + "CLIENT MERGE SORT", QueryUtil.getExplainPlan(rs)); + rs = conn1.createStatement().executeQuery(query); + Thread.sleep(1000); + int[] k3ColumnValue = new int[26]; + for (int j = 0; j < 26; j++) { + assertTrue(rs.next()); + tIdColumnValues[j] = rs.getString("t_id"); + k1ColumnValue[j] = rs.getInt("k1"); + k3ColumnValue[j] = rs.getInt("k3"); + } + Arrays.sort(tIdColumnValues); + Arrays.sort(k1ColumnValue); + Arrays.sort(k3ColumnValue); + assertTrue(Arrays.equals(strings, tIdColumnValues)); + for(int m=0;m<26;m++) { + assertEquals(m, k1ColumnValue[m]); + assertEquals(m+2, k3ColumnValue[m]); + } + } + } finally { + conn1.close(); + } + } + + // Moved from LocalIndexIT because it was causing parallel runs to hang + @Ignore + @Test + public void testLocalIndexScanAfterRegionsMerge() throws Exception { + String schemaName = generateUniqueName(); + String tableName = schemaName + "." + generateUniqueName(); + String indexName = "IDX_" + generateUniqueName(); + TableName physicalTableName = SchemaUtil.getPhysicalTableName(tableName.getBytes(), false); + String indexPhysicalTableName = physicalTableName.getNameAsString(); + + createBaseTable(tableName, "('e','j','o')"); + Connection conn1 = getConnectionForLocalIndexTest(); + try{ + String[] strings = {"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"}; + for (int i = 0; i < 26; i++) { + conn1.createStatement().execute( + "UPSERT INTO " + tableName + " values('"+strings[i]+"'," + i + "," + + (i + 1) + "," + (i + 2) + ",'" + strings[25 - i] + "')"); + } + conn1.commit(); + conn1.createStatement().execute("CREATE LOCAL INDEX " + indexName + " ON " + tableName + "(v1)"); + conn1.createStatement().execute("CREATE LOCAL INDEX " + indexName + "_2 ON " + tableName + "(k3)"); + + ResultSet rs = conn1.createStatement().executeQuery("SELECT * FROM " + tableName); + assertTrue(rs.next()); + + HBaseAdmin admin = conn1.unwrap(PhoenixConnection.class).getQueryServices().getAdmin(); + CatalogTracker ct = new CatalogTracker(admin.getConfiguration()); + List<HRegionInfo> regionsOfUserTable = + MetaReader.getTableRegions(ct, + physicalTableName, false); + admin.mergeRegions(regionsOfUserTable.get(0).getEncodedNameAsBytes(), + regionsOfUserTable.get(1).getEncodedNameAsBytes(), false); + regionsOfUserTable = + MetaReader.getTableRegions(ct, + physicalTableName, false); + + while (regionsOfUserTable.size() != 3) { + Thread.sleep(100); + regionsOfUserTable = + MetaReader.getTableRegions(ct, + physicalTableName, false); + } + assertEquals(3, regionsOfUserTable.size()); + TableName indexTable = + TableName.valueOf(indexPhysicalTableName); + List<HRegionInfo> regionsOfIndexTable = + MetaReader.getTableRegions(ct, indexTable, false); + + while (regionsOfIndexTable.size() != 3) { + Thread.sleep(100); + regionsOfIndexTable = MetaReader.getTableRegions(ct, indexTable, false); + } + assertEquals(3, regionsOfIndexTable.size()); + String query = "SELECT t_id,k1,v1 FROM " + tableName; + rs = conn1.createStatement().executeQuery(query); + Thread.sleep(1000); + for (int j = 0; j < 26; j++) { + assertTrue(rs.next()); + assertEquals(strings[25 - j], rs.getString("t_id")); + assertEquals(25 - j, rs.getInt("k1")); + assertEquals(strings[j], rs.getString("V1")); + } + rs = conn1.createStatement().executeQuery("EXPLAIN " + query); + assertEquals( + "CLIENT PARALLEL " + 3 + "-WAY RANGE SCAN OVER " + + indexPhysicalTableName + + " [1]\n" + " SERVER FILTER BY FIRST KEY ONLY\n" + + "CLIENT MERGE SORT", QueryUtil.getExplainPlan(rs)); + + query = "SELECT t_id,k1,k3 FROM " + tableName; + rs = conn1.createStatement().executeQuery("EXPLAIN " + query); + assertEquals( + "CLIENT PARALLEL " + 3 + "-WAY RANGE SCAN OVER " + + indexPhysicalTableName + + " [2]\n" + " SERVER FILTER BY FIRST KEY ONLY\n" + + "CLIENT MERGE SORT", QueryUtil.getExplainPlan(rs)); + + rs = conn1.createStatement().executeQuery(query); + Thread.sleep(1000); + for (int j = 0; j < 26; j++) { + assertTrue(rs.next()); + assertEquals(strings[j], rs.getString("t_id")); + assertEquals(j, rs.getInt("k1")); + assertEquals(j + 2, rs.getInt("k3")); + } + } finally { + conn1.close(); + } + } + +}
