PHOENIX-4658 IllegalStateException: requestSeek cannot be called on ReversedKeyValueH...
authorJames Taylor <jtaylor@salesforce.com>
Wed, 11 Apr 2018 20:31:38 +0000 (21:31 +0100)
committerPedro Boado <pboado@apache.org>
Fri, 13 Apr 2018 22:27:22 +0000 (23:27 +0100)
phoenix-core/src/it/java/org/apache/phoenix/end2end/MultiCfQueryExecIT.java
phoenix-core/src/main/java/org/apache/phoenix/compile/OrderByCompiler.java
phoenix-core/src/main/java/org/apache/phoenix/compile/QueryCompiler.java
phoenix-core/src/main/java/org/apache/phoenix/parse/HintNode.java
phoenix-core/src/main/java/org/apache/phoenix/util/ScanUtil.java

index d94df6c..01da2d8 100644 (file)
@@ -31,11 +31,13 @@ import java.sql.DriverManager;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.sql.Statement;
 import java.util.List;
 import java.util.Properties;
 
 import org.apache.phoenix.query.KeyRange;
 import org.apache.phoenix.util.PropertiesUtil;
+import org.apache.phoenix.util.QueryUtil;
 import org.apache.phoenix.util.SchemaUtil;
 import org.apache.phoenix.util.TestUtil;
 import org.junit.Before;
@@ -406,4 +408,49 @@ public class MultiCfQueryExecIT extends ParallelStatsEnabledIT {
             assertFalse(rs.next());
         }
     }
+
+    @Test
+    public void testBug4658() throws Exception {
+        try (Connection conn = DriverManager.getConnection(getUrl());
+          Statement stmt = conn.createStatement()) {
+            String tableName = generateUniqueName();
+
+            stmt.execute("CREATE TABLE " + tableName + " ("
+                + "COL1 VARCHAR NOT NULL,"
+                + "COL2 VARCHAR NOT NULL,"
+                + "COL3 VARCHAR,"
+                + "FAM.COL4 VARCHAR,"
+                + "CONSTRAINT TRADE_EVENT_PK PRIMARY KEY (COL1, COL2))");
+            stmt.execute("UPSERT INTO " + tableName + " (COL1, COL2) values ('111', 'AAA')");
+            stmt.execute("UPSERT INTO " + tableName + " (COL1, COL2) values ('222', 'AAA')");
+            conn.commit();
+
+            try (ResultSet rs = stmt.executeQuery(
+              "SELECT * FROM " + tableName + " WHERE COL2 = 'AAA' ORDER BY COL1 DESC")) {
+                assertTrue(rs.next());
+                assertEquals(rs.getString("COL1"), "222");
+                assertEquals(rs.getString("COL2"), "AAA");
+                assertTrue(rs.next());
+                assertEquals(rs.getString("COL1"), "111");
+                assertEquals(rs.getString("COL2"), "AAA");
+                assertFalse(rs.next());
+            }
+
+            // Tests for FORWARD_SCAN hint
+            String query = "SELECT /*+ FORWARD_SCAN */ * FROM " + tableName + " WHERE COL2 = 'AAA' ORDER BY COL1 DESC";
+            try (ResultSet rs = stmt.executeQuery("EXPLAIN " + query)) {
+                String explainPlan = QueryUtil.getExplainPlan(rs);
+                assertFalse(explainPlan.contains("REVERSE"));
+            }
+            try (ResultSet rs = stmt.executeQuery(query)) {
+                assertTrue(rs.next());
+                assertEquals(rs.getString("COL1"), "222");
+                assertEquals(rs.getString("COL2"), "AAA");
+                assertTrue(rs.next());
+                assertEquals(rs.getString("COL1"), "111");
+                assertEquals(rs.getString("COL2"), "AAA");
+                assertFalse(rs.next());
+            }
+        }
+    }
 }
index 1097f70..b83c7a8 100644 (file)
@@ -30,6 +30,7 @@ import org.apache.phoenix.exception.SQLExceptionInfo;
 import org.apache.phoenix.execute.TupleProjector;
 import org.apache.phoenix.expression.Expression;
 import org.apache.phoenix.expression.OrderByExpression;
+import org.apache.phoenix.parse.HintNode.Hint;
 import org.apache.phoenix.parse.LiteralParseNode;
 import org.apache.phoenix.parse.OrderByNode;
 import org.apache.phoenix.parse.ParseNode;
