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();

Reply via email to