This is an automated email from the ASF dual-hosted git repository.
vjasani 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 986b79b670 PHOENIX-7035 Index on data table with only pk columns
result in invalid state (#1675)
986b79b670 is described below
commit 986b79b67090a19138522adf5c9c5317e6130b41
Author: Viraj Jasani <[email protected]>
AuthorDate: Thu Oct 5 21:13:41 2023 -0800
PHOENIX-7035 Index on data table with only pk columns result in invalid
state (#1675)
---
.../index/UncoveredGlobalIndexRegionScannerIT.java | 208 +++++++++++++++++++++
.../org/apache/phoenix/index/IndexMaintainer.java | 4 +
.../org/apache/phoenix/schema/DelegateTable.java | 5 +
.../java/org/apache/phoenix/schema/PTable.java | 7 +
.../java/org/apache/phoenix/schema/PTableImpl.java | 5 +
5 files changed, 229 insertions(+)
diff --git
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/UncoveredGlobalIndexRegionScannerIT.java
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/UncoveredGlobalIndexRegionScannerIT.java
index a87daee045..aef6009ae5 100644
---
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/UncoveredGlobalIndexRegionScannerIT.java
+++
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/UncoveredGlobalIndexRegionScannerIT.java
@@ -134,6 +134,27 @@ public class UncoveredGlobalIndexRegionScannerIT extends
BaseTest {
}
}
+ @Test
+ public void testDDLWithPhoenixRowTimestamp() throws Exception {
+ try (Connection conn = DriverManager.getConnection(getUrl())) {
+ String dataTableName = generateUniqueName();
+ conn.createStatement().execute("create table " + dataTableName +
+ " (id varchar(10) not null primary key)" + (salted ? "
SALT_BUCKETS=4" : ""));
+ if (uncovered) {
+ conn.createStatement().execute("CREATE UNCOVERED INDEX IDX_" +
dataTableName
+ + " on " + dataTableName + "
(PHOENIX_ROW_TIMESTAMP())");
+ conn.createStatement()
+ .execute("CREATE UNCOVERED LOCAL INDEX IDX_LOCAL_" +
dataTableName
+ + " on " + dataTableName + "
(PHOENIX_ROW_TIMESTAMP())");
+ } else {
+ conn.createStatement().execute("CREATE INDEX IDX_" +
dataTableName
+ + " on " + dataTableName + "
(PHOENIX_ROW_TIMESTAMP())");
+ conn.createStatement().execute("CREATE LOCAL INDEX IDX_LOCAL_"
+ dataTableName
+ + " on " + dataTableName + "
(PHOENIX_ROW_TIMESTAMP())");
+ }
+ }
+ }
+
@Test
public void testUncoveredQueryWithPhoenixRowTimestamp() throws Exception {
try (Connection conn = DriverManager.getConnection(getUrl())) {
@@ -304,6 +325,193 @@ public class UncoveredGlobalIndexRegionScannerIT extends
BaseTest {
}
}
+ @Test
+ public void testUncoveredQueryWithPhoenixRowTimestampAndAllPkCols() throws
Exception {
+ try (Connection conn = DriverManager.getConnection(getUrl())) {
+ String dataTableName = generateUniqueName();
+ String indexTableName = generateUniqueName();
+ Timestamp initial = new
Timestamp(EnvironmentEdgeManager.currentTimeMillis() - 1);
+ conn.createStatement().execute("create table " + dataTableName +
+ " (id varchar(10), val1 varchar(10), val2 varchar(10), " +
+ " val3 varchar(10) constraint pk primary key(id, val1,
val2, val3))" +
+ (salted ? " SALT_BUCKETS=4" : ""));
+ conn.createStatement().execute("upsert into " + dataTableName
+ + " values ('a', 'ab', 'abc', 'abcd')");
+ conn.commit();
+ Timestamp before = new
Timestamp(EnvironmentEdgeManager.currentTimeMillis());
+ // Sleep 1ms to get a different row timestamps
+ Thread.sleep(1);
+ conn.createStatement()
+ .execute("upsert into " + dataTableName + " values ('b',
'bc', 'bcd', 'bcde')");
+ conn.commit();
+ Timestamp after = new
Timestamp(EnvironmentEdgeManager.currentTimeMillis() + 1);
+ conn.createStatement().execute(
+ "CREATE " + (uncovered ? "UNCOVERED " : " ") + "INDEX " +
indexTableName
+ + " on " + dataTableName + " (val1,
PHOENIX_ROW_TIMESTAMP()) ");
+
+ String timeZoneID = Calendar.getInstance().getTimeZone().getID();
+ // Write a query to get the val2 = 'bc' with a time range query
+ String query = "SELECT" +
+ (uncovered ? " " : "/*+ INDEX(" + dataTableName + " " +
indexTableName + ")*/ ")
+ + "val1, val2, PHOENIX_ROW_TIMESTAMP() from " +
dataTableName
+ + " WHERE val1 = 'bc' AND " + "PHOENIX_ROW_TIMESTAMP() >
TO_DATE('"
+ + before.toString() + "','yyyy-MM-dd HH:mm:ss.SSS', '"
+ + timeZoneID + "') AND " + "PHOENIX_ROW_TIMESTAMP() <
TO_DATE('" + after
+ + "','yyyy-MM-dd HH:mm:ss.SSS', '" + timeZoneID + "')";
+ // Verify that we will read from the index table
+ assertExplainPlan(conn, query, dataTableName, indexTableName);
+ ResultSet rs = conn.createStatement().executeQuery(query);
+ assertTrue(rs.next());
+ assertEquals("bc", rs.getString(1));
+ assertEquals("bcd", rs.getString(2));
+ assertTrue(rs.getTimestamp(3).after(before));
+ assertTrue(rs.getTimestamp(3).before(after));
+ assertFalse(rs.next());
+ // Count the number of index rows
+ rs = conn.createStatement().executeQuery("SELECT COUNT(*) from " +
indexTableName);
+ assertTrue(rs.next());
+ assertEquals(2, rs.getInt(1));
+ // Add one more row with val2 ='bc' and check this does not change
the result of the previous
+ // query
+ // Sleep 1ms to get a different row timestamps
+ Thread.sleep(1);
+ conn.createStatement()
+ .execute("upsert into " + dataTableName + " values ('c',
'bc', 'ccc', 'cccc')");
+ conn.commit();
+ assertExplainPlan(conn, query, dataTableName, indexTableName);
+ rs = conn.createStatement().executeQuery(query);
+ assertTrue(rs.next());
+ assertEquals("bc", rs.getString(1));
+ assertEquals("bcd", rs.getString(2));
+ assertTrue(rs.getTimestamp(3).after(before));
+ assertTrue(rs.getTimestamp(3).before(after));
+ assertFalse(rs.next());
+ // Write a time range query to get the last row with val2 ='bc'
+ query = "SELECT" +
+ (uncovered ? " " : "/*+ INDEX(" + dataTableName + " " +
indexTableName + ")*/ ")
+ + "val1, val2, PHOENIX_ROW_TIMESTAMP() from " +
dataTableName +
+ " WHERE val1 = 'bc' AND " + "PHOENIX_ROW_TIMESTAMP() >
TO_DATE('" + after
+ + "','yyyy-MM-dd HH:mm:ss.SSS', '" + timeZoneID + "')";
+ // Verify that we will read from the index table
+ assertExplainPlan(conn, query, dataTableName, indexTableName);
+ rs = conn.createStatement().executeQuery(query);
+ assertTrue(rs.next());
+ assertEquals("bc", rs.getString(1));
+ assertEquals("ccc", rs.getString(2));
+ assertTrue(rs.getTimestamp(3).after(after));
+ assertFalse(rs.next());
+ // Verify that we can execute the same query without using the
index
+ String noIndexQuery =
+ "SELECT /*+ NO_INDEX */ val1, val2,
PHOENIX_ROW_TIMESTAMP() from " +
+ dataTableName + " WHERE val1 = 'bc' AND " +
+ "PHOENIX_ROW_TIMESTAMP() > TO_DATE('" + after +
+ "','yyyy-MM-dd HH:mm:ss.SSS', '" + timeZoneID +
"')";
+ // Verify that we will read from the data table
+ rs = conn.createStatement().executeQuery("EXPLAIN " +
noIndexQuery);
+ String explainPlan = QueryUtil.getExplainPlan(rs);
+ assertTrue(explainPlan.contains(salted ? "RANGE" :
+ "FULL" + " SCAN OVER " + dataTableName));
+ rs = conn.createStatement().executeQuery(noIndexQuery);
+ assertTrue(rs.next());
+ assertEquals("bc", rs.getString(1));
+ assertEquals("ccc", rs.getString(2));
+ assertTrue(rs.getTimestamp(3).after(after));
+ after = rs.getTimestamp(3);
+ assertFalse(rs.next());
+ // Sleep 1ms to get a different row timestamps
+ Thread.sleep(1);
+ conn.createStatement()
+ .execute("upsert into " + dataTableName + " values ('d',
'de', 'def', 'defg')");
+ conn.commit();
+
+ query = "SELECT" +
+ (uncovered ? " " : "/*+ INDEX(" + dataTableName + " " +
indexTableName + ")*/ ")
+ + " val1, val2, PHOENIX_ROW_TIMESTAMP() from " +
dataTableName
+ + " WHERE val1 = 'de'";
+ // Verify that we will read from the index table
+ assertExplainPlan(conn, query, dataTableName, indexTableName);
+ rs = conn.createStatement().executeQuery(query);
+ assertTrue(rs.next());
+ assertEquals("de", rs.getString(1));
+ assertEquals("def", rs.getString(2));
+ assertTrue(rs.getTimestamp(3).after(after));
+ assertFalse(rs.next());
+ conn.createStatement().execute("DROP INDEX " + indexTableName + "
on " +
+ dataTableName);
+ conn.commit();
+ // Add a new index where the index row key starts with
PHOENIX_ROW_TIMESTAMP()
+ indexTableName = generateUniqueName();
+ conn.createStatement().execute(
+ "CREATE " + (uncovered ? "UNCOVERED " : " ") + "INDEX " +
indexTableName
+ + " on " + dataTableName + "
(PHOENIX_ROW_TIMESTAMP()) ");
+ conn.commit();
+ // Add one more row
+ // Sleep 1ms to get a different row timestamps
+ Thread.sleep(1);
+ conn.createStatement()
+ .execute("upsert into " + dataTableName + " values ('e',
'ae', 'efg', 'efgh')");
+ conn.commit();
+ // Write a query to get all the rows in the order of their
timestamps
+ query = "SELECT" +
+ (uncovered ? " " : "/*+ INDEX(" + dataTableName + " " +
indexTableName + ")*/ ")
+ + " id, val1, val2, PHOENIX_ROW_TIMESTAMP() from " +
dataTableName + " WHERE "
+ + "PHOENIX_ROW_TIMESTAMP() > TO_DATE('" + initial
+ + "','yyyy-MM-dd HH:mm:ss.SSS', '" + timeZoneID + "')";
+ // Verify that we will read from the index table
+ assertExplainPlan(conn, query, dataTableName, indexTableName);
+ rs = conn.createStatement().executeQuery(query);
+ assertTrue(rs.next());
+ assertEquals("a", rs.getString(1));
+ assertEquals("ab", rs.getString(2));
+ assertEquals("abc", rs.getString(3));
+ assertTrue(rs.next());
+ assertEquals("b", rs.getString(1));
+ assertEquals("bc", rs.getString(2));
+ assertEquals("bcd", rs.getString(3));
+ assertTrue(rs.next());
+ assertEquals("c", rs.getString(1));
+ assertEquals("bc", rs.getString(2));
+ assertEquals("ccc", rs.getString(3));
+ assertTrue(rs.next());
+ assertEquals("d", rs.getString(1));
+ assertEquals("de", rs.getString(2));
+ assertEquals("def", rs.getString(3));
+ assertTrue(rs.next());
+ assertEquals("e", rs.getString(1));
+ assertEquals("ae", rs.getString(2));
+ assertEquals("efg", rs.getString(3));
+ assertFalse(rs.next());
+
+ // Sleep 1ms to get a different row timestamps
+ Thread.sleep(1);
+ conn.createStatement()
+ .execute("upsert into " + dataTableName + " values ('a',
'ab', 'abc', 'abcd')");
+ conn.commit();
+ rs = conn.createStatement().executeQuery(query);
+ assertTrue(rs.next());
+ assertEquals("b", rs.getString(1));
+ assertEquals("bc", rs.getString(2));
+ assertEquals("bcd", rs.getString(3));
+ assertTrue(rs.next());
+ assertEquals("c", rs.getString(1));
+ assertEquals("bc", rs.getString(2));
+ assertEquals("ccc", rs.getString(3));
+ assertTrue(rs.next());
+ assertEquals("d", rs.getString(1));
+ assertEquals("de", rs.getString(2));
+ assertEquals("def", rs.getString(3));
+ assertTrue(rs.next());
+ assertEquals("e", rs.getString(1));
+ assertEquals("ae", rs.getString(2));
+ assertEquals("efg", rs.getString(3));
+ assertTrue(rs.next());
+ assertEquals("a", rs.getString(1));
+ assertEquals("ab", rs.getString(2));
+ assertEquals("abc", rs.getString(3));
+ assertFalse(rs.next());
+ }
+ }
+
private void assertIndexTableNotSelected(Connection conn, String
dataTableName, String indexTableName, String sql)
throws Exception {
try {
diff --git
a/phoenix-core/src/main/java/org/apache/phoenix/index/IndexMaintainer.java
b/phoenix-core/src/main/java/org/apache/phoenix/index/IndexMaintainer.java
index 7893c9d693..0ad9e76aee 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/index/IndexMaintainer.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/index/IndexMaintainer.java
@@ -630,10 +630,14 @@ public class IndexMaintainer implements Writable,
Iterable<ColumnReference> {
}
} catch (ColumnNotFoundException |
ColumnFamilyNotFoundException
| AmbiguousColumnException e) {
+ if (dataTable.hasOnlyPkColumns()) {
+ return null;
+ }
throw new RuntimeException(e);
}
return null;
}
+
};
expression.accept(kvVisitor);
}
diff --git
a/phoenix-core/src/main/java/org/apache/phoenix/schema/DelegateTable.java
b/phoenix-core/src/main/java/org/apache/phoenix/schema/DelegateTable.java
index b79691b335..10ab0c7245 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/DelegateTable.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/DelegateTable.java
@@ -99,6 +99,11 @@ public class DelegateTable implements PTable {
return delegate.getColumnFamilies();
}
+ @Override
+ public boolean hasOnlyPkColumns() {
+ return delegate.hasOnlyPkColumns();
+ }
+
@Override
public PColumnFamily getColumnFamily(byte[] family) throws
ColumnFamilyNotFoundException {
return delegate.getColumnFamily(family);
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/PTable.java
b/phoenix-core/src/main/java/org/apache/phoenix/schema/PTable.java
index 1798d444b6..09bc2023a2 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/PTable.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/PTable.java
@@ -706,6 +706,13 @@ public interface PTable extends PMetaDataEntity {
*/
List<PColumnFamily> getColumnFamilies();
+ /**
+ * Return true if the table only has pk columns and no non-pk columns.
+ *
+ * @return true if the table only has pk columns and no non-pk columns.
+ */
+ boolean hasOnlyPkColumns();
+
/**
* Get the column family with the given name
* @param family the column family name
diff --git
a/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableImpl.java
b/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableImpl.java
index d332a95269..ba888d7358 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableImpl.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableImpl.java
@@ -1138,6 +1138,11 @@ public class PTableImpl implements PTable {
return families;
}
+ @Override
+ public boolean hasOnlyPkColumns() {
+ return allColumns.stream().allMatch(SchemaUtil::isPKColumn);
+ }
+
@Override
public int newKey(ImmutableBytesWritable key, byte[][] values) {
List<PColumn> columns = getPKColumns();