You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@phoenix.apache.org by ya...@apache.org on 2020/03/26 00:17:47 UTC
[phoenix] branch master updated: PHOENIX-5698 Phoenix Query with
RVC IN list expression generates wrong scan with non-pk ordered pks
This is an automated email from the ASF dual-hosted git repository.
yanxinyi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/phoenix.git
The following commit(s) were added to refs/heads/master by this push:
new 14a17d8 PHOENIX-5698 Phoenix Query with RVC IN list expression generates wrong scan with non-pk ordered pks
14a17d8 is described below
commit 14a17d8f9b23089bea4c2a910430a73c669bc0fb
Author: Xinyi Yan <xy...@salesforce.com>
AuthorDate: Mon Mar 16 16:59:25 2020 -0700
PHOENIX-5698 Phoenix Query with RVC IN list expression generates wrong scan with non-pk ordered pks
Signed-off-by: Xinyi Yan <ya...@apache.org>
---
.../java/org/apache/phoenix/end2end/InListIT.java | 1140 ++++++++++++++++++--
.../phoenix/expression/InListExpression.java | 118 +-
.../org/apache/phoenix/iterate/ExplainTable.java | 3 +-
.../phoenix/expression/InListExpressionTest.java | 207 +++-
4 files changed, 1350 insertions(+), 118 deletions(-)
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/InListIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/InListIT.java
index 7e0a7a5..ba4a511 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/InListIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/InListIT.java
@@ -29,18 +29,21 @@ import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
+import java.sql.PreparedStatement;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
+import org.apache.phoenix.compile.QueryPlan;
+import org.apache.phoenix.iterate.ExplainTable;
import org.apache.phoenix.schema.SortOrder;
import org.apache.phoenix.schema.TypeMismatchException;
import org.apache.phoenix.schema.types.PDataType;
import org.apache.phoenix.schema.types.PInteger;
-import org.apache.phoenix.schema.types.PLong;
import org.apache.phoenix.util.PhoenixRuntime;
import org.apache.phoenix.util.PropertiesUtil;
+import org.junit.Before;
import org.junit.Test;
import com.google.common.base.Function;
@@ -49,8 +52,19 @@ import com.google.common.collect.Lists;
public class InListIT extends ParallelStatsDisabledIT {
-
- private final String TENANT_SPECIFIC_URL1 = getUrl() + ';' + TENANT_ID_ATTRIB + "=tenant1";
+ private static final String TENANT_SPECIFIC_URL1 = getUrl() + ';' + TENANT_ID_ATTRIB + "=tenant1";
+ String tableName;
+ String descViewName;
+ String ascViewName;
+
+ @Before
+ public void setup() throws Exception {
+ tableName = generateUniqueName();
+ descViewName = generateUniqueName();
+ ascViewName = generateUniqueName();
+ buildSchema(tableName, generateUniqueName(), true);
+ buildSchema(generateUniqueName(), generateUniqueName(), false);
+ }
@Test
public void testLeadingPKWithTrailingRVC() throws Exception {
@@ -71,7 +85,7 @@ public class InListIT extends ParallelStatsDisabledIT {
assertTrue(rs.next());
assertEquals("c", rs.getString(1));
assertFalse(rs.next());
-
+
conn.close();
}
@@ -90,10 +104,10 @@ public class InListIT extends ParallelStatsDisabledIT {
assertTrue(rs.next());
assertEquals(1, rs.getInt(1));
assertFalse(rs.next());
-
+
conn.close();
}
-
+
/**
* Builds the DDL statement that will create a table with the given properties.
* Assumes 5 pk columns of the given type.
@@ -107,7 +121,7 @@ public class InListIT extends ParallelStatsDisabledIT {
private static String createTableDDL(String tableName, PDataType pkType, int saltBuckets, boolean isMultiTenant) {
StringBuilder ddlBuilder = new StringBuilder();
ddlBuilder.append("CREATE TABLE ").append(tableName).append(" ( ");
-
+
// column declarations
if(isMultiTenant) {
ddlBuilder.append("tenantId VARCHAR(5) NOT NULL, ");
@@ -116,14 +130,14 @@ public class InListIT extends ParallelStatsDisabledIT {
ddlBuilder.append("pk").append(i + 1).append(" ").append(pkType.getSqlTypeName()).append(" NOT NULL, ");
}
ddlBuilder.append("nonPk VARCHAR ");
-
+
// primary key constraint declaration
ddlBuilder.append("CONSTRAINT pk PRIMARY KEY (");
if(isMultiTenant) {
ddlBuilder.append("tenantId, ");
}
ddlBuilder.append("pk1, pk2, pk3, pk4, pk5) ) ");
-
+
// modifier declarations
if(saltBuckets != 0) {
ddlBuilder.append("SALT_BUCKETS = ").append(saltBuckets);
@@ -134,10 +148,10 @@ public class InListIT extends ParallelStatsDisabledIT {
if(isMultiTenant) {
ddlBuilder.append("MULTI_TENANT=true");
}
-
+
return ddlBuilder.toString();
}
-
+
/**
* Creates a table with the given properties and returns its name. If the table is multi-tenant,
* also creates a tenant view for that table and returns the name of the view instead.
@@ -149,32 +163,32 @@ public class InListIT extends ParallelStatsDisabledIT {
* @return the table or view name that should be used to access the created table
*/
private static String initializeAndGetTable(Connection baseConn, Connection conn, boolean isMultiTenant, PDataType pkType, int saltBuckets) throws SQLException {
- String tableName = generateUniqueName() + "in_test" + pkType.getSqlTypeName() + saltBuckets + (isMultiTenant ? "_multi" : "_single");
- String tableDDL = createTableDDL(tableName, pkType, saltBuckets, isMultiTenant);
- baseConn.createStatement().execute(tableDDL);
-
- // if requested, create a tenant specific view and return the view name instead
- if(isMultiTenant) {
- String viewName = tableName + "_view";
- String viewDDL = "CREATE VIEW " + viewName + " AS SELECT * FROM " + tableName;
- conn.createStatement().execute(viewDDL);
- return viewName;
- }
- else {
- return tableName;
- }
+ String tableName = generateUniqueName() + "in_test" + pkType.getSqlTypeName() + saltBuckets + (isMultiTenant ? "_multi" : "_single");
+ String tableDDL = createTableDDL(tableName, pkType, saltBuckets, isMultiTenant);
+ baseConn.createStatement().execute(tableDDL);
+
+ // if requested, create a tenant specific view and return the view name instead
+ if(isMultiTenant) {
+ String viewName = tableName + "_view";
+ String viewDDL = "CREATE VIEW " + viewName + " AS SELECT * FROM " + tableName;
+ conn.createStatement().execute(viewDDL);
+ return viewName;
+ }
+ else {
+ return tableName;
+ }
}
-
+
private static final String TENANT_ID = "ABC";
private static final String TENANT_URL = getUrl() + ";" + PhoenixRuntime.TENANT_ID_ATTRIB + '=' + TENANT_ID;
-
+
// the different combinations to check each test against
private static final List<Boolean> TENANCIES = Arrays.asList(false, true);
private static final List<? extends PDataType> INTEGER_TYPES = Arrays.asList(PInteger.INSTANCE);
private static final List<Integer> SALT_BUCKET_NUMBERS = Arrays.asList(0, 4);
private static final List<String> HINTS = Arrays.asList("/*+ SKIP_SCAN */", "/*+ RANGE_SCAN */");
-
+
/**
* Tests the given where clause against the given upserts by comparing against the list of
* expected result strings.
@@ -187,9 +201,9 @@ public class InListIT extends ParallelStatsDisabledIT {
// test single and multitenant tables
for(boolean isMultiTenant : TENANCIES) {
Connection baseConn = DriverManager.getConnection(getUrl());
- Connection conn = isMultiTenant ? DriverManager.getConnection(TENANT_URL)
- : baseConn;
-
+ Connection conn = isMultiTenant ? DriverManager.getConnection(TENANT_URL)
+ : baseConn;
+
try {
// test each combination of types and salting
for(PDataType pkType : INTEGER_TYPES) {
@@ -197,7 +211,7 @@ public class InListIT extends ParallelStatsDisabledIT {
// use a different table with a unique name for each variation
String tableName = initializeAndGetTable(baseConn, conn, isMultiTenant, pkType, saltBuckets);
- // upsert the given data
+ // upsert the given data
for(String upsertBody : upsertBodies) {
conn.createStatement().execute("UPSERT INTO " + tableName + " " + upsertBody);
}
@@ -228,224 +242,224 @@ public class InListIT extends ParallelStatsDisabledIT {
}
}
}
-
+
List<List<Object>> DEFAULT_UPSERTS = Arrays.asList(Arrays.<Object>asList(1, 2, 4, 5, 6, "row1"),
- Arrays.<Object>asList(2, 3, 4, 5, 6, "row2"),
- Arrays.<Object>asList(2, 3, 6, 4, 5, "row3"),
- Arrays.<Object>asList(6, 5, 4, 3, 2, "row4"));
-
+ Arrays.<Object>asList(2, 3, 4, 5, 6, "row2"),
+ Arrays.<Object>asList(2, 3, 6, 4, 5, "row3"),
+ Arrays.<Object>asList(6, 5, 4, 3, 2, "row4"));
+
List<String> DEFAULT_UPSERT_BODIES = Lists.transform(DEFAULT_UPSERTS, new Function<List<Object>, String>() {
@Override
public String apply(List<Object> input) {
List<Object> pks = input.subList(0, 5);
Object nonPk = input.get(5);
-
+
return "(pk1, pk2, pk3, pk4, pk5, nonPk) VALUES ( "
- + Joiner.on(", ").join(pks) + ", '" + nonPk + "')";
+ + Joiner.on(", ").join(pks) + ", '" + nonPk + "')";
}
});
-
+
// test variations used:
// 1. queries with no results
// 2. queries with fully qualified row keys
// 3. queries with partiall qualified row keys, starting from the beginning
// 4. queries with partially qualified row keys, but not the beginning
// 5. queries with partially qualified row keys with a "hole slot" in the middle
-
+
@Test
public void testPlainRVCNoResults() throws Exception {
String whereClause = "WHERE (pk1, pk2, pk3, pk4, pk5) IN ((1, 2, 3, 4, 5), (1, 2, 4, 5, 3))";
List<String> expecteds = Collections.<String>emptyList();
-
+
testWithIntegerTypesWithVariedSaltingAndTenancy(DEFAULT_UPSERT_BODIES, whereClause, expecteds);
}
-
+
@Test
public void testPlainRVCFullyQualified() throws Exception {
String whereClause = "WHERE (pk1, pk2, pk3, pk4, pk5) IN ((1, 2, 3, 4, 5), (1, 2, 4, 5, 6))";
List<String> expecteds = singletonList("row1");
-
+
testWithIntegerTypesWithVariedSaltingAndTenancy(DEFAULT_UPSERT_BODIES, whereClause, expecteds);
}
-
+
@Test
public void testPlainRVCPartiallyQualifiedBegin() throws Exception {
String whereClause = "WHERE (pk1, pk2, pk3, pk4) IN ((2, 3, 4, 5), (1, 2, 4, 5))";
List<String> expecteds = Arrays.asList("row1", "row2");
-
+
testWithIntegerTypesWithVariedSaltingAndTenancy(DEFAULT_UPSERT_BODIES, whereClause, expecteds);
}
-
+
@Test
public void testPlainRVCPartiallyQualifiedEnd() throws Exception {
String whereClause = "WHERE (pk2, pk3, pk4, pk5) IN ((2, 3, 4, 5), (2, 4, 5, 6))";
List<String> expecteds = singletonList("row1");
-
+
testWithIntegerTypesWithVariedSaltingAndTenancy(DEFAULT_UPSERT_BODIES, whereClause, expecteds);
}
-
+
@Test
public void testPlainRVCSlotHole() throws Exception {
String whereClause = "WHERE (pk1, pk2, pk4, pk5) IN ((1, 2, 4, 5), (6, 5, 3, 2))";
List<String> expecteds = singletonList("row4");
-
+
testWithIntegerTypesWithVariedSaltingAndTenancy(DEFAULT_UPSERT_BODIES, whereClause, expecteds);
}
-
+
@Test
public void testLeadingPKWithTrailingRVCNoResults() throws Exception {
String whereClause = "WHERE pk1 != 2 AND (pk3, pk4, pk5) IN ((6, 4, 5), (5, 6, 4))";
List<String> expecteds = Collections.<String>emptyList();
-
+
testWithIntegerTypesWithVariedSaltingAndTenancy(DEFAULT_UPSERT_BODIES, whereClause, expecteds);
}
-
+
@Test
public void testLeadingPKWithTrailingRVCFullyQualified() throws Exception {
String whereClause = "WHERE pk1 = 2 AND (pk2, pk3, pk4, pk5) IN ((2, 4, 5, 6), (3, 4, 5, 6))";
List<String> expecteds = singletonList("row2");
-
+
testWithIntegerTypesWithVariedSaltingAndTenancy(DEFAULT_UPSERT_BODIES, whereClause, expecteds);
}
-
+
@Test
public void testLeadingPKWithTrailingRVCPartiallyQualifiedBegin() throws Exception {
String whereClause = "WHERE pk1 = 2 AND (pk2, pk3) IN ((3, 6), (5, 4))";
List<String> expecteds = singletonList("row3");
-
+
testWithIntegerTypesWithVariedSaltingAndTenancy(DEFAULT_UPSERT_BODIES, whereClause, expecteds);
}
-
+
@Test
public void testLeadingPKWithTrailingRVCPartiallyQualifiedEnd() throws Exception {
String whereClause = "WHERE pk2 = 2 AND (pk3, pk4, pk5) IN ((4, 5, 6), (5, 6, 4))";
List<String> expecteds = singletonList("row1");
-
+
testWithIntegerTypesWithVariedSaltingAndTenancy(DEFAULT_UPSERT_BODIES, whereClause, expecteds);
}
-
+
@Test
public void testLeadingPKWithTrailingRVCSlotHole() throws Exception {
String whereClause = "WHERE pk1 = 2 AND (pk3, pk4, pk5) IN ((4, 5, 6), (5, 6, 4))";
List<String> expecteds = singletonList("row2");
-
+
testWithIntegerTypesWithVariedSaltingAndTenancy(DEFAULT_UPSERT_BODIES, whereClause, expecteds);
}
-
+
@Test
public void testLeadingRVCWithTrailingPKNoResults() throws Exception {
String whereClause = "WHERE (pk1, pk2, pk3) IN ((2, 3, 4), (2, 3, 6)) AND pk4 = 3";
List<String> expecteds = Collections.<String>emptyList();
-
+
testWithIntegerTypesWithVariedSaltingAndTenancy(DEFAULT_UPSERT_BODIES, whereClause, expecteds);
}
-
+
@Test
public void testLeadingRVCWithTrailingPKFullyQualified() throws Exception {
String whereClause = "WHERE (pk1, pk2, pk3, pk4) IN ((1, 2, 4, 5), (2, 3, 4, 5)) AND pk5 = 6";
List<String> expecteds = Arrays.asList("row1", "row2");
-
+
testWithIntegerTypesWithVariedSaltingAndTenancy(DEFAULT_UPSERT_BODIES, whereClause, expecteds);
}
-
+
@Test
public void testLeadingRVCWithTrailingPKPartiallyQualifiedBegin() throws Exception {
String whereClause = "WHERE (pk1, pk2, pk3) IN ((2, 3, 4), (2, 3, 6)) AND pk4 = 4";
List<String> expecteds = singletonList("row3");
-
+
testWithIntegerTypesWithVariedSaltingAndTenancy(DEFAULT_UPSERT_BODIES, whereClause, expecteds);
}
-
+
@Test
public void testLeadingRVCWithTrailingPKPartiallyQualifiedEnd() throws Exception {
String whereClause = "WHERE (pk2, pk3, pk4) IN ((3, 4, 5), (3, 6, 4)) AND pk5 = 5";
List<String> expecteds = singletonList("row3");
-
+
testWithIntegerTypesWithVariedSaltingAndTenancy(DEFAULT_UPSERT_BODIES, whereClause, expecteds);
}
-
+
@Test
public void testLeadingRVCWithTrailingPKSlotHole() throws Exception {
String whereClause = "WHERE (pk1, pk2, pk3) IN ((2, 3, 4), (2, 3, 6)) AND pk5 = 5";
List<String> expecteds = singletonList("row3");
-
+
testWithIntegerTypesWithVariedSaltingAndTenancy(DEFAULT_UPSERT_BODIES, whereClause, expecteds);
}
-
+
@Test
public void testOverlappingRVCAndPKNoResults() throws Exception {
String whereClause = "WHERE (pk1, pk2) IN ((1, 2), (2, 3)) AND pk2 = 4";
List<String> expecteds = Collections.<String>emptyList();
-
+
testWithIntegerTypesWithVariedSaltingAndTenancy(DEFAULT_UPSERT_BODIES, whereClause, expecteds);
}
-
+
@Test
public void testOverlappingRVCAndPKFullyQualified() throws Exception {
String whereClause = "WHERE (pk1, pk2, pk3, pk4, pk5) IN ((1, 2, 4, 5, 6), (2, 3, 4, 5, 6)) AND pk1 = 2";
List<String> expecteds = singletonList("row2");
-
+
testWithIntegerTypesWithVariedSaltingAndTenancy(DEFAULT_UPSERT_BODIES, whereClause, expecteds);
}
-
+
@Test
public void testOverlappingRVCAndPKPartiallyQualifiedBegin() throws Exception {
String whereClause = "WHERE (pk1, pk2, pk3) IN ((1, 2, 4), (2, 3, 6)) AND pk3 = 4";
List<String> expecteds = singletonList("row1");
-
+
testWithIntegerTypesWithVariedSaltingAndTenancy(DEFAULT_UPSERT_BODIES, whereClause, expecteds);
}
-
+
@Test
public void testOverlappingRVCAndPKPartiallyQualifiedEnd() throws Exception {
String whereClause = "WHERE (pk3, pk4, pk5) IN ((4, 5, 6), (4, 3, 2)) AND pk5 = 2";
List<String> expecteds = singletonList("row4");
-
+
testWithIntegerTypesWithVariedSaltingAndTenancy(DEFAULT_UPSERT_BODIES, whereClause, expecteds);
}
-
+
@Test
public void testOverlappingRVCAndRVCNoResults() throws Exception {
String whereClause = "WHERE (pk1, pk2) IN ((1, 2), (2, 3)) AND (pk2, pk3) IN ((4, 4), (4, 6))";
List<String> expecteds = Collections.<String>emptyList();
-
+
testWithIntegerTypesWithVariedSaltingAndTenancy(DEFAULT_UPSERT_BODIES, whereClause, expecteds);
}
-
+
@Test
public void testOverlappingRVCAndRVCFullyQualified() throws Exception {
String whereClause = "WHERE (pk1, pk2, pk3) IN ((2, 3, 6), (2, 3, 4)) AND (pk3, pk4, pk5) IN ((4, 5, 6), (4, 3, 2))";
List<String> expecteds = singletonList("row2");
-
+
testWithIntegerTypesWithVariedSaltingAndTenancy(DEFAULT_UPSERT_BODIES, whereClause, expecteds);
}
-
+
@Test
public void testOverlappingRVCAndRVCPartiallyQualifiedBegin() throws Exception {
String whereClause = "WHERE (pk1, pk2) IN ((1, 2), (2, 3)) AND (pk2, pk3) IN ((3, 4), (3, 6))";
List<String> expecteds = Arrays.asList("row2", "row3");
-
+
testWithIntegerTypesWithVariedSaltingAndTenancy(DEFAULT_UPSERT_BODIES, whereClause, expecteds);
}
-
+
@Test
public void testOverlappingRVCAndRVCPartiallyQualifiedEnd() throws Exception {
String whereClause = "WHERE (pk3, pk4) IN ((4, 5), (4, 3)) AND (pk4, pk5) IN ((3, 2), (4, 5))";
List<String> expecteds = singletonList("row4");
-
+
testWithIntegerTypesWithVariedSaltingAndTenancy(DEFAULT_UPSERT_BODIES, whereClause, expecteds);
}
-
+
@Test
public void testWithFixedLengthDescPK() throws Exception {
testWithFixedLengthPK(SortOrder.DESC);
}
-
+
@Test
public void testWithFixedLengthAscPK() throws Exception {
- testWithFixedLengthPK(SortOrder.ASC);
+ testWithFixedLengthPK(SortOrder.ASC);
}
-
+
@Test
public void testWithFixedLengthKV() throws Exception {
Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
@@ -463,7 +477,7 @@ public class InListIT extends ParallelStatsDisabledIT {
assertTrue(rs.next());
assertEquals("bb", rs.getString(1));
assertFalse(rs.next());
-
+
conn.close();
}
@@ -483,7 +497,7 @@ public class InListIT extends ParallelStatsDisabledIT {
assertTrue(rs.next());
assertEquals(sortOrder == SortOrder.ASC ? "bb" : "aa", rs.getString(1));
assertFalse(rs.next());
-
+
conn.close();
}
@@ -534,7 +548,7 @@ public class InListIT extends ParallelStatsDisabledIT {
assertEquals(0, rs.getInt(1));
}
}
-
+
@Test
public void testInListExpressionWithDesc() throws Exception {
String fullTableName = generateUniqueName();
@@ -543,38 +557,38 @@ public class InListIT extends ParallelStatsDisabledIT {
// create base table and global view using global connection
try (Connection conn = DriverManager.getConnection(getUrl())) {
Statement stmt = conn.createStatement();
- stmt.execute("CREATE TABLE " + fullTableName + "(\n" +
- " TENANT_ID CHAR(15) NOT NULL,\n" +
- " KEY_PREFIX CHAR(3) NOT NULL,\n" +
- " CREATED_DATE DATE,\n" +
- " CREATED_BY CHAR(15),\n" +
- " SYSTEM_MODSTAMP DATE\n" +
- " CONSTRAINT PK PRIMARY KEY (\n" +
- " TENANT_ID," +
- " KEY_PREFIX" +
+ stmt.execute("CREATE TABLE " + fullTableName + "(\n" +
+ " TENANT_ID CHAR(15) NOT NULL,\n" +
+ " KEY_PREFIX CHAR(3) NOT NULL,\n" +
+ " CREATED_DATE DATE,\n" +
+ " CREATED_BY CHAR(15),\n" +
+ " SYSTEM_MODSTAMP DATE\n" +
+ " CONSTRAINT PK PRIMARY KEY (\n" +
+ " TENANT_ID," +
+ " KEY_PREFIX" +
")) MULTI_TENANT=TRUE");
-
- stmt.execute("CREATE VIEW " + fullViewName + "(\n" +
- " MODEL VARCHAR NOT NULL,\n" +
- " MILEAGE BIGINT NOT NULL,\n" +
- " MILES_DRIVEN BIGINT NOT NULL,\n" +
- " MAKE VARCHAR,\n" +
- " CONSTRAINT PKVIEW PRIMARY KEY\n" +
- " (\n" +
- " MODEL, MILEAGE DESC, MILES_DRIVEN\n" +
+
+ stmt.execute("CREATE VIEW " + fullViewName + "(\n" +
+ " MODEL VARCHAR NOT NULL,\n" +
+ " MILEAGE BIGINT NOT NULL,\n" +
+ " MILES_DRIVEN BIGINT NOT NULL,\n" +
+ " MAKE VARCHAR,\n" +
+ " CONSTRAINT PKVIEW PRIMARY KEY\n" +
+ " (\n" +
+ " MODEL, MILEAGE DESC, MILES_DRIVEN\n" +
")) AS SELECT * FROM " + fullTableName + " WHERE KEY_PREFIX = '0CY'");
-
+
}
-
+
// create and use a tenant specific view to write data
- try (Connection viewConn = DriverManager.getConnection(TENANT_SPECIFIC_URL1) ) {
+ try (Connection viewConn = DriverManager.getConnection(TENANT_SPECIFIC_URL1) ) {
Statement stmt = viewConn.createStatement();
stmt.execute("CREATE VIEW IF NOT EXISTS " + tenantView + " AS SELECT * FROM " + fullViewName );
viewConn.createStatement().execute("UPSERT INTO " + tenantView + "(CREATED_BY, CREATED_DATE, SYSTEM_MODSTAMP, MODEL, MILEAGE, MILES_DRIVEN, MAKE) VALUES ('005xx000001Sv6o', 1532458254819, 1532458254819, 'a5', 23, 10000, 'AUDI')");
viewConn.createStatement().execute("UPSERT INTO " + tenantView + "(CREATED_BY, CREATED_DATE, SYSTEM_MODSTAMP, MODEL, MILEAGE, MILES_DRIVEN, MAKE) VALUES ('005xx000001Sv6o', 1532458254819, 1532458254819, 'a4', 27, 30000, 'AUDI')");
viewConn.createStatement().execute("UPSERT INTO " + tenantView + "(CREATED_BY, CREATED_DATE, SYSTEM_MODSTAMP, MODEL, MILEAGE, MILES_DRIVEN, MAKE) VALUES ('005xx000001Sv6o', 1532458254819, 1532458254819, '328i', 32, 40000, 'BMW')");
viewConn.commit();
-
+
ResultSet rs = stmt.executeQuery("SELECT Make, Model FROM " + tenantView + " WHERE MILEAGE IN (32, 27)");
assertTrue(rs.next());
assertEquals("BMW", rs.getString(1));
@@ -603,4 +617,900 @@ public class InListIT extends ParallelStatsDisabledIT {
}
}
+ private void buildSchema(String fullTableName, String fullViewName, boolean isDecOrder) throws Exception {
+ try (Connection conn = DriverManager.getConnection(getUrl())) {
+ conn.setAutoCommit(true);
+ try (Statement stmt = conn.createStatement()) {
+ stmt.execute("CREATE TABLE " + fullTableName + "(\n" + " TENANT_ID CHAR(15) NOT NULL,\n" + " KEY_PREFIX CHAR(3) NOT NULL, ID5 BIGINT \n" +
+ " CONSTRAINT PK PRIMARY KEY (\n" + " TENANT_ID," + " KEY_PREFIX" + ")) MULTI_TENANT=TRUE");
+ if (isDecOrder) {
+ stmt.execute("CREATE VIEW " + fullViewName + "(\n" + " ID1 VARCHAR NOT NULL,\n" + " ID2 VARCHAR NOT NULL,\n" + " ID3 BIGINT, ID4 BIGINT \n" +
+ " CONSTRAINT PKVIEW PRIMARY KEY\n" + " (\n" + " ID1, ID2 DESC\n" + ")) " +
+ "AS SELECT * FROM " + fullTableName + " WHERE KEY_PREFIX = '0CY'");
+ try (Connection viewConn = DriverManager.getConnection(TENANT_SPECIFIC_URL1)) {
+ viewConn.setAutoCommit(true);
+ try (Statement tenantStmt = viewConn.createStatement()) {
+ tenantStmt.execute("CREATE VIEW IF NOT EXISTS " + this.descViewName + " AS SELECT * FROM " + fullViewName);
+ }
+ }
+ } else {
+ stmt.execute("CREATE VIEW " + fullViewName + "(\n" + " ID1 VARCHAR NOT NULL,\n" + " ID2 VARCHAR NOT NULL,\n" + " ID3 BIGINT, ID4 BIGINT \n" +
+ " CONSTRAINT PKVIEW PRIMARY KEY\n" + " (ID1, ID2)) " +
+ "AS SELECT * FROM " + fullTableName + " WHERE KEY_PREFIX = '0CY'");
+ try (Connection viewConn = DriverManager.getConnection(TENANT_SPECIFIC_URL1)) {
+ viewConn.setAutoCommit(true);
+ try (Statement tenantStmt = viewConn.createStatement()) {
+ tenantStmt.execute("CREATE VIEW IF NOT EXISTS " + this.ascViewName + " AS SELECT * FROM " + fullViewName);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testPkDescOrderedTenantViewOnGlobalViewWithRightQueryPlan() throws Exception {
+ String tenantView = generateUniqueName();
+
+ try (Connection viewConn = DriverManager.getConnection(TENANT_SPECIFIC_URL1)) {
+ viewConn.setAutoCommit(true);
+ try (Statement stmt = viewConn.createStatement()) {
+ stmt.execute("CREATE VIEW IF NOT EXISTS " + tenantView + " AS SELECT * FROM " + descViewName);
+ viewConn.createStatement().execute("UPSERT INTO " + tenantView + "(ID1, ID2) VALUES " +
+ "('foo', '000000000000300')");
+ viewConn.createStatement().execute("UPSERT INTO " + tenantView + "(ID1, ID2) VALUES " +
+ "('005xx000001Sv6o', '000000000000500')");
+ viewConn.commit();
+
+ try (PreparedStatement preparedStmt = viewConn.prepareStatement(
+ "SELECT * FROM " + tenantView + " WHERE (ID1, ID2) " +
+ "IN (('005xx000001Sv6o', '000000000000500'))")) {
+ QueryPlan queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt);
+ assertTrue(queryPlan.getExplainPlan().toString().contains(ExplainTable.POINT_LOOKUP_ON_STRING));
+ }
+
+ try (PreparedStatement preparedStmt = viewConn.prepareStatement(
+ "SELECT * FROM " + tenantView + " WHERE (ID2, ID1) " +
+ "IN (('000000000000500', '005xx000001Sv6o'))")) {
+ QueryPlan queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt);
+ assertTrue(queryPlan.getExplainPlan().toString().contains(ExplainTable.POINT_LOOKUP_ON_STRING));
+ }
+
+ stmt.execute("DELETE FROM " + tenantView + " WHERE (ID2, ID1) IN " +
+ "(('bar', '005xx000001Sv6o'))");
+
+ ResultSet rs = stmt.executeQuery("SELECT ID2 FROM " + tenantView);
+ assertTrue(rs.next());
+ assertEquals("000000000000500", rs.getString(1));
+ }
+ }
+ }
+
+ @Test
+ public void testColumnDescOrderedTenantViewOnGlobalViewWithStringValue() throws Exception {
+ String tenantView = generateUniqueName();
+
+ try (Connection viewConn = DriverManager.getConnection(TENANT_SPECIFIC_URL1)) {
+ viewConn.setAutoCommit(true);
+ try (Statement stmt = viewConn.createStatement()) {
+ stmt.execute("CREATE VIEW IF NOT EXISTS " + tenantView + " AS SELECT * FROM " + descViewName);
+ viewConn.createStatement().execute("UPSERT INTO " + tenantView + "(ID1, ID2) VALUES " +
+ "('foo', '000000000000300')");
+ viewConn.createStatement().execute("UPSERT INTO " + tenantView + "(ID1, ID2) VALUES " +
+ "('bar', '000000000000400')");
+ viewConn.createStatement().execute("UPSERT INTO " + tenantView + "(ID1, ID2) VALUES " +
+ "('005xx000001Sv6o', '000000000000500')");
+ viewConn.commit();
+
+ try (PreparedStatement preparedStmt = viewConn.prepareStatement(
+ "SELECT * FROM " + tenantView + " WHERE (ID1, ID2) " +
+ "IN (('005xx000001Sv6o', '000000000000500')," +
+ "('bar', '000000000000400')," +
+ "('foo', '000000000000300'))")) {
+ QueryPlan queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt);
+ assertTrue(queryPlan.getExplainPlan().toString().contains(ExplainTable.POINT_LOOKUP_ON_STRING));
+ }
+
+ try (PreparedStatement preparedStmt = viewConn.prepareStatement(
+ "SELECT * FROM " + tenantView + " WHERE (ID2, ID1) IN " +
+ "(('bar', '005xx000001Sv6o')," +
+ "('foo', '005xx000001Sv6o'))")) {
+ QueryPlan queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt);
+ assertTrue(queryPlan.getExplainPlan().toString().contains(ExplainTable.POINT_LOOKUP_ON_STRING));
+ }
+
+ stmt.execute("DELETE FROM " + tenantView + " WHERE (ID2, ID1) IN " +
+ "(('000000000000400', 'bar')," +
+ "('000000000000300', 'foo'))");
+
+ ResultSet rs = stmt.executeQuery("SELECT ID2 FROM " + tenantView);
+ assertTrue(rs.next());
+ assertEquals("000000000000500", rs.getString(1));
+
+ rs = stmt.executeQuery("SELECT COUNT(*) FROM " + tenantView);
+ assertTrue(rs.next());
+ assertEquals(1, rs.getInt(1));
+ stmt.execute("DELETE FROM " + tenantView);
+ }
+ }
+ }
+
+ @Test
+ public void testInListExpressionWithRightQueryPlanForTenantViewOnGlobalView() throws Exception {
+ String tenantView = generateUniqueName();
+
+ try (Connection viewConn = DriverManager.getConnection(TENANT_SPECIFIC_URL1)) {
+ viewConn.setAutoCommit(true);
+ try (Statement stmt = viewConn.createStatement()) {
+ stmt.execute("CREATE VIEW IF NOT EXISTS " + tenantView + " AS SELECT * FROM " + ascViewName);
+
+ viewConn.createStatement().execute("UPSERT INTO " + tenantView + "(ID1, ID2) VALUES " +
+ "('005xx000001Sv6o', '000000000000300')");
+ viewConn.createStatement().execute("UPSERT INTO " + tenantView + "(ID1, ID2) VALUES " +
+ "('005xx000001Sv6o', '000000000000400')");
+ viewConn.commit();
+
+ try (PreparedStatement preparedStmt = viewConn.prepareStatement(
+ "SELECT * FROM " + tenantView + " WHERE (ID1, ID2) IN " +
+ "(('005xx000001Sv6o', '000000000000500')," +
+ "('005xx000001Sv6o', '000000000000400')," +
+ "('005xx000001Sv6o', '000000000000300'))")) {
+ QueryPlan queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt);
+ assertTrue(queryPlan.getExplainPlan().toString().contains(ExplainTable.POINT_LOOKUP_ON_STRING));
+ }
+
+ try (PreparedStatement preparedStmt = viewConn.prepareStatement(
+ "SELECT * FROM " + tenantView + " WHERE (ID2, ID1) IN " +
+ "(('000000000000400', '005xx000001Sv6o')," +
+ "('000000000000300', '005xx000001Sv6o'))")) {
+ QueryPlan queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt);
+ assertTrue(queryPlan.getExplainPlan().toString().contains(ExplainTable.POINT_LOOKUP_ON_STRING));
+ }
+ ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM " + tenantView + " WHERE (ID2, ID1) IN " +
+ "(('000000000000400', '005xx000001Sv6o')," +
+ "('000000000000300', '005xx000001Sv6o'))");
+ assertTrue(rs.next());
+
+ stmt.execute("DELETE FROM " + tenantView + " WHERE (ID2, ID1) IN " +
+ "(('000000000000300', '005xx000001Sv6o'))");
+
+ rs = stmt.executeQuery("SELECT COUNT(*) FROM " + tenantView);
+ assertTrue(rs.next());
+ assertEquals(1, rs.getInt(1));
+ }
+ }
+ }
+
+ @Test
+ public void testInListExpressionGeneratesRightScanForAsc() throws Exception {
+ testFullPkListPlan(this.ascViewName);
+ testPartialPkListPlan(this.ascViewName);
+ testPartialPkPlusNonPkListPlan(this.ascViewName);
+ testNonPkListPlan(this.ascViewName);
+ }
+
+ @Test
+ public void testInListExpressionGeneratesRightScanForDesc() throws Exception {
+ testFullPkListPlan(this.descViewName);
+ testPartialPkListPlan(this.descViewName);
+ testPartialPkPlusNonPkListPlan(this.descViewName);
+ testNonPkListPlan(this.descViewName);
+ }
+
+ private void testFullPkListPlan(String tenantView) throws Exception {
+ Long numberOfRowsToScan = new Long(2);
+ try (Connection viewConn = DriverManager.getConnection(TENANT_SPECIFIC_URL1) ) {
+ PreparedStatement preparedStmt = viewConn.prepareStatement("SELECT * FROM " + tenantView + " WHERE (ID1, ID2) IN " +
+ "(('005xx000001Sv6o', '000000000000500')," +
+ "('005xx000001Sv6o', '000000000000400'))");
+ QueryPlan queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt);
+ assertEquals(numberOfRowsToScan, queryPlan.getEstimatedRowsToScan());
+ assertTrue(queryPlan.getExplainPlan().toString().contains(ExplainTable.POINT_LOOKUP_ON_STRING));
+
+ viewConn.prepareStatement("DELETE FROM " + tenantView + " WHERE (ID1, ID2) IN " +
+ "(('005xx000001Sv6o', '000000000000500')," +
+ "('005xx000001Sv6o', '000000000000400'))");
+ queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt);
+ assertEquals(numberOfRowsToScan, queryPlan.getEstimatedRowsToScan());
+ assertTrue(queryPlan.getExplainPlan().toString().contains(ExplainTable.POINT_LOOKUP_ON_STRING));
+ }
+ }
+
+ private void testPartialPkListPlan(String tenantView) throws Exception {
+ try (Connection viewConn = DriverManager.getConnection(TENANT_SPECIFIC_URL1) ) {
+ PreparedStatement preparedStmt = viewConn.prepareStatement("SELECT * FROM " + tenantView + " WHERE (ID1) IN " +
+ "(('005xx000001Sv6o')," +
+ "('005xx000001Sv6o'))");
+ QueryPlan queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt);
+ assertTrue(queryPlan.getExplainPlan().toString().contains("CLIENT PARALLEL 1-WAY RANGE SCAN OVER"));
+
+ viewConn.prepareStatement("DELETE FROM " + tenantView + " WHERE (ID1) IN " +
+ "(('005xx000001Sv6o')," +
+ "('005xx000001Sv6o'))");
+ queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt);
+ assertTrue(queryPlan.getExplainPlan().toString().contains("CLIENT PARALLEL 1-WAY RANGE SCAN OVER"));
+
+ preparedStmt = viewConn.prepareStatement("SELECT * FROM " + tenantView + " WHERE (ID2) IN " +
+ "(('000000000000500')," +
+ "('000000000000400'))");
+ queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt);
+ assertTrue(queryPlan.getExplainPlan().toString().contains("CLIENT PARALLEL 1-WAY RANGE SCAN OVER"));
+
+ viewConn.prepareStatement("DELETE FROM " + tenantView + " WHERE (ID2) IN " +
+ "(('000000000000500')," +
+ "('000000000000400'))");
+ queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt);
+ assertTrue(queryPlan.getExplainPlan().toString().contains("CLIENT PARALLEL 1-WAY RANGE SCAN OVER"));
+ }
+ }
+
+ private void testPartialPkPlusNonPkListPlan(String tenantView) throws Exception {
+ try (Connection viewConn = DriverManager.getConnection(TENANT_SPECIFIC_URL1) ) {
+ PreparedStatement preparedStmt = viewConn.prepareStatement("SELECT * FROM " + tenantView + " WHERE (ID1, ID3) IN " +
+ "(('005xx000001Sv6o', 1)," +
+ "('005xx000001Sv6o', 2))");
+ QueryPlan queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt);
+ assertTrue(queryPlan.getExplainPlan().toString().contains("CLIENT PARALLEL 1-WAY RANGE SCAN OVER"));
+
+ viewConn.prepareStatement("DELETE FROM " + tenantView + " WHERE (ID1, ID3) IN " +
+ "(('005xx000001Sv6o', 1)," +
+ "('005xx000001Sv6o', 2))");
+ queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt);
+ assertTrue(queryPlan.getExplainPlan().toString().contains("CLIENT PARALLEL 1-WAY RANGE SCAN OVER"));
+
+ preparedStmt = viewConn.prepareStatement("SELECT * FROM " + tenantView + " WHERE (ID2, ID3) IN " +
+ "(('000000000000500', 1)," +
+ "('000000000000400', 2))");
+ queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt);
+ assertTrue(queryPlan.getExplainPlan().toString().contains("CLIENT PARALLEL 1-WAY RANGE SCAN OVER"));
+
+ viewConn.prepareStatement("DELETE FROM " + tenantView + " WHERE (ID2, ID3) IN " +
+ "(('000000000000500', 1)," +
+ "('000000000000400', 2))");
+ queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt);
+ assertTrue(queryPlan.getExplainPlan().toString().contains("CLIENT PARALLEL 1-WAY RANGE SCAN OVER"));
+ }
+ }
+
+ private void testNonPkListPlan(String tenantView) throws Exception {
+ // Tenant connection should generate a range scan because tenant id is the leading PK.
+ try (Connection viewConn = DriverManager.getConnection(TENANT_SPECIFIC_URL1) ) {
+ PreparedStatement preparedStmt = viewConn.prepareStatement("SELECT * FROM " + tenantView + " WHERE (ID3, ID4) IN " +
+ "((1, 1)," +
+ "(2, 2))");
+ QueryPlan queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt);
+ assertTrue(queryPlan.getExplainPlan().toString().contains("CLIENT PARALLEL 1-WAY RANGE SCAN OVER"));
+
+ viewConn.prepareStatement("DELETE FROM " + tenantView + " WHERE (ID3, ID4) IN " +
+ "((1, 1)," +
+ "(2, 2))");
+ queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt);
+ assertTrue(queryPlan.getExplainPlan().toString().contains("CLIENT PARALLEL 1-WAY RANGE SCAN OVER"));
+ }
+ }
+
+ @Test
+ public void testInListExpressionWithRightQueryPlanForNumericalValue() throws Exception {
+ String tenantView = generateUniqueName();
+
+ try (Connection viewConn = DriverManager.getConnection(TENANT_SPECIFIC_URL1)) {
+ viewConn.setAutoCommit(true);
+ try (Statement stmt = viewConn.createStatement()) {
+ stmt.execute("CREATE VIEW IF NOT EXISTS " + tenantView + "(DOUBLE1 DOUBLE NOT NULL, INT1 BIGINT NOT NULL " +
+ " CONSTRAINT PKVIEW PRIMARY KEY\n" + " (DOUBLE1, INT1)) " +
+ " AS SELECT * FROM " + tableName + " WHERE KEY_PREFIX = 'ABC'");
+
+ viewConn.createStatement().execute("UPSERT INTO " + tenantView + "(DOUBLE1, INT1) VALUES " +
+ "(12.0, 8)");
+ viewConn.createStatement().execute("UPSERT INTO " + tenantView + "(DOUBLE1, INT1) VALUES " +
+ "(13.0, 9)");
+ viewConn.commit();
+
+ ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM " + tenantView + " WHERE (INT1, DOUBLE1) IN " +
+ "((8, 12.0)," +
+ "(9, 13.0))");
+ assertTrue(rs.next());
+ assertEquals(2, rs.getInt(1));
+
+ stmt.execute("DELETE FROM " + tenantView + " WHERE (INT1, DOUBLE1) IN " +
+ "((8, 12.0)," +
+ "(9, 13.0))");
+
+ rs = stmt.executeQuery("SELECT COUNT(*) FROM " + tenantView);
+ assertTrue(rs.next());
+ assertEquals(0, rs.getInt(1));
+ }
+ }
+ }
+
+ @Test
+ public void testQueryPlanForPkDescOrderedTenantViewOnGlobalViewForStringValue() throws Exception {
+ String tenantView = generateUniqueName();
+
+ try (Connection viewConn = DriverManager.getConnection(TENANT_SPECIFIC_URL1)) {
+ viewConn.setAutoCommit(true);
+ try (Statement stmt = viewConn.createStatement()) {
+ stmt.execute("CREATE VIEW IF NOT EXISTS " + tenantView + " AS SELECT * FROM " + descViewName);
+ viewConn.createStatement().execute("UPSERT INTO " + tenantView + "(ID1, ID2) VALUES " +
+ "('foo', '000000000000300')");
+ viewConn.createStatement().execute("UPSERT INTO " + tenantView + "(ID1, ID2) VALUES " +
+ "('bar', '000000000000400')");
+ viewConn.createStatement().execute("UPSERT INTO " + tenantView + "(ID1, ID2) VALUES " +
+ "('005xx000001Sv6o', '000000000000500')");
+ viewConn.commit();
+
+ ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM " + tenantView + " WHERE (ID2, ID1) IN " +
+ "(('000000000000400', 'bar')," +
+ "('000000000000300','foo'))");
+
+ assertTrue(rs.next());
+ assertEquals(2, rs.getInt(1));
+
+ stmt.execute("DELETE FROM " + tenantView + " WHERE (ID2, ID1) IN " +
+ "(('000000000000400', 'bar')," +
+ "('000000000000300','foo'))");
+
+ rs = stmt.executeQuery("SELECT ID2 FROM " + tenantView);
+ assertTrue(rs.next());
+ assertEquals("000000000000500", rs.getString(1));
+
+ rs = stmt.executeQuery("SELECT COUNT(*) FROM " + tenantView);
+ assertTrue(rs.next());
+ assertEquals(1, rs.getInt(1));
+ stmt.execute("DELETE FROM " + tenantView);
+ }
+ }
+ }
+
+ @Test
+ public void testQueryPlanForTenantViewOnBaseTableWithVarcharValue() throws Exception {
+ String tenantView = generateUniqueName();
+
+ try (Connection viewConn = DriverManager.getConnection(TENANT_SPECIFIC_URL1)) {
+ viewConn.setAutoCommit(true);
+ try (Statement stmt = viewConn.createStatement()) {
+ stmt.execute("CREATE VIEW IF NOT EXISTS " + tenantView + "(DOUBLE1 VARCHAR NOT NULL, INT1 VARCHAR NOT NULL " +
+ " CONSTRAINT PKVIEW PRIMARY KEY\n" + " (DOUBLE1, INT1)) " +
+ " AS SELECT * FROM " + tableName + " WHERE KEY_PREFIX = 'ABC'");
+
+ viewConn.createStatement().execute("UPSERT INTO " + tenantView + "(DOUBLE1, INT1) VALUES " +
+ "('12.0', '8')");
+ viewConn.createStatement().execute("UPSERT INTO " + tenantView + "(DOUBLE1, INT1) VALUES " +
+ "('13.0', '9')");
+ viewConn.commit();
+
+ ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM " + tenantView + " WHERE (INT1, DOUBLE1) IN " +
+ "(('8', '12.0')," +
+ "('9', '13.0'))");
+ assertTrue(rs.next());
+ assertEquals(2, rs.getInt(1));
+
+ stmt.execute("DELETE FROM " + tenantView + " WHERE (INT1, DOUBLE1) IN " +
+ "(('8', '12.0')," +
+ "('9', '13.0'))");
+
+ rs = stmt.executeQuery("SELECT COUNT(*) FROM " + tenantView);
+ assertTrue(rs.next());
+ assertEquals(0, rs.getInt(1));
+ }
+ }
+ }
+
+ @Test
+ public void testQueryPlanForTenantViewOnBaseTableWithNumericalValue() throws Exception {
+ String tenantView = generateUniqueName();
+
+ try (Connection viewConn = DriverManager.getConnection(TENANT_SPECIFIC_URL1)) {
+ viewConn.setAutoCommit(true);
+ try (Statement stmt = viewConn.createStatement()) {
+ stmt.execute("CREATE VIEW IF NOT EXISTS " + tenantView +
+ " (ID1 BIGINT NOT NULL, ID2 BIGINT NOT NULL, ID4 BIGINT " +
+ " CONSTRAINT PKVIEW PRIMARY KEY " + " (ID1, ID2)) " +
+ " AS SELECT * FROM " + tableName + " WHERE KEY_PREFIX = 'ABC'");
+
+ viewConn.createStatement().execute("UPSERT INTO " + tenantView + "(ID1, ID2, ID5, ID4) VALUES " +
+ "(12, 8, 7, 6)");
+ viewConn.createStatement().execute("UPSERT INTO " + tenantView + "(ID1, ID2, ID5, ID4) VALUES " +
+ "(13, 9, 13, 9)");
+ viewConn.commit();
+
+ ResultSet rs = stmt.executeQuery("SELECT ID1, ID2, ID5, ID4 FROM " + tenantView +
+ " WHERE (ID1, ID5) IN " +
+ "((12, 7)," +
+ "(12, 13))");
+ assertTrue(rs.next());
+ assertEquals(12, rs.getInt(1));
+ assertEquals(8, rs.getInt(2));
+ assertEquals(7, rs.getInt(3));
+ assertEquals(6, rs.getInt(4));
+ assertTrue(!rs.next());
+
+ rs = stmt.executeQuery("SELECT ID1, ID2, ID5, ID4 FROM " + tenantView + " WHERE (ID4, ID1) IN " +
+ "((9, 13)," +
+ "(12, 13))");
+ assertTrue(rs.next());
+ assertEquals(13, rs.getInt(1));
+ assertEquals(9, rs.getInt(2));
+ assertEquals(13, rs.getInt(3));
+ assertEquals(9, rs.getInt(4));
+ assertTrue(!rs.next());
+
+ rs = stmt.executeQuery("SELECT ID1, ID2, ID5, ID4 FROM " + tenantView + " WHERE (ID2, ID1) IN " +
+ "((8, 12)," +
+ "(9, 13))");
+ assertTrue(rs.next());
+ assertEquals(12, rs.getInt(1));
+ assertEquals(8, rs.getInt(2));
+ assertEquals(7, rs.getInt(3));
+ assertEquals(6, rs.getInt(4));
+ assertTrue(rs.next());
+ assertEquals(13, rs.getInt(1));
+ assertEquals(9, rs.getInt(2));
+ assertEquals(13, rs.getInt(3));
+ assertEquals(9, rs.getInt(4));
+ assertTrue(!rs.next());
+
+ stmt.execute("DELETE FROM " + tenantView + " WHERE (ID2, ID1) IN " +
+ "((8, 12)," +
+ "(9, 13))");
+
+ rs = stmt.executeQuery("SELECT COUNT(*) FROM " + tenantView);
+ assertTrue(rs.next());
+ assertEquals(0, rs.getInt(1));
+ }
+ }
+ }
+
+ @Test
+ public void testInListExpressionWithFunction() throws Exception {
+ String tenantView = generateUniqueName();
+
+ try (Connection viewConn = DriverManager.getConnection(TENANT_SPECIFIC_URL1)) {
+ viewConn.setAutoCommit(true);
+ try (Statement stmt = viewConn.createStatement()) {
+ stmt.execute("CREATE VIEW IF NOT EXISTS " + tenantView + "(ID1 BIGINT NOT NULL, ID2 BIGINT NOT NULL, ID4 BIGINT " +
+ " CONSTRAINT PKVIEW PRIMARY KEY\n" + " (ID1, ID2)) " +
+ " AS SELECT * FROM " + tableName + " WHERE KEY_PREFIX = 'ABC'");
+
+ viewConn.createStatement().execute("UPSERT INTO " + tenantView + "(ID1, ID2, ID5, ID4) VALUES " +
+ "(12, 8, 7, 6)");
+ viewConn.createStatement().execute("UPSERT INTO " + tenantView + "(ID1, ID2, ID5, ID4) VALUES " +
+ "(13, 9, 13, 9)");
+ viewConn.commit();
+
+ ResultSet rs = stmt.executeQuery("SELECT ID1, ID2, ID5, ID4 FROM " + tenantView + " WHERE (ID1 + 1, ID5) IN " +
+ "((13, 7)," +
+ "(13, 13))");
+ assertTrue(rs.next());
+ assertEquals(12, rs.getInt(1));
+ assertEquals(8, rs.getInt(2));
+ assertEquals(7, rs.getInt(3));
+ assertEquals(6, rs.getInt(4));
+ assertTrue(!rs.next());
+
+ rs = stmt.executeQuery("SELECT ID1, ID2, ID5, ID4 FROM " + tenantView + " WHERE (ID4 - 1, ID1) IN " +
+ "((8, 13)," +
+ "(11, 13))");
+ assertTrue(rs.next());
+ assertEquals(13, rs.getInt(1));
+ assertEquals(9, rs.getInt(2));
+ assertEquals(13, rs.getInt(3));
+ assertEquals(9, rs.getInt(4));
+ assertTrue(!rs.next());
+
+ rs = stmt.executeQuery("SELECT ID1, ID2, ID5, ID4 FROM " + tenantView + " WHERE (ID2 + 1 , ID1 - 1) IN " +
+ "((9, 11)," +
+ "(10, 12))");
+ assertTrue(rs.next());
+ assertEquals(12, rs.getInt(1));
+ assertEquals(8, rs.getInt(2));
+ assertEquals(7, rs.getInt(3));
+ assertEquals(6, rs.getInt(4));
+ assertTrue(rs.next());
+ assertEquals(13, rs.getInt(1));
+ assertEquals(9, rs.getInt(2));
+ assertEquals(13, rs.getInt(3));
+ assertEquals(9, rs.getInt(4));
+ assertTrue(!rs.next());
+
+ stmt.execute("DELETE FROM " + tenantView + " WHERE (ID2 -1, ID1 + 1) IN " +
+ "((7, 13)," +
+ "(8, 14))");
+
+ rs = stmt.executeQuery("SELECT COUNT(*) FROM " + tenantView);
+ assertTrue(rs.next());
+ assertEquals(0, rs.getInt(1));
+ }
+ }
+ }
+
+ @Test
+ public void testInListExpressionWithFunctionAndIndex() throws Exception {
+ String tenantView = generateUniqueName();
+ String tenantIndexView = generateUniqueName();
+
+ try (Connection viewConn = DriverManager.getConnection(TENANT_SPECIFIC_URL1)) {
+ viewConn.setAutoCommit(true);
+ try (Statement stmt = viewConn.createStatement()) {
+ stmt.execute("CREATE VIEW IF NOT EXISTS " + tenantView + "(ID1 BIGINT NOT NULL, ID2 BIGINT NOT NULL, ID4 BIGINT " +
+ " CONSTRAINT PKVIEW PRIMARY KEY\n" + " (ID1, ID2)) " +
+ " AS SELECT * FROM " + tableName + " WHERE KEY_PREFIX = 'ABC'");
+
+ viewConn.createStatement().execute("UPSERT INTO " + tenantView + "(ID1, ID2, ID5, ID4) VALUES " +
+ "(12, 8, 7, 6)");
+ viewConn.createStatement().execute("UPSERT INTO " + tenantView + "(ID1, ID2, ID5, ID4) VALUES " +
+ "(13, 9, 13, 9)");
+ viewConn.commit();
+
+ stmt.execute("CREATE INDEX " + tenantIndexView + " ON " + tenantView + " (ID5) INCLUDE (ID4, ID1)");
+
+ ResultSet rs = stmt.executeQuery("SELECT ID1, ID2, ID5, ID4 FROM " + tenantView + " WHERE (ID1 + 1, ID5) IN " +
+ "((13, 7)," +
+ "(13, 13))");
+ assertTrue(rs.next());
+ assertEquals(12, rs.getInt(1));
+ assertEquals(8, rs.getInt(2));
+ assertEquals(7, rs.getInt(3));
+ assertEquals(6, rs.getInt(4));
+ assertTrue(!rs.next());
+
+ rs = stmt.executeQuery("SELECT ID1, ID2, ID5, ID4 FROM " + tenantView + " WHERE (ID5 - 1, ID1) IN " +
+ "((6, 13)," +
+ "(12, 13))");
+ assertTrue(rs.next());
+ assertEquals(13, rs.getInt(1));
+ assertEquals(9, rs.getInt(2));
+ assertEquals(13, rs.getInt(3));
+ assertEquals(9, rs.getInt(4));
+ assertTrue(!rs.next());
+
+ rs = stmt.executeQuery("SELECT ID1, ID2, ID5, ID4 FROM " + tenantView + " WHERE (ID5 + 1 , ID1 - 1) IN " +
+ "((8, 11)," +
+ "(14, 12))");
+ assertTrue(rs.next());
+ assertEquals(12, rs.getInt(1));
+ assertEquals(8, rs.getInt(2));
+ assertEquals(7, rs.getInt(3));
+ assertEquals(6, rs.getInt(4));
+ assertTrue(rs.next());
+ assertEquals(13, rs.getInt(1));
+ assertEquals(9, rs.getInt(2));
+ assertEquals(13, rs.getInt(3));
+ assertEquals(9, rs.getInt(4));
+ assertTrue(!rs.next());
+
+ stmt.execute("DELETE FROM " + tenantView + " WHERE (ID5 -1, ID1 + 1) IN " +
+ "((6, 13)," +
+ "(12, 14))");
+
+ rs = stmt.executeQuery("SELECT COUNT(*) FROM " + tenantView);
+ assertTrue(rs.next());
+ assertEquals(0, rs.getInt(1));
+ }
+ }
+ }
+
+ @Test
+ public void testInListExpressionWithIndex() throws Exception {
+ String tenantView = generateUniqueName();
+ String tenantIndexView = generateUniqueName();
+
+ try (Connection viewConn = DriverManager.getConnection(TENANT_SPECIFIC_URL1)) {
+ viewConn.setAutoCommit(true);
+ try (Statement stmt = viewConn.createStatement()) {
+ stmt.execute("CREATE VIEW IF NOT EXISTS " + tenantView + "(ID1 BIGINT NOT NULL, ID2 BIGINT NOT NULL, ID4 BIGINT " +
+ " CONSTRAINT PKVIEW PRIMARY KEY\n" + " (ID1, ID2)) " +
+ " AS SELECT * FROM " + tableName + " WHERE KEY_PREFIX = 'ABC'");
+
+ viewConn.createStatement().execute("UPSERT INTO " + tenantView + "(ID1, ID2, ID5, ID4) VALUES " +
+ "(12, 8, 7, 6)");
+ viewConn.createStatement().execute("UPSERT INTO " + tenantView + "(ID1, ID2, ID5, ID4) VALUES " +
+ "(13, 9, 13, 9)");
+ viewConn.commit();
+
+ stmt.execute("CREATE INDEX " + tenantIndexView + " ON " + tenantView + " (ID5) INCLUDE (ID4)");
+
+ ResultSet rs = stmt.executeQuery("SELECT ID1, ID2, ID5, ID4 FROM " + tenantView + " WHERE (ID5, ID1) IN " +
+ "((7, 12)," +
+ "(7, 13))");
+ assertTrue(rs.next());
+ assertEquals(12, rs.getInt(1));
+ assertEquals(8, rs.getInt(2));
+ assertEquals(7, rs.getInt(3));
+ assertEquals(6, rs.getInt(4));
+ assertTrue(!rs.next());
+
+ rs = stmt.executeQuery("SELECT ID1, ID2, ID5, ID4 FROM " + tenantView + " WHERE (ID2, ID5) IN " +
+ "((8, 13)," +
+ "(9, 13))");
+ assertTrue(rs.next());
+ assertEquals(13, rs.getInt(1));
+ assertEquals(9, rs.getInt(2));
+ assertEquals(13, rs.getInt(3));
+ assertEquals(9, rs.getInt(4));
+ assertTrue(!rs.next());
+
+ rs = stmt.executeQuery("SELECT ID1, ID2, ID5, ID4 FROM " + tenantView + " WHERE (ID5, ID4) IN " +
+ "((7, 6)," +
+ "(13, 9))");
+ assertTrue(rs.next());
+ assertEquals(12, rs.getInt(1));
+ assertEquals(8, rs.getInt(2));
+ assertEquals(7, rs.getInt(3));
+ assertEquals(6, rs.getInt(4));
+ assertTrue(rs.next());
+ assertEquals(13, rs.getInt(1));
+ assertEquals(9, rs.getInt(2));
+ assertEquals(13, rs.getInt(3));
+ assertEquals(9, rs.getInt(4));
+ assertTrue(!rs.next());
+
+ stmt.execute("DELETE FROM " + tenantView + " WHERE (ID1, ID5) IN " +
+ "((12, 7)," +
+ "(13, 13))");
+
+ rs = stmt.executeQuery("SELECT COUNT(*) FROM " + tenantView);
+ assertTrue(rs.next());
+ assertEquals(0, rs.getInt(1));
+ }
+ }
+ }
+
+ @Test
+ public void testInListExpressionWithGlobalViewAndFunction() throws Exception {
+ String tenantView = generateUniqueName();
+ String globalView = generateUniqueName();
+
+ try (Connection conn = DriverManager.getConnection(getUrl())) {
+ conn.setAutoCommit(true);
+ try (Statement stmt = conn.createStatement()) {
+ stmt.execute("CREATE VIEW " + globalView + " AS SELECT * FROM " + tableName + " WHERE KEY_PREFIX = 'ABC'");
+ }
+ }
+
+ try (Connection viewConn = DriverManager.getConnection(TENANT_SPECIFIC_URL1)) {
+ viewConn.setAutoCommit(true);
+ try (Statement stmt = viewConn.createStatement()) {
+ stmt.execute("CREATE VIEW IF NOT EXISTS " + tenantView + "(ID1 BIGINT NOT NULL, ID2 BIGINT NOT NULL, ID4 BIGINT " +
+ " CONSTRAINT PKVIEW PRIMARY KEY\n" + " (ID1, ID2)) " +
+ " AS SELECT * FROM " + globalView);
+
+ viewConn.createStatement().execute("UPSERT INTO " + tenantView + "(ID1, ID2, ID5, ID4) VALUES " +
+ "(12, 8, 7, 6)");
+ viewConn.createStatement().execute("UPSERT INTO " + tenantView + "(ID1, ID2, ID5, ID4) VALUES " +
+ "(13, 9, 13, 9)");
+ viewConn.commit();
+
+ ResultSet rs = stmt.executeQuery("SELECT ID1, ID2, ID5, ID4 FROM " + tenantView + " WHERE (ID1 + 1, ID5) IN " +
+ "((13, 7)," +
+ "(13, 13))");
+ assertTrue(rs.next());
+ assertEquals(12, rs.getInt(1));
+ assertEquals(8, rs.getInt(2));
+ assertEquals(7, rs.getInt(3));
+ assertEquals(6, rs.getInt(4));
+ assertTrue(!rs.next());
+
+ rs = stmt.executeQuery("SELECT ID1, ID2, ID5, ID4 FROM " + tenantView + " WHERE (ID4 - 1, ID1) IN " +
+ "((8, 13)," +
+ "(11, 13))");
+ assertTrue(rs.next());
+ assertEquals(13, rs.getInt(1));
+ assertEquals(9, rs.getInt(2));
+ assertEquals(13, rs.getInt(3));
+ assertEquals(9, rs.getInt(4));
+ assertTrue(!rs.next());
+
+ rs = stmt.executeQuery("SELECT ID1, ID2, ID5, ID4 FROM " + tenantView + " WHERE (ID2 + 1 , ID1 - 1) IN " +
+ "((9, 11)," +
+ "(10, 12))");
+ assertTrue(rs.next());
+ assertEquals(12, rs.getInt(1));
+ assertEquals(8, rs.getInt(2));
+ assertEquals(7, rs.getInt(3));
+ assertEquals(6, rs.getInt(4));
+ assertTrue(rs.next());
+ assertEquals(13, rs.getInt(1));
+ assertEquals(9, rs.getInt(2));
+ assertEquals(13, rs.getInt(3));
+ assertEquals(9, rs.getInt(4));
+ assertTrue(!rs.next());
+
+ stmt.execute("DELETE FROM " + tenantView + " WHERE (ID2 -1, ID1 + 1) IN " +
+ "((7, 13)," +
+ "(8, 14))");
+
+ rs = stmt.executeQuery("SELECT COUNT(*) FROM " + tenantView);
+ assertTrue(rs.next());
+ assertEquals(0, rs.getInt(1));
+ }
+ }
+ }
+
+ @Test
+ public void testGlobalViewWithPowerFunction() throws Exception {
+ String tenantView = generateUniqueName();
+ String globalView = generateUniqueName();
+
+ try (Connection conn = DriverManager.getConnection(getUrl())) {
+ conn.setAutoCommit(true);
+ try (Statement stmt = conn.createStatement()) {
+ stmt.execute("CREATE VIEW " + globalView + " AS SELECT * FROM " + tableName + " WHERE KEY_PREFIX = 'ABC'");
+ }
+ }
+
+ try (Connection viewConn = DriverManager.getConnection(TENANT_SPECIFIC_URL1)) {
+ viewConn.setAutoCommit(true);
+ try (Statement stmt = viewConn.createStatement()) {
+ stmt.execute("CREATE VIEW IF NOT EXISTS " + tenantView + "(ID1 DOUBLE NOT NULL, ID2 DOUBLE NOT NULL, ID4 BIGINT " +
+ " CONSTRAINT PKVIEW PRIMARY KEY\n" + " (ID1, ID2)) " +
+ " AS SELECT * FROM " + globalView);
+
+ viewConn.createStatement().execute("UPSERT INTO " + tenantView + "(ID1, ID2, ID5, ID4) VALUES " +
+ "(9, 2, 7, 6)");
+ viewConn.createStatement().execute("UPSERT INTO " + tenantView + "(ID1, ID2, ID5, ID4) VALUES " +
+ "(2, 9, 13, 9)");
+ viewConn.commit();
+
+ ResultSet rs = stmt.executeQuery("SELECT ID1, ID2, ID5, ID4 FROM " + tenantView + " WHERE (POWER(ID2, 2), ID1) IN " +
+ "((4.0, 9)," +
+ "(10, 12))");
+ assertTrue(rs.next());
+ assertEquals(9, rs.getInt(1));
+ assertEquals(2, rs.getInt(2));
+ assertEquals(7, rs.getInt(3));
+ assertEquals(6, rs.getInt(4));
+ assertTrue(!rs.next());
+
+ stmt.execute("DELETE FROM " + tenantView + " WHERE (POWER(ID1,2), ID2) IN " +
+ "((81, 2)," +
+ "(4, 9))");
+
+ rs = stmt.executeQuery("SELECT COUNT(*) FROM " + tenantView);
+ assertTrue(rs.next());
+ assertEquals(0, rs.getInt(1));
+ }
+ }
+ }
+
+ @Test
+ public void testBaseTableAndIndexTableHaveReversePKOrder() throws Exception {
+ String view = generateUniqueName();
+ String index = generateUniqueName();
+
+ try (Connection conn = DriverManager.getConnection(TENANT_SPECIFIC_URL1)) {
+ conn.setAutoCommit(true);
+ try (Statement stmt = conn.createStatement()) {
+ stmt.execute("CREATE VIEW " + view + " (ID1 BIGINT NOT NULL, ID2 BIGINT NOT NULL, ID4 BIGINT " +
+ "CONSTRAINT PKVIEW PRIMARY KEY (ID1, ID2)) AS SELECT * FROM " + tableName + " WHERE KEY_PREFIX = 'ABC'");
+
+ conn.createStatement().execute("UPSERT INTO " + view + "(ID1, ID2, ID5, ID4) VALUES " +
+ "(9, 2, 7, 6)");
+ conn.createStatement().execute("UPSERT INTO " + view + "(ID1, ID2, ID5, ID4) VALUES " +
+ "(2, 9, 13, 9)");
+ conn.commit();
+
+ stmt.execute("CREATE INDEX " + index + " ON " + view + " (ID2, ID1) INCLUDE (ID5, ID4)");
+
+ // TESTING for optimized scan
+ PreparedStatement preparedStmt = conn.prepareStatement("SELECT * FROM " + view + " WHERE (ID1, ID2) IN " +
+ "((1, 1)," +
+ "(2, 2))");
+ QueryPlan queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt);
+ assertEquals(new Long(2), queryPlan.getEstimatedRowsToScan());
+ assertTrue(queryPlan.getExplainPlan().toString().contains(ExplainTable.POINT_LOOKUP_ON_STRING));
+
+ preparedStmt = conn.prepareStatement("SELECT * FROM " + view + " WHERE (ID2, ID1) IN ((1, 1),(2, 2))");
+ queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt);
+ assertEquals(new Long(2), queryPlan.getEstimatedRowsToScan());
+ assertTrue(queryPlan.getExplainPlan().toString().contains(ExplainTable.POINT_LOOKUP_ON_STRING));
+
+
+ preparedStmt = conn.prepareStatement("SELECT * FROM " + view + " WHERE (ID2, ID1) IN ((1, 1),(2, 2))");
+ queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt);
+ assertEquals(new Long(2), queryPlan.getEstimatedRowsToScan());
+ assertTrue(queryPlan.getExplainPlan().toString().contains(ExplainTable.POINT_LOOKUP_ON_STRING));
+
+
+ ResultSet rs = stmt.executeQuery("SELECT ID1, ID2, ID5, ID4 FROM " + view + " WHERE (POWER(ID2, 2), ID1) IN " +
+ "((4.0, 9)," +
+ "(10, 12))");
+ assertTrue(rs.next());
+ assertEquals(9, rs.getInt(1));
+ assertEquals(2, rs.getInt(2));
+ assertEquals(7, rs.getInt(3));
+ assertEquals(6, rs.getInt(4));
+ assertTrue(!rs.next());
+
+ stmt.execute("DELETE FROM " + view + " WHERE (POWER(ID1,2), ID2) IN " +
+ "((81, 2)," +
+ "(4, 9))");
+
+ rs = stmt.executeQuery("SELECT COUNT(*) FROM " + view);
+ assertTrue(rs.next());
+ assertEquals(0, rs.getInt(1));
+ }
+ }
+ }
+
+ @Test
+ public void testDeletionFromTenantViewAndViewIndex() throws Exception {
+ String view = generateUniqueName();
+ String index = generateUniqueName();
+
+ try (Connection conn = DriverManager.getConnection(TENANT_SPECIFIC_URL1)) {
+ conn.setAutoCommit(true);
+ try (Statement stmt = conn.createStatement()) {
+ stmt.execute("CREATE VIEW " + view + " (ID1 BIGINT NOT NULL, ID2 BIGINT NOT NULL, ID4 BIGINT " +
+ "CONSTRAINT PKVIEW PRIMARY KEY (ID1, ID2)) AS SELECT * FROM " + tableName + " WHERE KEY_PREFIX = 'ABC'");
+
+ conn.createStatement().execute("UPSERT INTO " + view + "(ID1, ID2, ID5, ID4) VALUES " +
+ "(9, 2, 7, 6)");
+ conn.createStatement().execute("UPSERT INTO " + view + "(ID1, ID2, ID5, ID4) VALUES " +
+ "(2, 9, 13, 9)");
+ conn.commit();
+
+ stmt.execute("CREATE INDEX " + index + " ON " + view + " (ID4, ID2) INCLUDE (ID1, ID5)");
+
+ // TESTING for optimized scan
+ PreparedStatement preparedStmt = conn.prepareStatement("SELECT * FROM " + view + " WHERE (ID4, ID2) IN " +
+ "((1, 1)," +
+ "(2, 2))");
+ QueryPlan queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt);
+ assertTrue(queryPlan.getExplainPlan().toString().contains("RANGE SCAN"));
+
+ preparedStmt = conn.prepareStatement("SELECT ID1,ID5 FROM " + view + " WHERE (ID1, ID2) IN " +
+ "((1, 1),(2, 2))");
+ queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt);
+ assertEquals(new Long(2), queryPlan.getEstimatedRowsToScan());
+ assertTrue(queryPlan.getExplainPlan().toString().contains(ExplainTable.POINT_LOOKUP_ON_STRING));
+
+
+ preparedStmt = conn.prepareStatement("SELECT * FROM " + view + " WHERE (ID2, ID1) IN ((1, 1),(2, 2))");
+ queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt);
+ assertEquals(new Long(2), queryPlan.getEstimatedRowsToScan());
+ assertTrue(queryPlan.getExplainPlan().toString().contains(ExplainTable.POINT_LOOKUP_ON_STRING));
+
+
+ ResultSet rs = stmt.executeQuery("SELECT ID1, ID2, ID5, ID4 FROM " + view + " WHERE (POWER(ID2, 2), ID1) IN " +
+ "((4.0, 9)," +
+ "(10, 12))");
+ assertTrue(rs.next());
+ assertEquals(9, rs.getInt(1));
+ assertEquals(2, rs.getInt(2));
+ assertEquals(7, rs.getInt(3));
+ assertEquals(6, rs.getInt(4));
+ assertTrue(!rs.next());
+
+ stmt.execute("DELETE FROM " + view + " WHERE (POWER(ID1,2), ID2) IN " +
+ "((81, 2)," +
+ "(4, 9))");
+
+ rs = stmt.executeQuery("SELECT COUNT(*) FROM " + view);
+ assertTrue(rs.next());
+ assertEquals(0, rs.getInt(1));
+ }
+ }
+ }
+
+ @Test
+ public void testBaseTableAndIndexTableHaveRightScan() throws Exception {
+ String index = generateUniqueName();
+ String fullTableName = generateUniqueName();
+
+ try (Connection conn = DriverManager.getConnection(getUrl())) {
+ conn.setAutoCommit(true);
+ try (Statement stmt = conn.createStatement()) {
+ stmt.execute("CREATE TABLE " + fullTableName + "(ID1 BIGINT NOT NULL, ID2 BIGINT NOT NULL, " +
+ "VAL1 BIGINT, VAL2 BIGINT CONSTRAINT PK PRIMARY KEY (ID1,ID2))");
+ stmt.execute("CREATE INDEX " + index + " ON " + fullTableName + " (ID2, ID1) INCLUDE (VAL2)");
+ }
+
+ PreparedStatement preparedStmt = conn.prepareStatement("SELECT VAL2 FROM " + fullTableName +
+ " WHERE (ID2, ID1) IN ((1, 1),(2, 2))");
+ QueryPlan queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt);
+ queryPlan.getTableRef().getTable().getType();
+ assertTrue(queryPlan.getExplainPlan().toString().contains(ExplainTable.POINT_LOOKUP_ON_STRING));
+ }
+ }
}
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/InListExpression.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/InListExpression.java
index fc276c8..900350d 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/expression/InListExpression.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/InListExpression.java
@@ -26,7 +26,7 @@ import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
-import java.util.stream.Collectors;
+import java.util.ArrayList;
import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
@@ -85,12 +85,24 @@ public class InListExpression extends BaseSingleExpression {
// In case of after removing nulls there is no remaining element in the IN list
return LiteralExpression.newConstant(false);
}
+
+ if (firstChild instanceof RowValueConstructorExpression) {
+ List<InListColumnKeyValuePair> inListColumnKeyValuePairList =
+ getSortedInListColumnKeyValuePair(childrenWithoutNulls);
+ if (inListColumnKeyValuePairList != null) {
+ childrenWithoutNulls = getSortedRowValueConstructorExpressionList(
+ inListColumnKeyValuePairList, firstChild.isStateless(),children.size() - 1);
+ firstChild = childrenWithoutNulls.get(0);
+ }
+ }
+
boolean nullInList = children.size() != childrenWithoutNulls.size();
if (childrenWithoutNulls.size() == 2 && !nullInList) {
return ComparisonExpression.create(isNegate ? CompareOp.NOT_EQUAL : CompareOp.EQUAL,
childrenWithoutNulls, ptr, rowKeyOrderOptimizable);
}
+
SQLException sqlE = null;
List<Expression> coercedKeyExpressions = Lists.newArrayListWithExpectedSize(childrenWithoutNulls.size());
coercedKeyExpressions.add(firstChild);
@@ -334,4 +346,108 @@ public class InListExpression extends BaseSingleExpression {
public InListExpression clone(List<Expression> l) {
return new InListExpression(l, this.rowKeyOrderOptimizable);
}
+
+ /**
+ * get list of InListColumnKeyValuePair with a PK ordered structure
+ * @param children children from rvc
+ * @return the list of InListColumnKeyValuePair
+ */
+ public static List<InListColumnKeyValuePair> getSortedInListColumnKeyValuePair(List<Expression> children) {
+ List<InListColumnKeyValuePair> inListColumnKeyValuePairList = new ArrayList<>();
+ int numberOfColumns = 0;
+
+ for (int i = 0; i < children.size(); i++) {
+ Expression child = children.get(i);
+ if (i == 0) {
+ numberOfColumns = child.getChildren().size();
+ for (int j = 0; j < child.getChildren().size(); j++) {
+ if (child.getChildren().get(j) instanceof RowKeyColumnExpression) {
+ RowKeyColumnExpression rowKeyColumnExpression =
+ (RowKeyColumnExpression)child.getChildren().get(j);
+ InListColumnKeyValuePair inListColumnKeyValuePair =
+ new InListColumnKeyValuePair(rowKeyColumnExpression);
+ inListColumnKeyValuePairList.add(inListColumnKeyValuePair);
+ } else {
+ // if one of the columns is not part of the pk, we ignore.
+ return null;
+ }
+ }
+ } else {
+ if (numberOfColumns != child.getChildren().size()) {
+ // if the number of the PK columns doesn't match number of values,
+ // it should not sort it in PK position.
+ return null;
+ }
+
+ for (int j = 0; j < child.getChildren().size(); j++) {
+ LiteralExpression literalExpression = (LiteralExpression) child.getChildren().get(j);
+ inListColumnKeyValuePairList.get(j).addToLiteralExpressionList(literalExpression);
+ }
+ }
+ }
+ Collections.sort(inListColumnKeyValuePairList);
+ return inListColumnKeyValuePairList;
+ }
+
+ /**
+ * get a PK ordered Expression RowValueConstructor
+ * @param inListColumnKeyValuePairList the object stores RowKeyColumnExpression and List of LiteralExpression
+ * @param isStateless
+ * @param numberOfRows number of literalExpressions
+ * @return the new RowValueConstructorExpression with PK ordered expressions
+ */
+ public static List<Expression> getSortedRowValueConstructorExpressionList(
+ List<InListColumnKeyValuePair> inListColumnKeyValuePairList, boolean isStateless, int numberOfRows) {
+ List<Expression> l = new ArrayList<>();
+ //reconstruct columns
+ List<Expression> keyExpressions = new ArrayList<>();
+ for (int i = 0; i < inListColumnKeyValuePairList.size(); i++) {
+ keyExpressions.add(inListColumnKeyValuePairList.get(i).getRowKeyColumnExpression());
+ }
+ l.add(new RowValueConstructorExpression(keyExpressions,isStateless));
+
+ //reposition to corresponding values
+ List<List<Expression>> valueExpressionsList = new ArrayList<>();
+
+ for (int j = 0; j < inListColumnKeyValuePairList.size(); j++) {
+ List<LiteralExpression> valueList = inListColumnKeyValuePairList.get(j).getLiteralExpressionList();
+ for (int i = 0; i < numberOfRows; i++) {
+ if (j == 0) {
+ valueExpressionsList.add(new ArrayList<Expression>());
+ }
+ valueExpressionsList.get(i).add(valueList.get(i));
+ }
+ }
+ for (List<Expression> valueExpressions: valueExpressionsList) {
+ l.add(new RowValueConstructorExpression(valueExpressions, isStateless));
+ }
+ return l;
+ }
+
+ public static class InListColumnKeyValuePair implements Comparable<InListColumnKeyValuePair> {
+ RowKeyColumnExpression rowKeyColumnExpression;
+ List<LiteralExpression> literalExpressionList;
+
+ public InListColumnKeyValuePair(RowKeyColumnExpression rowKeyColumnExpression) {
+ this.rowKeyColumnExpression = rowKeyColumnExpression;
+ this.literalExpressionList = new ArrayList<>();
+ }
+
+ public RowKeyColumnExpression getRowKeyColumnExpression() {
+ return this.rowKeyColumnExpression;
+ }
+
+ public void addToLiteralExpressionList(LiteralExpression literalExpression) {
+ this.literalExpressionList.add(literalExpression);
+ }
+
+ public List<LiteralExpression> getLiteralExpressionList() {
+ return this.literalExpressionList;
+ }
+
+ @Override
+ public int compareTo(InListColumnKeyValuePair o) {
+ return rowKeyColumnExpression.getPosition() - o.getRowKeyColumnExpression().getPosition();
+ }
+ }
}
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/iterate/ExplainTable.java b/phoenix-core/src/main/java/org/apache/phoenix/iterate/ExplainTable.java
index d8a0de9..1aaa3f9 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/iterate/ExplainTable.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/iterate/ExplainTable.java
@@ -54,6 +54,7 @@ import org.apache.phoenix.util.StringUtil;
public abstract class ExplainTable {
private static final List<KeyRange> EVERYTHING = Collections.singletonList(KeyRange.EVERYTHING_RANGE);
+ public static final String POINT_LOOKUP_ON_STRING = "POINT LOOKUP ON ";
protected final StatementContext context;
protected final TableRef tableRef;
protected final GroupBy groupBy;
@@ -81,7 +82,7 @@ public abstract class ExplainTable {
ScanRanges scanRanges = context.getScanRanges();
if (scanRanges.isPointLookup()) {
int keyCount = scanRanges.getPointLookupCount();
- buf.append("POINT LOOKUP ON " + keyCount + " KEY" + (keyCount > 1 ? "S " : " "));
+ buf.append(POINT_LOOKUP_ON_STRING + keyCount + " KEY" + (keyCount > 1 ? "S " : " "));
} else if (scanRanges.useSkipScanFilter()) {
buf.append("SKIP SCAN ");
int count = 1;
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/expression/InListExpressionTest.java b/phoenix-core/src/test/java/org/apache/phoenix/expression/InListExpressionTest.java
index 3d765ab..2cbc02b 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/expression/InListExpressionTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/expression/InListExpressionTest.java
@@ -19,13 +19,22 @@ package org.apache.phoenix.expression;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
import com.google.common.collect.Lists;
+
+import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
+
+
+import org.apache.phoenix.schema.types.PInteger;
+import org.junit.Test;
+import org.mockito.Mockito;
+
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.phoenix.hbase.index.util.ImmutableBytesPtr;
-import org.junit.Test;
+
public class InListExpressionTest {
@@ -62,4 +71,200 @@ public class InListExpressionTest {
+ hashCode, firstHashCode, hashCode);
}
}
+
+ @Test
+ public void testGetSortedInListColumnKeyValuePairWithNoPkOrder() {
+ testGetSortedInListColumnKeyValuePair(false);
+ }
+
+ @Test
+ public void testGetSortedInListColumnKeyValuePairWithPkOrder() {
+ testGetSortedInListColumnKeyValuePair(true);
+ }
+
+ private void testGetSortedInListColumnKeyValuePair(boolean isPkOrder) {
+ // mock literal
+ List<Expression> expressionList = new ArrayList<>();
+ LiteralExpression literalChild1 = Mockito.mock(LiteralExpression.class);
+ List<Expression> literalExpressions = new ArrayList<>();
+ when(literalChild1.getDataType()).thenReturn(PInteger.INSTANCE);
+ when(literalChild1.getBytes()).thenReturn(null);
+ when(literalChild1.getDeterminism()).thenReturn(Determinism.ALWAYS);
+ literalExpressions.add(literalChild1);
+ literalExpressions.add(literalChild1);
+
+ // mock row key column
+ List<Expression> expressionChildren = new ArrayList<>();
+ RowKeyColumnExpression rowKeyColumnExpressionMock1 = Mockito.mock(RowKeyColumnExpression.class);
+ RowKeyColumnExpression rowKeyColumnExpressionMock2 = Mockito.mock(RowKeyColumnExpression.class);
+
+ when(rowKeyColumnExpressionMock1.getPosition()).thenReturn(1);
+ when(rowKeyColumnExpressionMock1.getDeterminism()).thenReturn(Determinism.ALWAYS);
+ when(rowKeyColumnExpressionMock2.getPosition()).thenReturn(2);
+ when(rowKeyColumnExpressionMock2.getDeterminism()).thenReturn(Determinism.ALWAYS);
+ when(rowKeyColumnExpressionMock1.getChildren()).thenReturn(expressionChildren);
+ when(rowKeyColumnExpressionMock2.getChildren()).thenReturn(literalExpressions);
+
+ // mock row key column PK order position
+ if (isPkOrder) {
+ expressionChildren.add(rowKeyColumnExpressionMock1);
+ expressionChildren.add(rowKeyColumnExpressionMock2);
+
+ } else {
+ expressionChildren.add(rowKeyColumnExpressionMock2);
+ expressionChildren.add(rowKeyColumnExpressionMock1);
+
+ }
+
+ RowValueConstructorExpression rvc1 = new RowValueConstructorExpression(expressionChildren, true);
+ RowValueConstructorExpression rvc2 = new RowValueConstructorExpression(literalExpressions, true);
+ expressionList.add(rvc1);
+ expressionList.add(rvc2);
+
+ if (isPkOrder) {
+ assertEquals(1, ((RowKeyColumnExpression)expressionList.get(0).getChildren().get(0)).getPosition());
+ assertEquals(2, ((RowKeyColumnExpression)expressionList.get(0).getChildren().get(1)).getPosition());
+ } else {
+ assertEquals(2, ((RowKeyColumnExpression)expressionList.get(0).getChildren().get(0)).getPosition());
+ assertEquals(1, ((RowKeyColumnExpression)expressionList.get(0).getChildren().get(1)).getPosition());
+ }
+
+ List<InListExpression.InListColumnKeyValuePair> inListColumnKeyValuePairList =
+ InListExpression.getSortedInListColumnKeyValuePair(expressionList);
+
+ assertEquals(1, inListColumnKeyValuePairList.get(0).getRowKeyColumnExpression().getPosition());
+ assertEquals(2, inListColumnKeyValuePairList.get(1).getRowKeyColumnExpression().getPosition());
+ }
+
+ @Test
+ public void testGetSortedInListColumnKeyValuePairWithLessValueThanPkColumns() {
+ List<Expression> expressionList = new ArrayList<>();
+ LiteralExpression literalChild1 = Mockito.mock(LiteralExpression.class);
+ List<Expression> literalExpressions = new ArrayList<>();
+ when(literalChild1.getDataType()).thenReturn(PInteger.INSTANCE);
+ when(literalChild1.getBytes()).thenReturn(null);
+ when(literalChild1.getDeterminism()).thenReturn(Determinism.ALWAYS);
+ literalExpressions.add(literalChild1);
+ literalExpressions.add(literalChild1);
+
+ // mock row key column
+ List<Expression> expressionChildren = new ArrayList<>();
+ RowKeyColumnExpression rowKeyColumnExpressionMock1 = Mockito.mock(RowKeyColumnExpression.class);
+
+ when(rowKeyColumnExpressionMock1.getPosition()).thenReturn(1);
+ when(rowKeyColumnExpressionMock1.getDeterminism()).thenReturn(Determinism.ALWAYS);
+ when(rowKeyColumnExpressionMock1.getChildren()).thenReturn(expressionChildren);
+
+ expressionChildren.add(rowKeyColumnExpressionMock1);
+
+ RowValueConstructorExpression rvc1 = new RowValueConstructorExpression(expressionChildren, true);
+ RowValueConstructorExpression rvc2 = new RowValueConstructorExpression(literalExpressions, true);
+ expressionList.add(rvc1);
+ expressionList.add(rvc2);
+
+ List<InListExpression.InListColumnKeyValuePair> inListColumnKeyValuePairList =
+ InListExpression.getSortedInListColumnKeyValuePair(expressionList);
+
+ assertEquals(null, inListColumnKeyValuePairList);
+ }
+
+ @Test
+ public void testGetSortedInListColumnKeyValuePairWithMoreValueThanPkColumn() {
+ List<Expression> expressionList = new ArrayList<>();
+ LiteralExpression literalChild1 = Mockito.mock(LiteralExpression.class);
+ List<Expression> literalExpressions = new ArrayList<>();
+ when(literalChild1.getDataType()).thenReturn(PInteger.INSTANCE);
+ when(literalChild1.getBytes()).thenReturn(null);
+ when(literalChild1.getDeterminism()).thenReturn(Determinism.ALWAYS);
+ literalExpressions.add(literalChild1);
+
+ // mock row key column
+ List<Expression> expressionChildren = new ArrayList<>();
+ RowKeyColumnExpression rowKeyColumnExpressionMock1 = Mockito.mock(RowKeyColumnExpression.class);
+ when(rowKeyColumnExpressionMock1.getPosition()).thenReturn(1);
+ when(rowKeyColumnExpressionMock1.getDeterminism()).thenReturn(Determinism.ALWAYS);
+ when(rowKeyColumnExpressionMock1.getChildren()).thenReturn(expressionChildren);
+
+ expressionChildren.add(rowKeyColumnExpressionMock1);
+ expressionChildren.add(rowKeyColumnExpressionMock1);
+
+ RowValueConstructorExpression rvc1 = new RowValueConstructorExpression(expressionChildren, true);
+ RowValueConstructorExpression rvc2 = new RowValueConstructorExpression(literalExpressions, true);
+ expressionList.add(rvc1);
+ expressionList.add(rvc2);
+
+ List<InListExpression.InListColumnKeyValuePair> inListColumnKeyValuePairList =
+ InListExpression.getSortedInListColumnKeyValuePair(expressionList);
+
+ assertEquals(null, inListColumnKeyValuePairList);
+ }
+
+ @Test
+ public void testInListColumnKeyValuePairClass() {
+ RowKeyColumnExpression rowKeyColumnExpression = Mockito.mock(RowKeyColumnExpression.class);
+ LiteralExpression literalChild = Mockito.mock(LiteralExpression.class);
+
+ InListExpression.InListColumnKeyValuePair inListColumnKeyValuePair =
+ new InListExpression.InListColumnKeyValuePair(rowKeyColumnExpression);
+ inListColumnKeyValuePair.addToLiteralExpressionList(literalChild);
+
+ assertEquals(rowKeyColumnExpression, inListColumnKeyValuePair.getRowKeyColumnExpression());
+ assertEquals(literalChild, inListColumnKeyValuePair.getLiteralExpressionList().get(0));
+ }
+
+ @Test
+ public void testGetSortedRowValueConstructorExpressionList() {
+ byte[] bytesValueOne = ByteBuffer.allocate(4).putInt(1).array();
+ byte[] bytesValueTwo = ByteBuffer.allocate(4).putInt(1).array();
+ // mock literal
+ List<Expression> literalExpressions = new ArrayList<>();
+ LiteralExpression literalChild1 = Mockito.mock(LiteralExpression.class);
+ when(literalChild1.getDataType()).thenReturn(PInteger.INSTANCE);
+ when(literalChild1.getBytes()).thenReturn(bytesValueOne);
+ when(literalChild1.getDeterminism()).thenReturn(Determinism.ALWAYS);
+ literalExpressions.add(literalChild1);
+
+ LiteralExpression literalChild2 = Mockito.mock(LiteralExpression.class);
+ when(literalChild2.getDataType()).thenReturn(PInteger.INSTANCE);
+ when(literalChild2.getBytes()).thenReturn(bytesValueTwo);
+ when(literalChild2.getDeterminism()).thenReturn(Determinism.ALWAYS);
+ literalExpressions.add(literalChild2);
+
+ List<Expression> expressionChildren = new ArrayList<>();
+ RowKeyColumnExpression rowKeyColumnExpressionMock1 = Mockito.mock(RowKeyColumnExpression.class);
+ RowKeyColumnExpression rowKeyColumnExpressionMock2 = Mockito.mock(RowKeyColumnExpression.class);
+ expressionChildren.add(rowKeyColumnExpressionMock1);
+ expressionChildren.add(rowKeyColumnExpressionMock2);
+
+ when(rowKeyColumnExpressionMock1.getPosition()).thenReturn(1);
+ when(rowKeyColumnExpressionMock1.getDeterminism()).thenReturn(Determinism.ALWAYS);
+ when(rowKeyColumnExpressionMock2.getPosition()).thenReturn(2);
+ when(rowKeyColumnExpressionMock2.getDeterminism()).thenReturn(Determinism.ALWAYS);
+ when(rowKeyColumnExpressionMock1.getChildren()).thenReturn(expressionChildren);
+ when(rowKeyColumnExpressionMock2.getChildren()).thenReturn(literalExpressions);
+
+ //construct sorted InListColumnKeyValuePair list
+ List<InListExpression.InListColumnKeyValuePair> children = new ArrayList<>();
+ InListExpression.InListColumnKeyValuePair rvc1 =
+ new InListExpression.InListColumnKeyValuePair(rowKeyColumnExpressionMock1);
+ rvc1.addToLiteralExpressionList(literalChild1);
+ children.add(rvc1);
+ InListExpression.InListColumnKeyValuePair rvc2 =
+ new InListExpression.InListColumnKeyValuePair(rowKeyColumnExpressionMock2);
+ rvc2.addToLiteralExpressionList(literalChild2);
+ children.add(rvc2);
+
+ List<Expression> result = InListExpression.getSortedRowValueConstructorExpressionList(
+ children,true, 1);
+
+ assertTrue(result.get(0).getChildren().get(0) instanceof RowKeyColumnExpression);
+ assertTrue(result.get(0).getChildren().get(1) instanceof RowKeyColumnExpression);
+ assertEquals(1, ((RowKeyColumnExpression)result.get(0).getChildren().get(0)).getPosition());
+ assertEquals(2, ((RowKeyColumnExpression)result.get(0).getChildren().get(1)).getPosition());
+
+ assertTrue(result.get(1).getChildren().get(0) instanceof LiteralExpression);
+ assertTrue(result.get(1).getChildren().get(1) instanceof LiteralExpression);
+ assertEquals(bytesValueOne, ((LiteralExpression)result.get(1).getChildren().get(0)).getBytes());
+ assertEquals(bytesValueTwo, ((LiteralExpression)result.get(1).getChildren().get(1)).getBytes());
+ }
}