This is an automated email from the ASF dual-hosted git repository.

vjasani pushed a commit to branch 5.1
in repository https://gitbox.apache.org/repos/asf/phoenix.git


The following commit(s) were added to refs/heads/5.1 by this push:
     new 9081be2da2 PHOENIX-7035 Index on data table with only pk columns 
result in invalid state (#1675)
9081be2da2 is described below

commit 9081be2da2d14d83c96e30ee702b0c113efb1565
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 | 196 +++++++++++++++++++++
 .../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, 217 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 eb59c789b4..75169636e1 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
@@ -83,6 +83,21 @@ public class UncoveredGlobalIndexRegionScannerIT extends 
BaseTest {
         conn.close();
     }
 
+    @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)");
+            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 testUncoveredIndexWithPhoenixRowTimestamp() throws Exception {
         try (Connection conn = DriverManager.getConnection(getUrl())) {
@@ -216,6 +231,187 @@ 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))");
+            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 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 /*+ 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 /*+ 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("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 /*+ 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 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 /*+ 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 735bf8d035..f6455ee8a0 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
@@ -598,10 +598,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 3815e81eb9..17baf4d727 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
@@ -93,6 +93,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 136291d4f4..1b8cc4f8f3 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
@@ -616,6 +616,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 17a7cf56fa..bcceab23c1 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
@@ -1035,6 +1035,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