@@ -154,12 +155,16 @@ public class OrderByCompiler {
         // If we're ordering by the order returned by the scan, we don't need an order by
         if (isInRowKeyOrder && tracker.isOrderPreserving()) {
             if (tracker.isReverse()) {
-                // Don't use reverse scan if we're using a skip scan, as our skip scan doesn't support this yet.
+                // Don't use reverse scan if:
+                // 1) we're using a skip scan, as our skip scan doesn't support this yet.
+                // 2) we have the FORWARD_SCAN hint set to choose to keep loading of column
+                //    families on demand versus doing a reverse scan
                 // REV_ROW_KEY_ORDER_BY scan would not take effect for a projected table, so don't return it for such table types.
                 if (context.getConnection().getQueryServices().getProps().getBoolean(QueryServices.USE_REVERSE_SCAN_ATTRIB, QueryServicesOptions.DEFAULT_USE_REVERSE_SCAN)
                         && !context.getScanRanges().useSkipScanFilter()
                         && context.getCurrentTable().getTable().getType() != PTableType.PROJECTED
-                        && context.getCurrentTable().getTable().getType() != PTableType.SUBQUERY) {
+                        && context.getCurrentTable().getTable().getType() != PTableType.SUBQUERY
+                        && !statement.getHint().hasHint(Hint.FORWARD_SCAN)) {
                     return OrderBy.REV_ROW_KEY_ORDER_BY;
                 }
             } else {
@@ -172,4 +177,4 @@ public class OrderByCompiler {
 
     private OrderByCompiler() {
     }
-}
\ No newline at end of file
+}
index 9568ad8..3e5f5ee 100644 (file)
@@ -66,7 +66,6 @@ import org.apache.phoenix.parse.SelectStatement;
 import org.apache.phoenix.parse.SubqueryParseNode;
 import org.apache.phoenix.parse.TableNode;
 import org.apache.phoenix.query.ConnectionQueryServices;
-import org.apache.phoenix.query.QueryConstants;
 import org.apache.phoenix.query.QueryServices;
 import org.apache.phoenix.query.QueryServicesOptions;
 import org.apache.phoenix.schema.AmbiguousColumnException;
@@ -91,13 +90,6 @@ import com.google.common.collect.Sets;
  */
 public class QueryCompiler {
     private static final ParseNodeFactory NODE_FACTORY = new ParseNodeFactory();
-    /*
-     * Not using Scan.setLoadColumnFamiliesOnDemand(true) because we don't
-     * want to introduce a dependency on 0.94.5 (where this feature was
-     * introduced). This will do the same thing. Once we do have a
-     * dependency on 0.94.5 or above, switch this around.
-     */
-    private static final String LOAD_COLUMN_FAMILIES_ON_DEMAND_ATTR = "_ondemand_";
     private final PhoenixStatement statement;
     private final Scan scan;
     private final Scan originalScan;
@@ -128,9 +120,7 @@ public class QueryCompiler {
         this.noChildParentJoinOptimization = select.getHint().hasHint(Hint.NO_CHILD_PARENT_JOIN_OPTIMIZATION);
         ConnectionQueryServices services = statement.getConnection().getQueryServices();
         this.costBased = services.getProps().getBoolean(QueryServices.COST_BASED_OPTIMIZER_ENABLED, QueryServicesOptions.DEFAULT_COST_BASED_OPTIMIZER_ENABLED);
-        if (services.getLowestClusterHBaseVersion() >= PhoenixDatabaseMetaData.ESSENTIAL_FAMILY_VERSION_THRESHOLD) {
-            this.scan.setAttribute(LOAD_COLUMN_FAMILIES_ON_DEMAND_ATTR, QueryConstants.TRUE);
-        }
+        scan.setLoadColumnFamiliesOnDemand(true);
         if (select.getHint().hasHint(Hint.NO_CACHE)) {
             scan.setCacheBlocks(false);
         }
index 6d8451b..39e9b05 100644 (file)
@@ -104,6 +104,10 @@ public class HintNode {
       * Enforces a serial scan.
       */
      SERIAL,
+        /**
+         * Enforces a forward scan.
+         */
+        FORWARD_SCAN,
     };
 
     private final Map<Hint,String> hints;
index 9c710c1..dd885fd 100644 (file)
@@ -612,10 +612,12 @@ public class ScanUtil {
     
     public static void setReversed(Scan scan) {
         scan.setAttribute(BaseScannerRegionObserver.REVERSE_SCAN, PDataType.TRUE_BYTES);
+        scan.setLoadColumnFamiliesOnDemand(false);
     }
 
     public static void unsetReversed(Scan scan) {
         scan.setAttribute(BaseScannerRegionObserver.REVERSE_SCAN, PDataType.FALSE_BYTES);
+        scan.setLoadColumnFamiliesOnDemand(true);
     }
 
     private static byte[] getReversedRow(byte[] startRow) {