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

apurtell pushed a commit to branch PHOENIX-7876-feature
in repository https://gitbox.apache.org/repos/asf/phoenix.git


The following commit(s) were added to refs/heads/PHOENIX-7876-feature by this 
push:
     new d21ddf8f0e PHOENIX-7923 Simplify EXPLAIN value classes (#2530)
d21ddf8f0e is described below

commit d21ddf8f0ef14f1ec5381add7dba56d93e313698
Author: Andrew Purtell <[email protected]>
AuthorDate: Sat Jun 13 23:18:25 2026 -0700

    PHOENIX-7923 Simplify EXPLAIN value classes (#2530)
    
    Co-authored-by: Claude Opus 4.8[1m] <[email protected]>
---
 phoenix-core-client/src/main/antlr3/PhoenixSQL.g   |  54 +++-
 .../phoenix/compile/ExplainPlanAttributes.java     | 271 +++++++--------------
 .../org/apache/phoenix/iterate/ExplainTable.java   |  43 +---
 .../org/apache/phoenix/parse/ExplainOptions.java   |  45 ----
 4 files changed, 129 insertions(+), 284 deletions(-)

diff --git a/phoenix-core-client/src/main/antlr3/PhoenixSQL.g 
b/phoenix-core-client/src/main/antlr3/PhoenixSQL.g
index c84d8f3b54..768eb60249 100644
--- a/phoenix-core-client/src/main/antlr3/PhoenixSQL.g
+++ b/phoenix-core-client/src/main/antlr3/PhoenixSQL.g
@@ -203,6 +203,7 @@ import java.lang.Boolean;
 import java.math.BigDecimal;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.EnumSet;
 import java.util.HashSet;
 import java.util.Set;
 import java.util.Stack;
@@ -319,29 +320,60 @@ package org.apache.phoenix.parse;
     }
 
     /**
-     * Parse a single EXPLAIN option into the given builder. The option name 
is matched
+     * Closed-set keys for the EXPLAIN option list, used to reject duplicate 
options as they are
+     * encountered while parsing.
+     */
+    private enum ExplainOpt { REGIONS, VERBOSE, FORMAT }
+
+    /**
+     * Mutable accumulator for the EXPLAIN option list. Lifetime is one 
explain_node parse.
+     */
+    private static final class ExplainOptsAcc {
+        boolean regions;
+        boolean verbose;
+        ExplainOptions.Format format;
+        final EnumSet<ExplainOpt> seen = EnumSet.noneOf(ExplainOpt.class);
+
+        void mark(ExplainOpt opt) {
+            if (!seen.add(opt)) {
+                throw new RuntimeException("Duplicate EXPLAIN option: " + opt);
+            }
+        }
+
+        ExplainOptions build() {
+            return new ExplainOptions(regions, verbose,
+                format == null ? ExplainOptions.Format.TEXT : format);
+        }
+    }
+
+    /**
+     * Parse a single EXPLAIN option into the given accumulator. The option 
name is matched
      * case-insensitively against the closed set {REGIONS, VERBOSE, FORMAT}. 
REGIONS and VERBOSE are
-     * flags and must not carry a value; FORMAT requires a value of TEXT or 
JSON.
+     * flags and must not carry a value; FORMAT requires a value of TEXT or 
JSON. Duplicate options
+     * are rejected.
      */
-    private void parseExplainOption(ExplainOptions.Builder opts, String name, 
String value) {
+    private void parseExplainOption(ExplainOptsAcc opts, String name, String 
value) {
         if ("REGIONS".equalsIgnoreCase(name)) {
             if (value != null) {
                 throw new RuntimeException("EXPLAIN option REGIONS does not 
take a value");
             }
-            opts.setRegions(true);
+            opts.mark(ExplainOpt.REGIONS);
+            opts.regions = true;
         } else if ("VERBOSE".equalsIgnoreCase(name)) {
             if (value != null) {
                 throw new RuntimeException("EXPLAIN option VERBOSE does not 
take a value");
             }
-            opts.setVerbose(true);
+            opts.mark(ExplainOpt.VERBOSE);
+            opts.verbose = true;
         } else if ("FORMAT".equalsIgnoreCase(name)) {
             if (value == null) {
                 throw new RuntimeException("EXPLAIN option FORMAT requires a 
value: TEXT or JSON");
             }
+            opts.mark(ExplainOpt.FORMAT);
             if ("TEXT".equalsIgnoreCase(value)) {
-                opts.setFormat(ExplainOptions.Format.TEXT);
+                opts.format = ExplainOptions.Format.TEXT;
             } else if ("JSON".equalsIgnoreCase(value)) {
-                opts.setFormat(ExplainOptions.Format.JSON);
+                opts.format = ExplainOptions.Format.JSON;
             } else {
                 throw new RuntimeException("Unknown EXPLAIN FORMAT: " + value);
             }
@@ -509,11 +541,11 @@ oneStatement returns [BindableStatement ret]
 finally{ contextStack.pop(); }
     
 explain_node returns [BindableStatement ret]
-@init { ExplainOptions.Builder opts = new ExplainOptions.Builder(); }
+@init { ExplainOptsAcc opts = new ExplainOptsAcc(); }
     :   EXPLAIN
         (
             (LPAREN explain_option[opts] (COMMA explain_option[opts])* RPAREN)
-          | (WITH REGIONS                   { opts.setRegions(true); })
+          | (WITH REGIONS                   { opts.mark(ExplainOpt.REGIONS); 
opts.regions = true; })
         )?
         q=oneStatement
      {
@@ -523,8 +555,8 @@ explain_node returns [BindableStatement ret]
 
 // A single EXPLAIN option. REGIONS is a global keyword token. The remaining 
option keywords
 // (VERBOSE, FORMAT, TEXT, JSON) are not reserved and arrive as NAME tokens, 
validated in the action.
-explain_option[ExplainOptions.Builder opts]
-    :   REGIONS                 { opts.setRegions(true); }
+explain_option[ExplainOptsAcc opts]
+    :   REGIONS                 { opts.mark(ExplainOpt.REGIONS); opts.regions 
= true; }
     |   k=NAME (v=NAME)?        { parseExplainOption(opts, k.getText(), v == 
null ? null : v.getText()); }
     ;
 
diff --git 
a/phoenix-core-client/src/main/java/org/apache/phoenix/compile/ExplainPlanAttributes.java
 
b/phoenix-core-client/src/main/java/org/apache/phoenix/compile/ExplainPlanAttributes.java
index 24a74a771b..29be54c6e0 100644
--- 
a/phoenix-core-client/src/main/java/org/apache/phoenix/compile/ExplainPlanAttributes.java
+++ 
b/phoenix-core-client/src/main/java/org/apache/phoenix/compile/ExplainPlanAttributes.java
@@ -141,188 +141,95 @@ public class ExplainPlanAttributes {
   private final Integer regionLocationsTotalSize;
   private final int numRegionLocationLookups;
 
-  private static final ExplainPlanAttributes EXPLAIN_PLAN_INSTANCE = new 
ExplainPlanAttributes();
-
-  private ExplainPlanAttributes() {
-    this.tenantId = null;
-    this.viewName = null;
-    this.viewBaseName = null;
-    this.cdcScopes = null;
-    this.txnProvider = null;
-    this.rewrites = null;
-    this.estimatedRows = null;
-    this.estimatedSizeInBytes = null;
-    this.estimateInfoTs = null;
-    this.abstractExplainPlan = null;
-    this.onDuplicateKeyAction = null;
-    this.serverUpdateSet = null;
-    this.returningRow = false;
-    this.hint = null;
-    this.explainScanType = null;
-    this.consistency = null;
-    this.tableName = null;
-    this.keyRanges = null;
-    this.indexName = null;
-    this.indexKind = null;
-    this.indexRule = null;
-    this.indexRejected = null;
-    this.saltBuckets = null;
-    this.regionsPlanned = null;
-    this.scanTimeRangeMin = null;
-    this.scanTimeRangeMax = null;
-    this.splitsChunk = null;
-    this.useRoundRobinIterator = false;
-    this.samplingRate = null;
-    this.hexStringRVCOffset = null;
-    this.iteratorTypeAndScanSize = null;
-    this.scanEstimatedRows = null;
-    this.scanEstimatedSizeInBytes = null;
-    this.serverWhereFilter = null;
-    this.serverDistinctFilter = null;
-    this.serverMergeColumns = null;
-    this.serverParsedProjections = null;
-    this.serverProject = null;
-    this.serverFilters = null;
-    this.ignoredHints = null;
-    this.serverFirstKeyOnlyProjection = false;
-    this.serverEmptyColumnOnlyProjection = false;
-    this.serverAggregate = null;
-    this.serverGroupByLimit = null;
-    this.serverSortedBy = null;
-    this.serverOffset = null;
-    this.serverRowLimit = null;
-    this.clientFilterBy = null;
-    this.clientFilters = null;
-    this.clientAggregate = null;
-    this.clientDistinctFilter = null;
-    this.clientAfterAggregate = null;
-    this.clientSortAlgo = null;
-    this.clientSortedBy = null;
-    this.clientOffset = null;
-    this.clientRowLimit = null;
-    this.clientSequenceCount = null;
-    this.clientCursorName = null;
-    this.clientSteps = null;
-    this.lhsJoinQueryExplainPlan = null;
-    this.rhsJoinQueryExplainPlan = null;
-    this.subPlans = null;
-    this.dynamicServerFilter = null;
-    this.afterJoinFilter = null;
-    this.joinScannerLimit = null;
-    this.sortMergeSkipMerge = false;
-    this.regionLocations = null;
-    this.regionLocationsTotalSize = null;
-    this.numRegionLocationLookups = 0;
-  }
-
-  public ExplainPlanAttributes(String tenantId, String viewName, String 
viewBaseName,
-    String cdcScopes, String txnProvider, List<String> rewrites, Long 
estimatedRows,
-    Long estimatedSizeInBytes, Long estimateInfoTs, String abstractExplainPlan,
-    OnDuplicateKeyType onDuplicateKeyAction, List<String> serverUpdateSet, 
boolean returningRow,
-    Hint hint, String explainScanType, Consistency consistency, String 
tableName, String keyRanges,
-    String indexName, String indexKind, String indexRule, 
List<RejectedIndexEntry> indexRejected,
-    Integer saltBuckets, Integer regionsPlanned, Long scanTimeRangeMin, Long 
scanTimeRangeMax,
-    Integer splitsChunk, boolean useRoundRobinIterator, Double samplingRate,
-    String hexStringRVCOffset, String iteratorTypeAndScanSize, Long 
scanEstimatedRows,
-    Long scanEstimatedSizeInBytes, String serverWhereFilter, String 
serverDistinctFilter,
-    Set<PColumn> serverMergeColumns, Map<String, List<String>> 
serverParsedProjections,
-    boolean serverFirstKeyOnlyProjection, boolean 
serverEmptyColumnOnlyProjection,
-    String serverAggregate, Integer serverGroupByLimit, String serverSortedBy, 
Integer serverOffset,
-    Long serverRowLimit, String clientFilterBy, String clientAggregate, String 
clientDistinctFilter,
-    String clientAfterAggregate, String clientSortAlgo, String clientSortedBy, 
Integer clientOffset,
-    Integer clientRowLimit, Integer clientSequenceCount, String 
clientCursorName,
-    List<String> clientSteps, ExplainPlanAttributes lhsJoinQueryExplainPlan,
-    ExplainPlanAttributes rhsJoinQueryExplainPlan, List<ExplainPlanAttributes> 
subPlans,
-    String dynamicServerFilter, String afterJoinFilter, Long joinScannerLimit,
-    boolean sortMergeSkipMerge, List<HRegionLocation> regionLocations,
-    Integer regionLocationsTotalSize, int numRegionLocationLookups, 
List<String> serverProject,
-    List<ExplainFilter> serverFilters, Map<String, String> ignoredHints,
-    List<ExplainFilter> clientFilters) {
-    this.tenantId = tenantId;
-    this.viewName = viewName;
-    this.viewBaseName = viewBaseName;
-    this.cdcScopes = cdcScopes;
-    this.txnProvider = txnProvider;
-    this.rewrites = (rewrites == null || rewrites.isEmpty())
+  private static final ExplainPlanAttributes EXPLAIN_PLAN_INSTANCE =
+    new ExplainPlanAttributesBuilder().build();
+
+  private ExplainPlanAttributes(ExplainPlanAttributesBuilder b) {
+    this.tenantId = b.tenantId;
+    this.viewName = b.viewName;
+    this.viewBaseName = b.viewBaseName;
+    this.cdcScopes = b.cdcScopes;
+    this.txnProvider = b.txnProvider;
+    this.rewrites = (b.rewrites == null || b.rewrites.isEmpty())
       ? null
-      : Collections.unmodifiableList(new ArrayList<>(rewrites));
-    this.estimatedRows = estimatedRows;
-    this.estimatedSizeInBytes = estimatedSizeInBytes;
-    this.estimateInfoTs = estimateInfoTs;
-    this.abstractExplainPlan = abstractExplainPlan;
-    this.onDuplicateKeyAction = onDuplicateKeyAction;
-    this.serverUpdateSet = (serverUpdateSet == null || 
serverUpdateSet.isEmpty())
+      : Collections.unmodifiableList(new ArrayList<>(b.rewrites));
+    this.estimatedRows = b.estimatedRows;
+    this.estimatedSizeInBytes = b.estimatedSizeInBytes;
+    this.estimateInfoTs = b.estimateInfoTs;
+    this.abstractExplainPlan = b.abstractExplainPlan;
+    this.onDuplicateKeyAction = b.onDuplicateKeyAction;
+    this.serverUpdateSet = (b.serverUpdateSet == null || 
b.serverUpdateSet.isEmpty())
       ? null
-      : Collections.unmodifiableList(new ArrayList<>(serverUpdateSet));
-    this.returningRow = returningRow;
-    this.hint = hint;
-    this.explainScanType = explainScanType;
-    this.consistency = consistency;
-    this.tableName = tableName;
-    this.keyRanges = keyRanges;
-    this.indexName = indexName;
-    this.indexKind = indexKind;
-    this.indexRule = indexRule;
-    this.indexRejected = (indexRejected == null || indexRejected.isEmpty())
+      : Collections.unmodifiableList(new ArrayList<>(b.serverUpdateSet));
+    this.returningRow = b.returningRow;
+    this.hint = b.hint;
+    this.explainScanType = b.explainScanType;
+    this.consistency = b.consistency;
+    this.tableName = b.tableName;
+    this.keyRanges = b.keyRanges;
+    this.indexName = b.indexName;
+    this.indexKind = b.indexKind;
+    this.indexRule = b.indexRule;
+    this.indexRejected = (b.indexRejected == null || b.indexRejected.isEmpty())
       ? null
-      : Collections.unmodifiableList(new ArrayList<>(indexRejected));
-    this.saltBuckets = saltBuckets;
-    this.regionsPlanned = regionsPlanned;
-    this.scanTimeRangeMin = scanTimeRangeMin;
-    this.scanTimeRangeMax = scanTimeRangeMax;
-    this.splitsChunk = splitsChunk;
-    this.useRoundRobinIterator = useRoundRobinIterator;
-    this.samplingRate = samplingRate;
-    this.hexStringRVCOffset = hexStringRVCOffset;
-    this.iteratorTypeAndScanSize = iteratorTypeAndScanSize;
-    this.scanEstimatedRows = scanEstimatedRows;
-    this.scanEstimatedSizeInBytes = scanEstimatedSizeInBytes;
-    this.serverWhereFilter = serverWhereFilter;
-    this.serverDistinctFilter = serverDistinctFilter;
-    this.serverMergeColumns = serverMergeColumns;
-    this.serverParsedProjections = 
copyServerParsedProjections(serverParsedProjections);
-    this.serverFirstKeyOnlyProjection = serverFirstKeyOnlyProjection;
-    this.serverEmptyColumnOnlyProjection = serverEmptyColumnOnlyProjection;
-    this.serverAggregate = serverAggregate;
-    this.serverGroupByLimit = serverGroupByLimit;
-    this.serverSortedBy = serverSortedBy;
-    this.serverOffset = serverOffset;
-    this.serverRowLimit = serverRowLimit;
-    this.clientFilterBy = clientFilterBy;
-    this.clientFilters = (clientFilters == null || clientFilters.isEmpty())
+      : Collections.unmodifiableList(new ArrayList<>(b.indexRejected));
+    this.saltBuckets = b.saltBuckets;
+    this.regionsPlanned = b.regionsPlanned;
+    this.scanTimeRangeMin = b.scanTimeRangeMin;
+    this.scanTimeRangeMax = b.scanTimeRangeMax;
+    this.splitsChunk = b.splitsChunk;
+    this.useRoundRobinIterator = b.useRoundRobinIterator;
+    this.samplingRate = b.samplingRate;
+    this.hexStringRVCOffset = b.hexStringRVCOffset;
+    this.iteratorTypeAndScanSize = b.iteratorTypeAndScanSize;
+    this.scanEstimatedRows = b.scanEstimatedRows;
+    this.scanEstimatedSizeInBytes = b.scanEstimatedSizeInBytes;
+    this.serverWhereFilter = b.serverWhereFilter;
+    this.serverDistinctFilter = b.serverDistinctFilter;
+    this.serverMergeColumns = b.serverMergeColumns;
+    this.serverParsedProjections = 
copyServerParsedProjections(b.serverParsedProjections);
+    this.serverProject = (b.serverProject == null || b.serverProject.isEmpty())
       ? null
-      : Collections.unmodifiableList(new ArrayList<>(clientFilters));
-    this.clientAggregate = clientAggregate;
-    this.clientDistinctFilter = clientDistinctFilter;
-    this.clientAfterAggregate = clientAfterAggregate;
-    this.clientSortAlgo = clientSortAlgo;
-    this.clientSortedBy = clientSortedBy;
-    this.clientOffset = clientOffset;
-    this.clientRowLimit = clientRowLimit;
-    this.clientSequenceCount = clientSequenceCount;
-    this.clientCursorName = clientCursorName;
-    this.clientSteps = (clientSteps == null || clientSteps.isEmpty())
+      : Collections.unmodifiableList(new ArrayList<>(b.serverProject));
+    this.serverFilters = (b.serverFilters == null || b.serverFilters.isEmpty())
       ? null
-      : Collections.unmodifiableList(new ArrayList<>(clientSteps));
-    this.lhsJoinQueryExplainPlan = lhsJoinQueryExplainPlan;
-    this.rhsJoinQueryExplainPlan = rhsJoinQueryExplainPlan;
-    this.subPlans = subPlans;
-    this.dynamicServerFilter = dynamicServerFilter;
-    this.afterJoinFilter = afterJoinFilter;
-    this.joinScannerLimit = joinScannerLimit;
-    this.sortMergeSkipMerge = sortMergeSkipMerge;
-    this.regionLocations = regionLocations;
-    this.regionLocationsTotalSize = regionLocationsTotalSize;
-    this.numRegionLocationLookups = numRegionLocationLookups;
-    this.serverProject = (serverProject == null || serverProject.isEmpty())
+      : Collections.unmodifiableList(new ArrayList<>(b.serverFilters));
+    this.ignoredHints = (b.ignoredHints == null || b.ignoredHints.isEmpty())
       ? null
-      : Collections.unmodifiableList(new ArrayList<>(serverProject));
-    this.serverFilters = (serverFilters == null || serverFilters.isEmpty())
+      : Collections.unmodifiableMap(new LinkedHashMap<>(b.ignoredHints));
+    this.serverFirstKeyOnlyProjection = b.serverFirstKeyOnlyProjection;
+    this.serverEmptyColumnOnlyProjection = b.serverEmptyColumnOnlyProjection;
+    this.serverAggregate = b.serverAggregate;
+    this.serverGroupByLimit = b.serverGroupByLimit;
+    this.serverSortedBy = b.serverSortedBy;
+    this.serverOffset = b.serverOffset;
+    this.serverRowLimit = b.serverRowLimit;
+    this.clientFilterBy = b.clientFilterBy;
+    this.clientFilters = (b.clientFilters == null || b.clientFilters.isEmpty())
       ? null
-      : Collections.unmodifiableList(new ArrayList<>(serverFilters));
-    this.ignoredHints = (ignoredHints == null || ignoredHints.isEmpty())
+      : Collections.unmodifiableList(new ArrayList<>(b.clientFilters));
+    this.clientAggregate = b.clientAggregate;
+    this.clientDistinctFilter = b.clientDistinctFilter;
+    this.clientAfterAggregate = b.clientAfterAggregate;
+    this.clientSortAlgo = b.clientSortAlgo;
+    this.clientSortedBy = b.clientSortedBy;
+    this.clientOffset = b.clientOffset;
+    this.clientRowLimit = b.clientRowLimit;
+    this.clientSequenceCount = b.clientSequenceCount;
+    this.clientCursorName = b.clientCursorName;
+    this.clientSteps = (b.clientSteps == null || b.clientSteps.isEmpty())
       ? null
-      : Collections.unmodifiableMap(new LinkedHashMap<>(ignoredHints));
+      : Collections.unmodifiableList(new ArrayList<>(b.clientSteps));
+    this.lhsJoinQueryExplainPlan = b.lhsJoinQueryExplainPlan;
+    this.rhsJoinQueryExplainPlan = b.rhsJoinQueryExplainPlan;
+    this.subPlans = b.subPlans;
+    this.dynamicServerFilter = b.dynamicServerFilter;
+    this.afterJoinFilter = b.afterJoinFilter;
+    this.joinScannerLimit = b.joinScannerLimit;
+    this.sortMergeSkipMerge = b.sortMergeSkipMerge;
+    this.regionLocations = b.regionLocations;
+    this.regionLocationsTotalSize = b.regionLocationsTotalSize;
+    this.numRegionLocationLookups = b.numRegionLocationLookups;
   }
 
   public String getTenantId() {
@@ -1236,21 +1143,7 @@ public class ExplainPlanAttributes {
     }
 
     public ExplainPlanAttributes build() {
-      return new ExplainPlanAttributes(tenantId, viewName, viewBaseName, 
cdcScopes, txnProvider,
-        rewrites, estimatedRows, estimatedSizeInBytes, estimateInfoTs, 
abstractExplainPlan,
-        onDuplicateKeyAction, serverUpdateSet, returningRow, hint, 
explainScanType, consistency,
-        tableName, keyRanges, indexName, indexKind, indexRule, indexRejected, 
saltBuckets,
-        regionsPlanned, scanTimeRangeMin, scanTimeRangeMax, splitsChunk, 
useRoundRobinIterator,
-        samplingRate, hexStringRVCOffset, iteratorTypeAndScanSize, 
scanEstimatedRows,
-        scanEstimatedSizeInBytes, serverWhereFilter, serverDistinctFilter, 
serverMergeColumns,
-        serverParsedProjections, serverFirstKeyOnlyProjection, 
serverEmptyColumnOnlyProjection,
-        serverAggregate, serverGroupByLimit, serverSortedBy, serverOffset, 
serverRowLimit,
-        clientFilterBy, clientAggregate, clientDistinctFilter, 
clientAfterAggregate, clientSortAlgo,
-        clientSortedBy, clientOffset, clientRowLimit, clientSequenceCount, 
clientCursorName,
-        clientSteps, lhsJoinQueryExplainPlan, rhsJoinQueryExplainPlan, 
subPlans,
-        dynamicServerFilter, afterJoinFilter, joinScannerLimit, 
sortMergeSkipMerge, regionLocations,
-        regionLocationsTotalSize, numRegionLocationLookups, serverProject, 
serverFilters,
-        ignoredHints, clientFilters);
+      return new ExplainPlanAttributes(this);
     }
   }
 }
diff --git 
a/phoenix-core-client/src/main/java/org/apache/phoenix/iterate/ExplainTable.java
 
b/phoenix-core-client/src/main/java/org/apache/phoenix/iterate/ExplainTable.java
index dff466b3c8..1045d492fc 100644
--- 
a/phoenix-core-client/src/main/java/org/apache/phoenix/iterate/ExplainTable.java
+++ 
b/phoenix-core-client/src/main/java/org/apache/phoenix/iterate/ExplainTable.java
@@ -22,7 +22,6 @@ import java.text.Format;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedHashSet;
 import java.util.List;
@@ -804,14 +803,13 @@ public abstract class ExplainTable {
     }
     try {
       StringBuilder buf = new StringBuilder().append(REGION_LOCATIONS);
-      Set<RegionBoundary> regionBoundaries = new HashSet<>();
+      Set<String> regionBoundaries = new LinkedHashSet<>();
       List<HRegionLocation> regionLocations = new ArrayList<>();
       for (HRegionLocation regionLocation : regionLocationsFromResultIterator) 
{
-        RegionBoundary regionBoundary = new 
RegionBoundary(regionLocation.getRegion().getStartKey(),
-          regionLocation.getRegion().getEndKey());
-        if (!regionBoundaries.contains(regionBoundary)) {
+        String regionBoundary = 
Bytes.toStringBinary(regionLocation.getRegion().getStartKey()) + ":"
+          + Bytes.toStringBinary(regionLocation.getRegion().getEndKey());
+        if (regionBoundaries.add(regionBoundary)) {
           regionLocations.add(regionLocation);
-          regionBoundaries.add(regionBoundary);
         }
       }
       int maxLimitRegionLoc = 
context.getConnection().getQueryServices().getConfiguration().getInt(
@@ -845,39 +843,6 @@ public abstract class ExplainTable {
     }
   }
 
-  /**
-   * Region boundary class with start and end key of the region.
-   */
-  private static class RegionBoundary {
-    private final byte[] startKey;
-    private final byte[] endKey;
-
-    RegionBoundary(byte[] startKey, byte[] endKey) {
-      this.startKey = startKey;
-      this.endKey = endKey;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-      if (this == o) {
-        return true;
-      }
-      if (o == null || getClass() != o.getClass()) {
-        return false;
-      }
-      RegionBoundary that = (RegionBoundary) o;
-      return Bytes.compareTo(startKey, that.startKey) == 0
-        && Bytes.compareTo(endKey, that.endKey) == 0;
-    }
-
-    @Override
-    public int hashCode() {
-      int result = Arrays.hashCode(startKey);
-      result = 31 * result + Arrays.hashCode(endKey);
-      return result;
-    }
-  }
-
   @SuppressWarnings("rawtypes")
   private void appendPKColumnValue(StringBuilder buf, byte[] range, Boolean 
isNull, int slotIndex,
     boolean changeViewIndexId) {
diff --git 
a/phoenix-core-client/src/main/java/org/apache/phoenix/parse/ExplainOptions.java
 
b/phoenix-core-client/src/main/java/org/apache/phoenix/parse/ExplainOptions.java
index 776f4a7059..95b1bce97c 100644
--- 
a/phoenix-core-client/src/main/java/org/apache/phoenix/parse/ExplainOptions.java
+++ 
b/phoenix-core-client/src/main/java/org/apache/phoenix/parse/ExplainOptions.java
@@ -79,49 +79,4 @@ public final class ExplainOptions {
     return "ExplainOptions{regions=" + regions + ", verbose=" + verbose + ", 
format=" + format
       + "}";
   }
-
-  /**
-   * Mutable builder used by the parser to accumulate options as they are 
encountered in the option
-   * list. Rejects duplicate options.
-   */
-  public static final class Builder {
-    private boolean regions;
-    private boolean regionsSet;
-    private boolean verbose;
-    private boolean verboseSet;
-    private Format format;
-
-    public Builder setRegions(boolean regions) {
-      if (regionsSet) {
-        throw new RuntimeException("Duplicate EXPLAIN option: REGIONS");
-      }
-      this.regions = regions;
-      this.regionsSet = true;
-      return this;
-    }
-
-    public Builder setVerbose(boolean verbose) {
-      if (verboseSet) {
-        throw new RuntimeException("Duplicate EXPLAIN option: VERBOSE");
-      }
-      this.verbose = verbose;
-      this.verboseSet = true;
-      return this;
-    }
-
-    public Builder setFormat(Format format) {
-      if (format == null) {
-        throw new RuntimeException("EXPLAIN option FORMAT requires a value: 
TEXT or JSON");
-      }
-      if (this.format != null) {
-        throw new RuntimeException("Duplicate EXPLAIN option: FORMAT");
-      }
-      this.format = format;
-      return this;
-    }
-
-    public ExplainOptions build() {
-      return new ExplainOptions(regions, verbose, format == null ? Format.TEXT 
: format);
-    }
-  }
 }

Reply via email to