Xavier Fernandis created PHOENIX-8010:
-----------------------------------------

             Summary: NPE in 
NonAggregateRegionScannerFactory.getOffsetScanner() for point-lookup queries 
with OFFSET
                 Key: PHOENIX-8010
                 URL: https://issues.apache.org/jira/browse/PHOENIX-8010
             Project: Phoenix
          Issue Type: Bug
    Affects Versions: 5.3.2, 5.2.2
         Environment: h2. Environment
 * Reproduced on Phoenix branch-5.3.0 with HBase 2.6.4.
 * Code path is byte-identical across all 5.x branches and master; HBase 
version is irrelevant.
            Reporter: Xavier Fernandis
            Assignee: Xavier Fernandis


h2. Root cause

The crash is in {{getOffsetScanner(...)}} in:

{{phoenix-core-server/src/main/java/org/apache/phoenix/iterate/NonAggregateRegionScannerFactory.java}}

{{}}
{code:java}
byte[] prevScanStartRowKey =
  scan.getAttribute(BaseScannerRegionObserverConstants.SCAN_ACTUAL_START_ROW);
// If the region has moved after server has returned dummy or valid row to 
client,
// prevScanStartRowKey would be different from actual scan start rowkey.
// ...
if (
  Bytes.compareTo(prevScanStartRowKey, initStartRowKey) != 0 && Bytes
    .compareTo(ByteUtil.concat(prevScanStartRowKey, ByteUtil.ZERO_BYTE), 
initStartRowKey) != 0
) {
  iterator.setRowCountToOffset();
}{code}
{{ }}

{{prevScanStartRowKey}} is the value of the {{SCAN_ACTUAL_START_ROW}} . On the 
first RPC of a point-lookup query this attribute was never set on the client, 
so it is {{{}null{}}}.
 * {{org.apache.hadoop.hbase.util.Bytes.compareTo(byte[], byte[])}} is 
null-tolerant, so the first comparison does *not* throw.
 * {{org.apache.phoenix.util.ByteUtil.concat(byte[] first, byte[]... rest)}} is 
*not* null-tolerant. It dereferences {{first.length}} immediately, so 
{{concat(null, ByteUtil.ZERO_BYTE)}} throws.

Offending line in:

{{phoenix-core-client/src/main/java/org/apache/phoenix/util/ByteUtil.java}}

 
{code:java}
 
public static byte[] concat(byte[] first, byte[]... rest) {
 int totalLength = first.length; // <-- NPE when first == null ... 
}
{code}
 

Every range / salted / local-index / guide-post-split scan is produced through 
{{{}intersectScan(...){}}}, so those paths always carry the attribute. But the 
*point-lookup fast path* in {{{}BaseResultIterators.getParallelScans(byte[], 
byte[]){}}}:

{{}}
{code:java}
if (!isLocalIndex && scanRanges.isPointLookup() && 
!scanRanges.useSkipScanFilter()) {
    // builds the scan directly from context.getScan(); never calls 
intersectScan(...)
    // ... and therefore never sets SCAN_ACTUAL_START_ROW
}
{code}
{{ }}

{{}}



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to