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 53da985108 PHOENIX-7882 Per scan EXPLAIN output improvements (#2505)
53da985108 is described below
commit 53da985108caa5c8c77e57c6318010037bda37dc
Author: Andrew Purtell <[email protected]>
AuthorDate: Tue Jun 9 12:02:12 2026 -0700
PHOENIX-7882 Per scan EXPLAIN output improvements (#2505)
---
.../phoenix/compile/ExplainPlanAttributes.java | 100 +++++++---
.../phoenix/iterate/BaseResultIterators.java | 5 +
.../org/apache/phoenix/iterate/ExplainTable.java | 76 +++++++-
.../java/org/apache/phoenix/end2end/Bson4IT.java | 24 +--
.../java/org/apache/phoenix/end2end/Bson5IT.java | 12 +-
.../phoenix/end2end/SortMergeJoinMoreIT.java | 4 +-
.../end2end/join/SortMergeJoinGlobalIndexIT.java | 9 +-
.../end2end/join/SortMergeJoinLocalIndexIT.java | 7 +-
.../query/explain/ExplainJsonNormalizer.java | 3 +
.../phoenix/query/explain/ExplainPlanTest.java | 203 +++++++++++++++------
.../phoenix/query/explain/ExplainPlanTestUtil.java | 27 +++
.../query/explain/ExplainTextNormalizer.java | 6 +-
12 files changed, 359 insertions(+), 117 deletions(-)
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 74cd983332..fcf858479a 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
@@ -35,16 +35,16 @@ import org.apache.phoenix.schema.PColumn;
* Strings containing entire plan.
*/
@JsonPropertyOrder({ "abstractExplainPlan", "hint", "explainScanType",
"consistency", "tableName",
- "keyRanges", "scanTimeRangeMin", "scanTimeRangeMax", "splitsChunk",
"useRoundRobinIterator",
- "samplingRate", "hexStringRVCOffset", "iteratorTypeAndScanSize",
"estimatedRows",
- "estimatedSizeInBytes", "serverWhereFilter", "serverDistinctFilter",
"serverMergeColumns",
- "serverArrayElementProjection", "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" })
+ "keyRanges", "indexName", "indexKind", "saltBuckets", "regionsPlanned",
"scanTimeRangeMin",
+ "scanTimeRangeMax", "splitsChunk", "useRoundRobinIterator", "samplingRate",
"hexStringRVCOffset",
+ "iteratorTypeAndScanSize", "estimatedRows", "estimatedSizeInBytes",
"serverWhereFilter",
+ "serverDistinctFilter", "serverMergeColumns",
"serverArrayElementProjection", "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" })
public class ExplainPlanAttributes {
// Plan identity and scan-level metadata
@@ -54,6 +54,10 @@ public class ExplainPlanAttributes {
private final Consistency consistency;
private final String tableName;
private final String keyRanges;
+ private final String indexName;
+ private final String indexKind;
+ private final Integer saltBuckets;
+ private final Integer regionsPlanned;
private final Long scanTimeRangeMin;
private final Long scanTimeRangeMax;
private final Integer splitsChunk;
@@ -113,6 +117,10 @@ public class ExplainPlanAttributes {
this.consistency = null;
this.tableName = null;
this.keyRanges = null;
+ this.indexName = null;
+ this.indexKind = null;
+ this.saltBuckets = null;
+ this.regionsPlanned = null;
this.scanTimeRangeMin = null;
this.scanTimeRangeMax = null;
this.splitsChunk = null;
@@ -155,8 +163,9 @@ public class ExplainPlanAttributes {
}
public ExplainPlanAttributes(String abstractExplainPlan, Hint hint, String
explainScanType,
- Consistency consistency, String tableName, String keyRanges, Long
scanTimeRangeMin,
- Long scanTimeRangeMax, Integer splitsChunk, boolean useRoundRobinIterator,
Double samplingRate,
+ Consistency consistency, String tableName, String keyRanges, String
indexName, String indexKind,
+ Integer saltBuckets, Integer regionsPlanned, Long scanTimeRangeMin, Long
scanTimeRangeMax,
+ Integer splitsChunk, boolean useRoundRobinIterator, Double samplingRate,
String hexStringRVCOffset, String iteratorTypeAndScanSize, Long
estimatedRows,
Long estimatedSizeInBytes, String serverWhereFilter, String
serverDistinctFilter,
Set<PColumn> serverMergeColumns, boolean serverArrayElementProjection,
String serverAggregate,
@@ -175,6 +184,10 @@ public class ExplainPlanAttributes {
this.consistency = consistency;
this.tableName = tableName;
this.keyRanges = keyRanges;
+ this.indexName = indexName;
+ this.indexKind = indexKind;
+ this.saltBuckets = saltBuckets;
+ this.regionsPlanned = regionsPlanned;
this.scanTimeRangeMin = scanTimeRangeMin;
this.scanTimeRangeMax = scanTimeRangeMax;
this.splitsChunk = splitsChunk;
@@ -242,6 +255,22 @@ public class ExplainPlanAttributes {
return keyRanges;
}
+ public String getIndexName() {
+ return indexName;
+ }
+
+ public String getIndexKind() {
+ return indexKind;
+ }
+
+ public Integer getSaltBuckets() {
+ return saltBuckets;
+ }
+
+ public Integer getRegionsPlanned() {
+ return regionsPlanned;
+ }
+
public Long getScanTimeRangeMin() {
return scanTimeRangeMin;
}
@@ -411,6 +440,10 @@ public class ExplainPlanAttributes {
private Consistency consistency;
private String tableName;
private String keyRanges;
+ private String indexName;
+ private String indexKind;
+ private Integer saltBuckets;
+ private Integer regionsPlanned;
private Long scanTimeRangeMin;
private Long scanTimeRangeMax;
private Integer splitsChunk;
@@ -462,6 +495,10 @@ public class ExplainPlanAttributes {
this.consistency = explainPlanAttributes.getConsistency();
this.tableName = explainPlanAttributes.getTableName();
this.keyRanges = explainPlanAttributes.getKeyRanges();
+ this.indexName = explainPlanAttributes.getIndexName();
+ this.indexKind = explainPlanAttributes.getIndexKind();
+ this.saltBuckets = explainPlanAttributes.getSaltBuckets();
+ this.regionsPlanned = explainPlanAttributes.getRegionsPlanned();
this.scanTimeRangeMin = explainPlanAttributes.getScanTimeRangeMin();
this.scanTimeRangeMax = explainPlanAttributes.getScanTimeRangeMax();
this.splitsChunk = explainPlanAttributes.getSplitsChunk();
@@ -534,6 +571,26 @@ public class ExplainPlanAttributes {
return this;
}
+ public ExplainPlanAttributesBuilder setIndexName(String indexName) {
+ this.indexName = indexName;
+ return this;
+ }
+
+ public ExplainPlanAttributesBuilder setIndexKind(String indexKind) {
+ this.indexKind = indexKind;
+ return this;
+ }
+
+ public ExplainPlanAttributesBuilder setSaltBuckets(Integer saltBuckets) {
+ this.saltBuckets = saltBuckets;
+ return this;
+ }
+
+ public ExplainPlanAttributesBuilder setRegionsPlanned(Integer
regionsPlanned) {
+ this.regionsPlanned = regionsPlanned;
+ return this;
+ }
+
public ExplainPlanAttributesBuilder setScanTimeRangeMin(Long
scanTimeRangeMin) {
this.scanTimeRangeMin = scanTimeRangeMin;
return this;
@@ -743,15 +800,16 @@ public class ExplainPlanAttributes {
public ExplainPlanAttributes build() {
return new ExplainPlanAttributes(abstractExplainPlan, hint,
explainScanType, consistency,
- tableName, keyRanges, scanTimeRangeMin, scanTimeRangeMax, splitsChunk,
- useRoundRobinIterator, samplingRate, hexStringRVCOffset,
iteratorTypeAndScanSize,
- estimatedRows, estimatedSizeInBytes, serverWhereFilter,
serverDistinctFilter,
- serverMergeColumns, serverArrayElementProjection, 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);
+ tableName, keyRanges, indexName, indexKind, saltBuckets,
regionsPlanned, scanTimeRangeMin,
+ scanTimeRangeMax, splitsChunk, useRoundRobinIterator, samplingRate,
hexStringRVCOffset,
+ iteratorTypeAndScanSize, estimatedRows, estimatedSizeInBytes,
serverWhereFilter,
+ serverDistinctFilter, serverMergeColumns,
serverArrayElementProjection, 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);
}
}
}
diff --git
a/phoenix-core-client/src/main/java/org/apache/phoenix/iterate/BaseResultIterators.java
b/phoenix-core-client/src/main/java/org/apache/phoenix/iterate/BaseResultIterators.java
index 45e160fac8..7e81d3a534 100644
---
a/phoenix-core-client/src/main/java/org/apache/phoenix/iterate/BaseResultIterators.java
+++
b/phoenix-core-client/src/main/java/org/apache/phoenix/iterate/BaseResultIterators.java
@@ -640,6 +640,11 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
else return splits;
}
+ @Override
+ protected int getSplitCount() {
+ return splits == null ? 0 : splits.size();
+ }
+
@Override
public List<List<Scan>> getScans() {
if (scans == null) return Collections.emptyList();
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 8382100561..1a1f32786f 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
@@ -122,6 +122,33 @@ public abstract class ExplainTable {
return buf.toString();
}
+ /**
+ * Number of region scan splits the plan will hit, used to render the {@code
REGIONS PLANNED}
+ * per-scan line.
+ * @return the split count, or 0 when unknown
+ */
+ protected int getSplitCount() {
+ return 0;
+ }
+
+ /**
+ * Logical name used to render a table or index in EXPLAIN output. Shared by
both the scan
+ * {@code OVER} line's local index decoration and the per scan {@code INDEX}
line.
+ * @param table the scanned table or index
+ * @return the display name with any child-view local-index prefix stripped
+ */
+ private static String getExplainIndexName(PTable table) {
+ String indexName = table.getName().getString();
+ if (
+ table.getIndexType() == PTable.IndexType.LOCAL && table.getViewIndexId()
!= null
+ && indexName.contains(QueryConstants.CHILD_VIEW_INDEX_NAME_SEPARATOR)
+ ) {
+ int lastIndexOf =
indexName.lastIndexOf(QueryConstants.CHILD_VIEW_INDEX_NAME_SEPARATOR);
+ indexName = indexName.substring(lastIndexOf + 1);
+ }
+ return indexName;
+ }
+
protected void explain(String prefix, List<String> planSteps,
ExplainPlanAttributesBuilder explainPlanAttributesBuilder,
List<HRegionLocation> regionLocations) {
@@ -148,15 +175,7 @@ public abstract class ExplainTable {
String tableName = tableRef.getTable().getPhysicalName().getString();
if (tableRef.getTable().getIndexType() == PTable.IndexType.LOCAL) {
- String indexName = tableRef.getTable().getName().getString();
- if (
- tableRef.getTable().getViewIndexId() != null
- && indexName.contains(QueryConstants.CHILD_VIEW_INDEX_NAME_SEPARATOR)
- ) {
- int lastIndexOf =
indexName.lastIndexOf(QueryConstants.CHILD_VIEW_INDEX_NAME_SEPARATOR);
- indexName = indexName.substring(lastIndexOf + 1);
- }
- tableName = indexName + "(" + tableName + ")";
+ tableName = getExplainIndexName(tableRef.getTable()) + "(" + tableName +
")";
}
buf.append("OVER ").append(tableName);
@@ -178,6 +197,45 @@ public abstract class ExplainTable {
explainPlanAttributesBuilder.setKeyRanges(appendKeyRanges());
}
}
+
+ PTable.IndexType indexType = tableRef.getTable().getIndexType();
+ String explainIndexName = getExplainIndexName(tableRef.getTable());
+ String indexKind = null;
+ if (indexType != null) {
+ switch (indexType) {
+ case LOCAL:
+ indexKind = "LOCAL";
+ break;
+ case GLOBAL:
+ indexKind = "GLOBAL";
+ break;
+ case UNCOVERED_GLOBAL:
+ indexKind = "UNCOVERED GLOBAL";
+ break;
+ default:
+ indexKind = null;
+ }
+ }
+ planSteps.add(" INDEX " + explainIndexName + (indexKind == null ? "" :
" " + indexKind));
+ Integer bucketNum = tableRef.getTable().getBucketNum();
+ if (bucketNum != null) {
+ planSteps.add(" SALT BUCKETS " + bucketNum);
+ }
+ int splitCount = getSplitCount();
+ if (splitCount > 0) {
+ planSteps.add(" REGIONS PLANNED " + splitCount);
+ }
+ if (explainPlanAttributesBuilder != null) {
+ explainPlanAttributesBuilder.setIndexName(explainIndexName);
+ explainPlanAttributesBuilder.setIndexKind(indexKind);
+ if (bucketNum != null) {
+ explainPlanAttributesBuilder.setSaltBuckets(bucketNum);
+ }
+ if (splitCount > 0) {
+ explainPlanAttributesBuilder.setRegionsPlanned(splitCount);
+ }
+ }
+
if (context.getScan() != null &&
tableRef.getTable().getRowTimestampColPos() != -1) {
TimeRange range = context.getScan().getTimeRange();
planSteps.add(" ROW TIMESTAMP FILTER [" + range.getMin() + ", " +
range.getMax() + ")");
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/Bson4IT.java
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/Bson4IT.java
index c2d38bbac9..7c2b93f4d3 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/Bson4IT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/Bson4IT.java
@@ -642,9 +642,9 @@ public class Bson4IT extends ParallelStatsDisabledIT {
.append("browserling", new
BsonBinary(PDouble.INSTANCE.toBytes(-505169340.54880095)))
.append("track[0].shot[2][0].city.standard[5]", new
BsonString("soft"))
.append("track[0].shot[2][0].city.problem[2]",
- new BsonDocument().append("$ADD", new BsonArray(Arrays.asList(
- new BsonString("track[0].shot[2][0].city.problem[2]"),
- new BsonDouble(529.435))))))
+ new BsonDocument().append("$ADD",
+ new BsonArray(Arrays.asList(new
BsonString("track[0].shot[2][0].city.problem[2]"),
+ new BsonDouble(529.435))))))
.append("$UNSET",
new BsonDocument().append("track[0].shot[2][0].city.flame", new
BsonNull()));
@@ -728,9 +728,9 @@ public class Bson4IT extends ParallelStatsDisabledIT {
.append("new_field1", new
BsonBinary(PDouble.INSTANCE.toBytes(-505169340.54880095)))
.append("track[0].shot[2][0].city.standard[5]", new
BsonString("soft_new_val"))
.append("track[0].shot[2][0].city.problem[2]",
- new BsonDocument().append("$ADD", new BsonArray(Arrays.asList(
- new BsonString("track[0].shot[2][0].city.problem[2]"),
- new BsonInt32(123))))));
+ new BsonDocument().append("$ADD",
+ new BsonArray(Arrays.asList(new
BsonString("track[0].shot[2][0].city.problem[2]"),
+ new BsonInt32(123))))));
stmt = conn.prepareStatement(
"UPSERT INTO " + tableName + " VALUES (?) ON DUPLICATE KEY UPDATE COL
= CASE WHEN"
@@ -832,9 +832,9 @@ public class Bson4IT extends ParallelStatsDisabledIT {
.append("browserling", new
BsonBinary(PDouble.INSTANCE.toBytes(-505169340.54880095)))
.append("track[0].shot[2][0].city.standard[5]", new
BsonString("soft"))
.append("track[0].shot[2][0].city.problem[2]",
- new BsonDocument().append("$ADD", new BsonArray(Arrays.asList(
- new BsonString("track[0].shot[2][0].city.problem[2]"),
- new BsonDouble(529.435))))))
+ new BsonDocument().append("$ADD",
+ new BsonArray(Arrays.asList(new
BsonString("track[0].shot[2][0].city.problem[2]"),
+ new BsonDouble(529.435))))))
.append("$UNSET",
new BsonDocument().append("track[0].shot[2][0].city.flame", new
BsonNull()));
@@ -914,9 +914,9 @@ public class Bson4IT extends ParallelStatsDisabledIT {
.append("new_field1", new
BsonBinary(PDouble.INSTANCE.toBytes(-505169340.54880095)))
.append("track[0].shot[2][0].city.standard[5]", new
BsonString("soft_new_val"))
.append("track[0].shot[2][0].city.problem[2]",
- new BsonDocument().append("$ADD", new BsonArray(Arrays.asList(
- new BsonString("track[0].shot[2][0].city.problem[2]"),
- new BsonInt32(123))))));
+ new BsonDocument().append("$ADD",
+ new BsonArray(Arrays.asList(new
BsonString("track[0].shot[2][0].city.problem[2]"),
+ new BsonInt32(123))))));
stmt = conn.prepareStatement(
"UPSERT INTO " + tableName + " VALUES (?) ON DUPLICATE KEY UPDATE_ONLY
COL = CASE WHEN"
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/Bson5IT.java
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/Bson5IT.java
index 6978dcb174..0b7d4be140 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/Bson5IT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/Bson5IT.java
@@ -251,9 +251,9 @@ public class Bson5IT extends ParallelStatsDisabledIT {
.append("browserling", new
BsonBinary(PDouble.INSTANCE.toBytes(-505169340.54880095)))
.append("track[0].shot[2][0].city.standard[5]", new
BsonString("soft"))
.append("track[0].shot[2][0].city.problem[2]",
- new BsonDocument().append("$ADD", new BsonArray(Arrays.asList(
- new BsonString("track[0].shot[2][0].city.problem[2]"),
- new BsonDouble(529.435))))))
+ new BsonDocument().append("$ADD",
+ new BsonArray(Arrays.asList(new
BsonString("track[0].shot[2][0].city.problem[2]"),
+ new BsonDouble(529.435))))))
.append("$UNSET",
new BsonDocument().append("track[0].shot[2][0].city.flame", new
BsonNull()));
@@ -488,9 +488,9 @@ public class Bson5IT extends ParallelStatsDisabledIT {
.append("browserling", new
BsonBinary(PDouble.INSTANCE.toBytes(-505169340.54880095)))
.append("track[0].shot[2][0].city.standard[5]", new
BsonString("soft"))
.append("track[0].shot[2][0].city.problem[2]",
- new BsonDocument().append("$ADD", new BsonArray(Arrays.asList(
- new BsonString("track[0].shot[2][0].city.problem[2]"),
- new BsonDouble(529.435))))))
+ new BsonDocument().append("$ADD",
+ new BsonArray(Arrays.asList(new
BsonString("track[0].shot[2][0].city.problem[2]"),
+ new BsonDouble(529.435))))))
.append("$UNSET",
new BsonDocument().append("track[0].shot[2][0].city.flame", new
BsonNull()));
diff --git
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SortMergeJoinMoreIT.java
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SortMergeJoinMoreIT.java
index 1a57ae39e0..26621c1c9b 100644
---
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SortMergeJoinMoreIT.java
+++
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SortMergeJoinMoreIT.java
@@ -434,8 +434,8 @@ public class SortMergeJoinMoreIT extends
ParallelStatsDisabledIT {
.serverDistinctFilter("SERVER DISTINCT PREFIX FILTER OVER [BUCKET,
TIMESTAMP, LOCATION]")
.serverAggregate(
"SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [BUCKET,
TIMESTAMP, LOCATION]")
- .clientSortAlgo("CLIENT MERGE SORT").clientSortedBy("[BUCKET,
TIMESTAMP]")
- .end().rhs().iteratorType("PARALLEL").scanType("SKIP SCAN ON 2
RANGES").table(t[i])
+ .clientSortAlgo("CLIENT MERGE SORT").clientSortedBy("[BUCKET,
TIMESTAMP]").end().rhs()
+ .iteratorType("PARALLEL").scanType("SKIP SCAN ON 2
RANGES").table(t[i])
.keyRanges(rhsKeyRanges)
.serverWhereFilter("SERVER FILTER BY FIRST KEY ONLY AND SRC_LOCATION
= DST_LOCATION")
.serverDistinctFilter(
diff --git
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SortMergeJoinGlobalIndexIT.java
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SortMergeJoinGlobalIndexIT.java
index 59d69aa160..612768aabc 100644
---
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SortMergeJoinGlobalIndexIT.java
+++
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SortMergeJoinGlobalIndexIT.java
@@ -65,9 +65,8 @@ public class SortMergeJoinGlobalIndexIT extends
SortMergeJoinIT {
.serverWhereFilter("SERVER FILTER BY FIRST KEY
ONLY").serverSortedBy("[\"S.:supplier_id\"]")
.clientSortAlgo("CLIENT MERGE SORT").end().rhs()
.abstractExplainPlan("SORT-MERGE-JOIN (INNER)").sortMergeSkipMerge(true)
- .clientSortedBy("[\"I.0:supplier_id\"]").lhs()
- .scanType("FULL
SCAN").table(itemIndex).serverSortedBy("[\"I.:item_id\"]")
- .clientSortAlgo("CLIENT MERGE SORT").end().rhs()
+ .clientSortedBy("[\"I.0:supplier_id\"]").lhs().scanType("FULL
SCAN").table(itemIndex)
+ .serverSortedBy("[\"I.:item_id\"]").clientSortAlgo("CLIENT MERGE
SORT").end().rhs()
.scanType("FULL SCAN").table(order).serverWhereFilter("SERVER FILTER BY
QUANTITY < 5000")
.serverSortedBy("[\"O.item_id\"]").clientSortAlgo("CLIENT MERGE
SORT").end().end();
}
@@ -96,8 +95,8 @@ public class SortMergeJoinGlobalIndexIT extends
SortMergeJoinIT {
assertPlan(attributes).abstractExplainPlan("SORT-MERGE-JOIN
(INNER)").sortMergeSkipMerge(false)
.clientRowLimit(4).lhs().scanType("FULL SCAN").table(itemIndex)
.serverWhereFilter("SERVER FILTER BY FIRST KEY
ONLY").serverSortedBy("[\"I.:item_id\"]")
- .clientSortAlgo("CLIENT MERGE SORT").end().rhs().scanType("FULL SCAN")
- .table(order).serverSortedBy(queryIndex == 0 ? "[\"O.item_id\"]" :
"[\"item_id\"]")
+ .clientSortAlgo("CLIENT MERGE SORT").end().rhs().scanType("FULL
SCAN").table(order)
+ .serverSortedBy(queryIndex == 0 ? "[\"O.item_id\"]" : "[\"item_id\"]")
.clientSortAlgo("CLIENT MERGE SORT").end();
}
diff --git
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SortMergeJoinLocalIndexIT.java
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SortMergeJoinLocalIndexIT.java
index ec31377e1a..95d5f44a2a 100644
---
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SortMergeJoinLocalIndexIT.java
+++
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/SortMergeJoinLocalIndexIT.java
@@ -67,10 +67,9 @@ public class SortMergeJoinLocalIndexIT extends
SortMergeJoinIT {
.serverWhereFilter("SERVER FILTER BY FIRST KEY
ONLY").serverSortedBy("[\"S.:supplier_id\"]")
.clientSortAlgo("CLIENT MERGE SORT").end().rhs()
.abstractExplainPlan("SORT-MERGE-JOIN (INNER)").sortMergeSkipMerge(true)
- .clientSortedBy("[\"I.0:supplier_id\"]").lhs()
- .scanType("RANGE SCAN").table(itemIndex + "(" + item + ")").keyRanges("
[1]")
- .serverSortedBy("[\"I.:item_id\"]").clientSortAlgo("CLIENT MERGE SORT")
- .end().rhs().scanType("FULL SCAN").table(order)
+ .clientSortedBy("[\"I.0:supplier_id\"]").lhs().scanType("RANGE SCAN")
+ .table(itemIndex + "(" + item + ")").keyRanges("
[1]").serverSortedBy("[\"I.:item_id\"]")
+ .clientSortAlgo("CLIENT MERGE SORT").end().rhs().scanType("FULL
SCAN").table(order)
.serverWhereFilter("SERVER FILTER BY QUANTITY <
5000").serverSortedBy("[\"O.item_id\"]")
.clientSortAlgo("CLIENT MERGE SORT").end().end();
}
diff --git
a/phoenix-core/src/test/java/org/apache/phoenix/query/explain/ExplainJsonNormalizer.java
b/phoenix-core/src/test/java/org/apache/phoenix/query/explain/ExplainJsonNormalizer.java
index 38f44fabea..eb96912f95 100644
---
a/phoenix-core/src/test/java/org/apache/phoenix/query/explain/ExplainJsonNormalizer.java
+++
b/phoenix-core/src/test/java/org/apache/phoenix/query/explain/ExplainJsonNormalizer.java
@@ -63,6 +63,9 @@ public final class ExplainJsonNormalizer {
if (obj.has("splitsChunk")) {
obj.set("splitsChunk", NullNode.getInstance());
}
+ if (obj.has("regionsPlanned")) {
+ obj.set("regionsPlanned", NullNode.getInstance());
+ }
if (obj.has("estimatedRows")) {
obj.set("estimatedRows", NullNode.getInstance());
}
diff --git
a/phoenix-core/src/test/java/org/apache/phoenix/query/explain/ExplainPlanTest.java
b/phoenix-core/src/test/java/org/apache/phoenix/query/explain/ExplainPlanTest.java
index 66fe6b8b71..8e57faa750 100644
---
a/phoenix-core/src/test/java/org/apache/phoenix/query/explain/ExplainPlanTest.java
+++
b/phoenix-core/src/test/java/org/apache/phoenix/query/explain/ExplainPlanTest.java
@@ -109,8 +109,8 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
"SELECT a_string, b_string FROM atable"
+ " WHERE organization_id = '00D000000000001' AND entity_id =
'00E00000000001'"
+ " AND x_integer = 2 AND a_integer < 5",
- text("CLIENT PARALLEL <N>-WAY POINT LOOKUP ON 1 KEY OVER ATABLE",
- " SERVER FILTER BY (X_INTEGER = 2 AND A_INTEGER < 5)"),
+ text("CLIENT PARALLEL <N>-WAY POINT LOOKUP ON 1 KEY OVER ATABLE", "
INDEX ATABLE",
+ " REGIONS PLANNED <N>", " SERVER FILTER BY (X_INTEGER = 2 AND
A_INTEGER < 5)"),
scanAttrs("POINT LOOKUP ON 1 KEY ", "ATABLE",
null).put("serverWhereFilter",
"SERVER FILTER BY (X_INTEGER = 2 AND A_INTEGER < 5)"));
}
@@ -121,7 +121,8 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
"SELECT a_string, b_string FROM atable"
+ " WHERE organization_id IN ('00D000000000001', '00D000000000005')"
+ " AND entity_id IN ('00E00000000000X','00E00000000000Z')",
- text("CLIENT PARALLEL <N>-WAY POINT LOOKUP ON 4 KEYS OVER ATABLE"),
+ text("CLIENT PARALLEL <N>-WAY POINT LOOKUP ON 4 KEYS OVER ATABLE", "
INDEX ATABLE",
+ " REGIONS PLANNED <N>"),
scanAttrs("POINT LOOKUP ON 4 KEYS ", "ATABLE", null));
}
@@ -130,8 +131,10 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
verifyQuery("rangeScan",
"SELECT a_string FROM atable WHERE organization_id = '00D000000000001'"
+ " AND entity_id > '00E00000000002' AND entity_id < '00E00000000008'",
- text("CLIENT PARALLEL <N>-WAY RANGE SCAN OVER ATABLE"
- + " ['00D000000000001','00E00000000002!'] -
['00D000000000001','00E00000000008 ']"),
+ text(
+ "CLIENT PARALLEL <N>-WAY RANGE SCAN OVER ATABLE"
+ + " ['00D000000000001','00E00000000002!'] -
['00D000000000001','00E00000000008 ']",
+ " INDEX ATABLE", " REGIONS PLANNED <N>"),
scanAttrs("RANGE SCAN ", "ATABLE",
" ['00D000000000001','00E00000000002!'] -
['00D000000000001','00E00000000008 ']"));
}
@@ -140,7 +143,7 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
public void testSkipScanKeys() throws Exception {
verifyQuery("skipScanKeys", "SELECT host FROM ptsdb3 WHERE host IN
('na1','na2','na3')",
text("CLIENT PARALLEL <N>-WAY SKIP SCAN ON 3 KEYS OVER PTSDB3 [~'na3'] -
[~'na1']",
- " SERVER FILTER BY FIRST KEY ONLY"),
+ " INDEX PTSDB3", " REGIONS PLANNED <N>", " SERVER FILTER BY
FIRST KEY ONLY"),
scanAttrs("SKIP SCAN ON 3 KEYS ", "PTSDB3", " [~'na3'] -
[~'na1']").put("serverWhereFilter",
"SERVER FILTER BY FIRST KEY ONLY"));
}
@@ -154,7 +157,7 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
text(
"CLIENT PARALLEL <N>-WAY SKIP SCAN ON 6 RANGES OVER PTSDB"
+ " ['na1','a','2013-01-01'] - ['na3','b','2013-01-02']",
- " SERVER FILTER BY FIRST KEY ONLY"),
+ " INDEX PTSDB", " REGIONS PLANNED <N>", " SERVER FILTER BY
FIRST KEY ONLY"),
scanAttrs("SKIP SCAN ON 6 RANGES ", "PTSDB",
" ['na1','a','2013-01-01'] -
['na3','b','2013-01-02']").put("serverWhereFilter",
"SERVER FILTER BY FIRST KEY ONLY"));
@@ -163,15 +166,17 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
@Test
public void testFullScan() throws Exception {
verifyQuery("fullScan", "SELECT * FROM atable",
- text("CLIENT PARALLEL <N>-WAY FULL SCAN OVER ATABLE"), scanAttrs("FULL
SCAN ", "ATABLE", ""));
+ text("CLIENT PARALLEL <N>-WAY FULL SCAN OVER ATABLE", " INDEX ATABLE",
+ " REGIONS PLANNED <N>"),
+ scanAttrs("FULL SCAN ", "ATABLE", ""));
}
@Test
public void testReverseScan() throws Exception {
verifyQuery("reverseScan",
"SELECT inst,\"DATE\" FROM ptsdb2 WHERE inst = 'na1' ORDER BY inst DESC,
\"DATE\" DESC",
- text("CLIENT PARALLEL <N>-WAY REVERSE RANGE SCAN OVER PTSDB2 ['na1']",
- " SERVER FILTER BY FIRST KEY ONLY"),
+ text("CLIENT PARALLEL <N>-WAY REVERSE RANGE SCAN OVER PTSDB2 ['na1']", "
INDEX PTSDB2",
+ " REGIONS PLANNED <N>", " SERVER FILTER BY FIRST KEY ONLY"),
scanAttrs("RANGE SCAN ", "PTSDB2", " ['na1']")
.put("serverWhereFilter", "SERVER FILTER BY FIRST KEY ONLY")
.put("clientSortedBy", "REVERSE"));
@@ -182,7 +187,7 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
verifyQuery("smallHint",
"SELECT /*+ SMALL */ host FROM ptsdb3 WHERE host IN ('na1','na2','na3')",
text("CLIENT PARALLEL <N>-WAY SMALL SKIP SCAN ON 3 KEYS OVER PTSDB3
[~'na3'] - [~'na1']",
- " SERVER FILTER BY FIRST KEY ONLY"),
+ " INDEX PTSDB3", " REGIONS PLANNED <N>", " SERVER FILTER BY
FIRST KEY ONLY"),
scanAttrs("SKIP SCAN ON 3 KEYS ", "PTSDB3", " [~'na3'] -
[~'na1']").put("hint", "SMALL")
.put("serverWhereFilter", "SERVER FILTER BY FIRST KEY ONLY"));
}
@@ -190,7 +195,8 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
@Test
public void testAggregateSingleRow() throws Exception {
verifyQuery("aggregateSingleRow", "SELECT count(*) FROM atable",
- text("CLIENT PARALLEL <N>-WAY FULL SCAN OVER ATABLE", " SERVER FILTER
BY FIRST KEY ONLY",
+ text("CLIENT PARALLEL <N>-WAY FULL SCAN OVER ATABLE", " INDEX ATABLE",
+ " REGIONS PLANNED <N>", " SERVER FILTER BY FIRST KEY ONLY",
" SERVER AGGREGATE INTO SINGLE ROW"),
scanAttrs("FULL SCAN ", "ATABLE", "")
.put("serverWhereFilter", "SERVER FILTER BY FIRST KEY ONLY")
@@ -200,8 +206,9 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
@Test
public void testAggregateOrderedDistinct() throws Exception {
verifyQuery("aggregateOrderedDistinct", "SELECT count(1) FROM atable GROUP
BY a_string",
- text("CLIENT PARALLEL <N>-WAY FULL SCAN OVER ATABLE",
- " SERVER AGGREGATE INTO DISTINCT ROWS BY [A_STRING]", "CLIENT MERGE
SORT"),
+ text("CLIENT PARALLEL <N>-WAY FULL SCAN OVER ATABLE", " INDEX ATABLE",
+ " REGIONS PLANNED <N>", " SERVER AGGREGATE INTO DISTINCT ROWS BY
[A_STRING]",
+ "CLIENT MERGE SORT"),
scanAttrs("FULL SCAN ", "ATABLE", "")
.put("serverAggregate", "SERVER AGGREGATE INTO DISTINCT ROWS BY
[A_STRING]")
.put("clientSortAlgo", "CLIENT MERGE SORT")
@@ -213,7 +220,8 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
verifyQuery("aggregateHashDistinct",
"SELECT count(1) FROM atable WHERE a_integer = 1"
+ " GROUP BY ROUND(a_time,'HOUR',2), entity_id",
- text("CLIENT PARALLEL <N>-WAY FULL SCAN OVER ATABLE", " SERVER FILTER
BY A_INTEGER = 1",
+ text("CLIENT PARALLEL <N>-WAY FULL SCAN OVER ATABLE", " INDEX ATABLE",
+ " REGIONS PLANNED <N>", " SERVER FILTER BY A_INTEGER = 1",
" SERVER AGGREGATE INTO DISTINCT ROWS BY [ENTITY_ID,
ROUND(A_TIME)]",
"CLIENT MERGE SORT"),
scanAttrs("FULL SCAN ", "ATABLE", "")
@@ -226,8 +234,9 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
@Test
public void testTopNSortedBy() throws Exception {
verifyQuery("topNSortedBy", "SELECT a_string FROM atable ORDER BY a_string
DESC LIMIT 3",
- text("CLIENT PARALLEL <N>-WAY FULL SCAN OVER ATABLE",
- " SERVER TOP 3 ROWS SORTED BY [A_STRING DESC]", "CLIENT MERGE
SORT", "CLIENT LIMIT 3"),
+ text("CLIENT PARALLEL <N>-WAY FULL SCAN OVER ATABLE", " INDEX ATABLE",
+ " REGIONS PLANNED <N>", " SERVER TOP 3 ROWS SORTED BY [A_STRING
DESC]",
+ "CLIENT MERGE SORT", "CLIENT LIMIT 3"),
scanAttrs("FULL SCAN ", "ATABLE", "").put("serverSortedBy", "[A_STRING
DESC]")
.put("serverRowLimit", 3).put("clientRowLimit", 3)
.put("clientSortAlgo", "CLIENT MERGE SORT")
@@ -238,7 +247,8 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
public void testClientFilterByMax() throws Exception {
verifyQuery("clientFilterByMax",
"SELECT count(1) FROM atable GROUP BY a_string, b_string HAVING
max(a_string) = 'a'",
- text("CLIENT PARALLEL <N>-WAY FULL SCAN OVER ATABLE",
+ text("CLIENT PARALLEL <N>-WAY FULL SCAN OVER ATABLE", " INDEX ATABLE",
+ " REGIONS PLANNED <N>",
" SERVER AGGREGATE INTO DISTINCT ROWS BY [A_STRING, B_STRING]",
"CLIENT MERGE SORT",
"CLIENT FILTER BY MAX(A_STRING) = 'a'"),
scanAttrs("FULL SCAN ", "ATABLE", "")
@@ -254,7 +264,8 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
"SELECT a_string, b_string FROM atable"
+ " WHERE organization_id = '00D000000000001' AND entity_id !=
'00E00000000002'"
+ " AND x_integer = 2 AND a_integer < 5 LIMIT 10",
- text("CLIENT PARALLEL <N>-WAY RANGE SCAN OVER ATABLE
['00D000000000001']",
+ text("CLIENT PARALLEL <N>-WAY RANGE SCAN OVER ATABLE
['00D000000000001']", " INDEX ATABLE",
+ " REGIONS PLANNED <N>",
" SERVER FILTER BY (ENTITY_ID != '00E00000000002' AND X_INTEGER = 2
AND A_INTEGER < 5)",
" SERVER 10 ROW LIMIT", "CLIENT 10 ROW LIMIT"),
scanAttrs("RANGE SCAN ", "ATABLE", " ['00D000000000001']")
@@ -267,8 +278,8 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
@Test
public void testArrayElementProjection() throws Exception {
verifyQuery("arrayElementProjection", "SELECT a_string_array[1] FROM
table_with_array",
- text("CLIENT PARALLEL <N>-WAY FULL SCAN OVER TABLE_WITH_ARRAY",
- " SERVER ARRAY ELEMENT PROJECTION"),
+ text("CLIENT PARALLEL <N>-WAY FULL SCAN OVER TABLE_WITH_ARRAY", "
INDEX TABLE_WITH_ARRAY",
+ " REGIONS PLANNED <N>", " SERVER ARRAY ELEMENT PROJECTION"),
scanAttrs("FULL SCAN ", "TABLE_WITH_ARRAY",
"").put("serverArrayElementProjection", true));
}
@@ -278,8 +289,10 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
"SELECT a_string,b_string FROM atable WHERE organization_id =
'000000000000001'"
+ " AND entity_id > '000000000000002' AND entity_id <
'000000000000008'"
+ " AND (organization_id,entity_id) <=
('000000000000001','000000000000005')",
- text("CLIENT PARALLEL <N>-WAY RANGE SCAN OVER ATABLE"
- + " ['000000000000001','000000000000003'] -
['000000000000001','000000000000005']"),
+ text(
+ "CLIENT PARALLEL <N>-WAY RANGE SCAN OVER ATABLE"
+ + " ['000000000000001','000000000000003'] -
['000000000000001','000000000000005']",
+ " INDEX ATABLE", " REGIONS PLANNED <N>"),
scanAttrs("RANGE SCAN ", "ATABLE",
" ['000000000000001','000000000000003'] -
['000000000000001','000000000000005']"));
}
@@ -293,6 +306,7 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
text(
"CLIENT PARALLEL <N>-WAY RANGE SCAN OVER ATABLE"
+ " ['000000000000003000000000000005'] - [*]",
+ " INDEX ATABLE", " REGIONS PLANNED <N>",
" SERVER FILTER BY (ENTITY_ID > '000000000000002' AND ENTITY_ID <
'000000000000008')"),
scanAttrs("RANGE SCAN ", "ATABLE", " ['000000000000003000000000000005']
- [*]").put(
"serverWhereFilter",
@@ -304,7 +318,8 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
verifyQuery("rangeScanNullNotNull",
"SELECT host FROM PTSDB WHERE inst IS NULL AND host IS NOT NULL"
+ " AND \"DATE\" >= to_date('2013-01-01')",
- text("CLIENT PARALLEL <N>-WAY RANGE SCAN OVER PTSDB [null,not null]",
+ text("CLIENT PARALLEL <N>-WAY RANGE SCAN OVER PTSDB [null,not null]", "
INDEX PTSDB",
+ " REGIONS PLANNED <N>",
" SERVER FILTER BY FIRST KEY ONLY AND \"DATE\" >= DATE '2013-01-01
00:00:00.000'"),
scanAttrs("RANGE SCAN ", "PTSDB", " [null,not
null]").put("serverWhereFilter",
"SERVER FILTER BY FIRST KEY ONLY AND \"DATE\" >= DATE '2013-01-01
00:00:00.000'"));
@@ -315,7 +330,8 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
verifyQuery("rangeScanNotNull",
"SELECT host FROM PTSDB WHERE inst IS NOT NULL AND host IS NULL"
+ " AND \"DATE\" >= to_date('2013-01-01')",
- text("CLIENT PARALLEL <N>-WAY RANGE SCAN OVER PTSDB [not null]",
+ text("CLIENT PARALLEL <N>-WAY RANGE SCAN OVER PTSDB [not null]", "
INDEX PTSDB",
+ " REGIONS PLANNED <N>",
" SERVER FILTER BY FIRST KEY ONLY AND (HOST IS NULL"
+ " AND \"DATE\" >= DATE '2013-01-01 00:00:00.000')"),
scanAttrs("RANGE SCAN ", "PTSDB", " [not null]").put("serverWhereFilter",
@@ -331,7 +347,7 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
text(
"CLIENT PARALLEL <N>-WAY SKIP SCAN ON 2 RANGES OVER PTSDB"
+ " ['na','a','2013-01-01'] - ['nb','b','2013-01-02']",
- " SERVER FILTER BY FIRST KEY ONLY"),
+ " INDEX PTSDB", " REGIONS PLANNED <N>", " SERVER FILTER BY
FIRST KEY ONLY"),
scanAttrs("SKIP SCAN ON 2 RANGES ", "PTSDB",
" ['na','a','2013-01-01'] -
['nb','b','2013-01-02']").put("serverWhereFilter",
"SERVER FILTER BY FIRST KEY ONLY"));
@@ -342,6 +358,7 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
verifyQuery("skipScanRegexpRanges",
"SELECT inst,host FROM PTSDB WHERE REGEXP_SUBSTR(INST, '[^-]+', 1) IN
('na1', 'na2','na3')",
text("CLIENT PARALLEL <N>-WAY SKIP SCAN ON 3 RANGES OVER PTSDB ['na1'] -
['na4']",
+ " INDEX PTSDB", " REGIONS PLANNED <N>",
" SERVER FILTER BY FIRST KEY ONLY AND"
+ " REGEXP_SUBSTR(INST, '[^-]+', 1) IN ('na1','na2','na3')"),
scanAttrs("SKIP SCAN ON 3 RANGES ", "PTSDB", " ['na1'] -
['na4']").put("serverWhereFilter",
@@ -353,8 +370,10 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
verifyQuery("rangeScanSubstrBounds",
"SELECT a_string FROM atable WHERE organization_id='000000000000001'"
+ " AND SUBSTR(entity_id,1,3) > '002' AND SUBSTR(entity_id,1,3) <=
'003'",
- text("CLIENT PARALLEL <N>-WAY RANGE SCAN OVER ATABLE"
- + " ['000000000000001','003 '] - ['000000000000001','004
']"),
+ text(
+ "CLIENT PARALLEL <N>-WAY RANGE SCAN OVER ATABLE"
+ + " ['000000000000001','003 '] - ['000000000000001','004
']",
+ " INDEX ATABLE", " REGIONS PLANNED <N>"),
scanAttrs("RANGE SCAN ", "ATABLE",
" ['000000000000001','003 '] - ['000000000000001','004
']"));
}
@@ -364,17 +383,19 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
verifyQuery("skipScanTwoKeys",
"SELECT a_string,b_string FROM atable"
+ " WHERE organization_id IN ('000000000000001', '000000000000005')",
- text("CLIENT PARALLEL <N>-WAY SKIP SCAN ON 2 KEYS OVER ATABLE"
- + " ['000000000000001'] - ['000000000000005']"),
+ text(
+ "CLIENT PARALLEL <N>-WAY SKIP SCAN ON 2 KEYS OVER ATABLE"
+ + " ['000000000000001'] - ['000000000000005']",
+ " INDEX ATABLE", " REGIONS PLANNED <N>"),
scanAttrs("SKIP SCAN ON 2 KEYS ", "ATABLE", " ['000000000000001'] -
['000000000000005']"));
}
@Test
public void testGroupByClientLimit() throws Exception {
verifyQuery("groupByClientLimit", "SELECT count(1) FROM atable GROUP BY
a_string LIMIT 5",
- text("CLIENT PARALLEL <N>-WAY FULL SCAN OVER ATABLE",
- " SERVER AGGREGATE INTO DISTINCT ROWS BY [A_STRING]", "CLIENT MERGE
SORT",
- "CLIENT 5 ROW LIMIT"),
+ text("CLIENT PARALLEL <N>-WAY FULL SCAN OVER ATABLE", " INDEX ATABLE",
+ " REGIONS PLANNED <N>", " SERVER AGGREGATE INTO DISTINCT ROWS BY
[A_STRING]",
+ "CLIENT MERGE SORT", "CLIENT 5 ROW LIMIT"),
scanAttrs("FULL SCAN ", "ATABLE", "")
.put("serverAggregate", "SERVER AGGREGATE INTO DISTINCT ROWS BY
[A_STRING]")
.put("clientRowLimit", 5).put("clientSortAlgo", "CLIENT MERGE SORT")
@@ -386,8 +407,9 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
verifyQuery("topNAscNullsFirstLimit",
"SELECT a_string,b_string FROM atable WHERE organization_id =
'000000000000001'"
+ " ORDER BY a_string ASC NULLS FIRST LIMIT 10",
- text("CLIENT PARALLEL <N>-WAY RANGE SCAN OVER ATABLE
['000000000000001']",
- " SERVER TOP 10 ROWS SORTED BY [A_STRING]", "CLIENT MERGE SORT",
"CLIENT LIMIT 10"),
+ text("CLIENT PARALLEL <N>-WAY RANGE SCAN OVER ATABLE
['000000000000001']", " INDEX ATABLE",
+ " REGIONS PLANNED <N>", " SERVER TOP 10 ROWS SORTED BY
[A_STRING]",
+ "CLIENT MERGE SORT", "CLIENT LIMIT 10"),
scanAttrs("RANGE SCAN ", "ATABLE", "
['000000000000001']").put("serverSortedBy", "[A_STRING]")
.put("serverRowLimit", 10).put("clientRowLimit", 10)
.put("clientSortAlgo", "CLIENT MERGE SORT")
@@ -399,9 +421,9 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
verifyQuery("topNDescNullsLastLimit",
"SELECT a_string,b_string FROM atable WHERE organization_id =
'000000000000001'"
+ " ORDER BY a_string DESC NULLS LAST LIMIT 10",
- text("CLIENT PARALLEL <N>-WAY RANGE SCAN OVER ATABLE
['000000000000001']",
- " SERVER TOP 10 ROWS SORTED BY [A_STRING DESC NULLS LAST]", "CLIENT
MERGE SORT",
- "CLIENT LIMIT 10"),
+ text("CLIENT PARALLEL <N>-WAY RANGE SCAN OVER ATABLE
['000000000000001']", " INDEX ATABLE",
+ " REGIONS PLANNED <N>", " SERVER TOP 10 ROWS SORTED BY [A_STRING
DESC NULLS LAST]",
+ "CLIENT MERGE SORT", "CLIENT LIMIT 10"),
scanAttrs("RANGE SCAN ", "ATABLE", " ['000000000000001']")
.put("serverSortedBy", "[A_STRING DESC NULLS
LAST]").put("serverRowLimit", 10)
.put("clientRowLimit", 10).put("clientSortAlgo", "CLIENT MERGE SORT")
@@ -414,7 +436,8 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
"SELECT max(a_integer) FROM atable WHERE organization_id =
'000000000000001'"
+ " GROUP BY organization_id,entity_id,ROUND(a_date,'HOUR')"
+ " ORDER BY entity_id NULLS LAST LIMIT 10",
- text("CLIENT PARALLEL <N>-WAY RANGE SCAN OVER ATABLE
['000000000000001']",
+ text("CLIENT PARALLEL <N>-WAY RANGE SCAN OVER ATABLE
['000000000000001']", " INDEX ATABLE",
+ " REGIONS PLANNED <N>",
" SERVER AGGREGATE INTO DISTINCT ROWS BY"
+ " [ORGANIZATION_ID, ENTITY_ID, ROUND(A_DATE)]",
"CLIENT MERGE SORT", "CLIENT 10 ROW LIMIT"),
@@ -430,7 +453,8 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
verifyQuery("clientSortedByHaving",
"SELECT count(1) FROM atable WHERE a_integer = 1 GROUP BY
a_string,b_string"
+ " HAVING max(a_string) = 'a' ORDER BY b_string",
- text("CLIENT PARALLEL <N>-WAY FULL SCAN OVER ATABLE", " SERVER FILTER
BY A_INTEGER = 1",
+ text("CLIENT PARALLEL <N>-WAY FULL SCAN OVER ATABLE", " INDEX ATABLE",
+ " REGIONS PLANNED <N>", " SERVER FILTER BY A_INTEGER = 1",
" SERVER AGGREGATE INTO DISTINCT ROWS BY [A_STRING, B_STRING]",
"CLIENT MERGE SORT",
"CLIENT FILTER BY MAX(A_STRING) = 'a'", "CLIENT SORTED BY [B_STRING]"),
scanAttrs("FULL SCAN ", "ATABLE", "")
@@ -457,8 +481,10 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
+ " JOIN atable b ON a.organization_id = b.organization_id"
+ " WHERE a.organization_id = '00D000000000001'",
text("SORT-MERGE-JOIN (INNER) TABLES",
- " CLIENT PARALLEL <N>-WAY RANGE SCAN OVER ATABLE
['00D000000000001']", "AND",
- " CLIENT PARALLEL <N>-WAY FULL SCAN OVER ATABLE"),
+ " CLIENT PARALLEL <N>-WAY RANGE SCAN OVER ATABLE
['00D000000000001']",
+ " INDEX ATABLE", " REGIONS PLANNED <N>", "AND",
+ " CLIENT PARALLEL <N>-WAY FULL SCAN OVER ATABLE", " INDEX
ATABLE",
+ " REGIONS PLANNED <N>"),
expected);
}
@@ -476,8 +502,10 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
"SELECT a.a_string, b.a_string FROM atable a"
+ " JOIN atable b ON a.organization_id = b.organization_id"
+ " WHERE a.organization_id = '00D000000000001'",
- text("CLIENT PARALLEL <N>-WAY RANGE SCAN OVER ATABLE
['00D000000000001']",
- " PARALLEL INNER-JOIN TABLE 0", " CLIENT PARALLEL <N>-WAY
FULL SCAN OVER ATABLE",
+ text("CLIENT PARALLEL <N>-WAY RANGE SCAN OVER ATABLE
['00D000000000001']", " INDEX ATABLE",
+ " REGIONS PLANNED <N>", " PARALLEL INNER-JOIN TABLE 0",
+ " CLIENT PARALLEL <N>-WAY FULL SCAN OVER ATABLE", "
INDEX ATABLE",
+ " REGIONS PLANNED <N>",
" DYNAMIC SERVER FILTER BY A.ORGANIZATION_ID IN
(B.ORGANIZATION_ID)"),
expected);
}
@@ -497,9 +525,10 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
verifyQuery("hashJoinSemiInSubquery",
"SELECT a_string FROM atable"
+ " WHERE organization_id IN (SELECT organization_id FROM atable WHERE
a_integer = 1)",
- text("CLIENT PARALLEL <N>-WAY FULL SCAN OVER ATABLE", "
SKIP-SCAN-JOIN TABLE 0",
- " CLIENT PARALLEL <N>-WAY FULL SCAN OVER ATABLE",
- " SERVER FILTER BY A_INTEGER = 1",
+ text("CLIENT PARALLEL <N>-WAY FULL SCAN OVER ATABLE", " INDEX ATABLE",
+ " REGIONS PLANNED <N>", " SKIP-SCAN-JOIN TABLE 0",
+ " CLIENT PARALLEL <N>-WAY FULL SCAN OVER ATABLE", "
INDEX ATABLE",
+ " REGIONS PLANNED <N>", " SERVER FILTER BY
A_INTEGER = 1",
" SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY
[ORGANIZATION_ID]",
" DYNAMIC SERVER FILTER BY ATABLE.ORGANIZATION_ID IN ($1.$2)"),
expected);
@@ -513,7 +542,9 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
+ " SELECT a_string FROM atable WHERE organization_id =
'00D000000000002'",
text("UNION ALL OVER 2 QUERIES",
" CLIENT PARALLEL <N>-WAY RANGE SCAN OVER ATABLE
['00D000000000001']",
- " CLIENT PARALLEL <N>-WAY RANGE SCAN OVER ATABLE
['00D000000000002']"),
+ " INDEX ATABLE", " REGIONS PLANNED <N>",
+ " CLIENT PARALLEL <N>-WAY RANGE SCAN OVER ATABLE
['00D000000000002']",
+ " INDEX ATABLE", " REGIONS PLANNED <N>"),
scanAttrs("RANGE SCAN ", "ATABLE", " ['00D000000000001']")
.put("abstractExplainPlan", "UNION ALL OVER 2 QUERIES")
.set("rhsJoinQueryExplainPlan", rhs));
@@ -534,7 +565,8 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
+ " SELECT organization_id, entity_id, a_string FROM atable"
+ " WHERE organization_id = '00D000000000001'",
false,
- text("UPSERT SELECT", "CLIENT PARALLEL <N>-WAY RANGE SCAN OVER ATABLE
['00D000000000001']"),
+ text("UPSERT SELECT", "CLIENT PARALLEL <N>-WAY RANGE SCAN OVER ATABLE
['00D000000000001']",
+ " INDEX ATABLE", " REGIONS PLANNED <N>"),
scanAttrs("RANGE SCAN ", "ATABLE", "
['00D000000000001']").put("abstractExplainPlan",
"UPSERT SELECT"));
}
@@ -546,7 +578,8 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
+ " SELECT organization_id, entity_id, a_string FROM atable"
+ " WHERE organization_id = '00D000000000001'",
true,
- text("UPSERT ROWS", "CLIENT PARALLEL <N>-WAY RANGE SCAN OVER ATABLE
['00D000000000001']"),
+ text("UPSERT ROWS", "CLIENT PARALLEL <N>-WAY RANGE SCAN OVER ATABLE
['00D000000000001']",
+ " INDEX ATABLE", " REGIONS PLANNED <N>"),
scanAttrs("RANGE SCAN ", "ATABLE", "
['00D000000000001']").put("abstractExplainPlan",
"UPSERT ROWS"));
}
@@ -564,6 +597,7 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
public void testDeleteServer() throws Exception {
verifyMutation("deleteServer", "DELETE FROM atable WHERE entity_id =
'abc'", true,
text("DELETE ROWS SERVER SELECT", "CLIENT PARALLEL <N>-WAY FULL SCAN
OVER ATABLE",
+ " INDEX ATABLE", " REGIONS PLANNED <N>",
" SERVER FILTER BY FIRST KEY ONLY AND ENTITY_ID = 'abc'"),
scanAttrs("FULL SCAN ", "ATABLE", "").put("abstractExplainPlan", "DELETE
ROWS SERVER SELECT")
.put("serverWhereFilter", "SERVER FILTER BY FIRST KEY ONLY AND
ENTITY_ID = 'abc'"));
@@ -573,6 +607,7 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
public void testDeleteClient() throws Exception {
verifyMutation("deleteClient", "DELETE FROM atable WHERE entity_id =
'abc'", false,
text("DELETE ROWS CLIENT SELECT", "CLIENT PARALLEL <N>-WAY FULL SCAN
OVER ATABLE",
+ " INDEX ATABLE", " REGIONS PLANNED <N>",
" SERVER FILTER BY FIRST KEY ONLY AND ENTITY_ID = 'abc'"),
scanAttrs("FULL SCAN ", "ATABLE", "").put("abstractExplainPlan", "DELETE
ROWS CLIENT SELECT")
.put("serverWhereFilter", "SERVER FILTER BY FIRST KEY ONLY AND
ENTITY_ID = 'abc'"));
@@ -581,7 +616,8 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
@Test
public void testSequenceNextValue() throws Exception {
verifyQuery("sequenceNextValue", "SELECT NEXT VALUE FOR " + SEQ + " FROM
atable",
- text("CLIENT PARALLEL <N>-WAY FULL SCAN OVER ATABLE", " SERVER FILTER
BY FIRST KEY ONLY",
+ text("CLIENT PARALLEL <N>-WAY FULL SCAN OVER ATABLE", " INDEX ATABLE",
+ " REGIONS PLANNED <N>", " SERVER FILTER BY FIRST KEY ONLY",
"CLIENT RESERVE VALUES FROM 1 SEQUENCE"),
scanAttrs("FULL SCAN ", "ATABLE", "")
.put("serverWhereFilter", "SERVER FILTER BY FIRST KEY
ONLY").put("clientSequenceCount", 1)
@@ -591,9 +627,11 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
@Test
public void testSaltedTableScan() throws Exception {
verifyQuery("saltedTableScan", "SELECT * FROM " + SALTED + " WHERE v = 7",
- text("CLIENT PARALLEL <N>-WAY FULL SCAN OVER EO_SALTED", " SERVER
FILTER BY V = 7",
+ text("CLIENT PARALLEL <N>-WAY FULL SCAN OVER EO_SALTED", " INDEX
EO_SALTED",
+ " SALT BUCKETS 4", " REGIONS PLANNED <N>", " SERVER FILTER BY
V = 7",
"CLIENT MERGE SORT"),
- scanAttrs("FULL SCAN ", "EO_SALTED", "").put("serverWhereFilter",
"SERVER FILTER BY V = 7")
+ scanAttrs("FULL SCAN ", "EO_SALTED", "").put("saltBuckets", 4)
+ .put("serverWhereFilter", "SERVER FILTER BY V = 7")
.put("clientSortAlgo", "CLIENT MERGE SORT")
.set("clientSteps", clientSteps("CLIENT MERGE SORT")));
}
@@ -604,12 +642,56 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
tenantProps.setProperty(DATE_FORMAT_ATTRIB, "yyyy-MM-dd");
tenantProps.setProperty(TENANT_ID_ATTRIB, TENANT_ID);
verifyQuery("multiTenantView", "SELECT * FROM " + MT_VIEW + " LIMIT 1",
tenantProps,
- text("CLIENT SERIAL <N>-WAY RANGE SCAN OVER EO_MT_BASE ['tenant42']",
- " SERVER 1 ROW LIMIT", "CLIENT 1 ROW LIMIT"),
+ text("CLIENT SERIAL <N>-WAY RANGE SCAN OVER EO_MT_BASE ['tenant42']", "
INDEX EO_MT_VIEW",
+ " REGIONS PLANNED <N>", " SERVER 1 ROW LIMIT", "CLIENT 1 ROW
LIMIT"),
attrs().put("iteratorTypeAndScanSize", "SERIAL
<N>-WAY").put("consistency", "STRONG")
.put("explainScanType", "RANGE SCAN ").put("tableName", "EO_MT_BASE")
- .put("keyRanges", " ['tenant42']").put("serverRowLimit",
1).put("clientRowLimit", 1)
- .set("clientSteps", clientSteps("CLIENT 1 ROW LIMIT")));
+ .put("indexName", "EO_MT_VIEW").put("keyRanges", "
['tenant42']").put("serverRowLimit", 1)
+ .put("clientRowLimit", 1).set("clientSteps", clientSteps("CLIENT 1 ROW
LIMIT")));
+ }
+
+ @Test
+ public void testIndexKindGlobal() throws Exception {
+ try (Connection conn = DriverManager.getConnection(getUrl(),
defaultProps());
+ java.sql.Statement stmt = conn.createStatement()) {
+ String base = generateUniqueName();
+ String idx = generateUniqueName();
+ stmt.execute("CREATE TABLE " + base + " (k VARCHAR PRIMARY KEY, v1
VARCHAR, v2 VARCHAR)");
+ stmt.execute("CREATE INDEX " + idx + " ON " + base + " (v1) INCLUDE
(v2)");
+ ExplainPlanTestUtil.assertPlan(conn, "SELECT v1, v2 FROM " + base + "
WHERE v1 = 'x'")
+ .table(idx).indexName(idx).indexKind("GLOBAL");
+ }
+ }
+
+ @Test
+ public void testIndexKindLocal() throws Exception {
+ try (Connection conn = DriverManager.getConnection(getUrl(),
defaultProps());
+ java.sql.Statement stmt = conn.createStatement()) {
+ String base = generateUniqueName();
+ String idx = generateUniqueName();
+ stmt.execute("CREATE TABLE " + base + " (k VARCHAR PRIMARY KEY, v1
VARCHAR, v2 VARCHAR)");
+ stmt.execute("CREATE LOCAL INDEX " + idx + " ON " + base + " (v1)");
+ // The OVER line decorates a local index as <idx>(<phys>); the INDEX
line prints just <idx>.
+ ExplainPlanTestUtil
+ .assertPlan(conn,
+ "SELECT /*+ INDEX(" + base + " " + idx + ") */ k, v1 FROM " + base +
" WHERE v1 = 'x'")
+ .indexName(idx).indexKind("LOCAL");
+ }
+ }
+
+ @Test
+ public void testIndexKindUncoveredGlobal() throws Exception {
+ try (Connection conn = DriverManager.getConnection(getUrl(),
defaultProps());
+ java.sql.Statement stmt = conn.createStatement()) {
+ String base = generateUniqueName();
+ String idx = generateUniqueName();
+ stmt.execute("CREATE TABLE " + base + " (k VARCHAR PRIMARY KEY, v1
VARCHAR, v2 VARCHAR)");
+ stmt.execute("CREATE UNCOVERED INDEX " + idx + " ON " + base + " (v1)");
+ ExplainPlanTestUtil
+ .assertPlan(conn,
+ "SELECT /*+ INDEX(" + base + " " + idx + ") */ k, v2 FROM " + base +
" WHERE v1 = 'x'")
+ .indexName(idx).indexKind("UNCOVERED GLOBAL");
+ }
}
@Test
@@ -908,6 +990,10 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
n.putNull("explainScanType");
n.putNull("tableName");
n.putNull("keyRanges");
+ n.putNull("indexName");
+ n.putNull("indexKind");
+ n.putNull("saltBuckets");
+ n.putNull("regionsPlanned");
n.putNull("scanTimeRangeMin");
n.putNull("scanTimeRangeMax");
n.putNull("serverWhereFilter");
@@ -957,6 +1043,9 @@ public class ExplainPlanTest extends
BaseConnectionlessQueryTest {
n.put("consistency", "STRONG");
n.put("explainScanType", scanType);
n.put("tableName", table);
+ // For a data table scan the per scan INDEX line names the same entity as
tableName. View and
+ // index scans that diverge override indexName on the returned node.
+ n.put("indexName", table);
if (keys != null) {
n.put("keyRanges", keys);
}
diff --git
a/phoenix-core/src/test/java/org/apache/phoenix/query/explain/ExplainPlanTestUtil.java
b/phoenix-core/src/test/java/org/apache/phoenix/query/explain/ExplainPlanTestUtil.java
index 7c55ecbafc..2f44a07e58 100644
---
a/phoenix-core/src/test/java/org/apache/phoenix/query/explain/ExplainPlanTestUtil.java
+++
b/phoenix-core/src/test/java/org/apache/phoenix/query/explain/ExplainPlanTestUtil.java
@@ -175,6 +175,33 @@ public final class ExplainPlanTestUtil {
return this;
}
+ /** Assert the chosen per-scan index (or data table) name. */
+ public ExplainPlanAssert indexName(String expected) {
+ assertEquals(at("indexName"), expected, attributes.getIndexName());
+ return this;
+ }
+
+ /**
+ * Assert the per-scan index kind token: {@code "LOCAL"}, {@code
"GLOBAL"}, or
+ * {@code "UNCOVERED GLOBAL"} (null for a data-table target).
+ */
+ public ExplainPlanAssert indexKind(String expected) {
+ assertEquals(at("indexKind"), expected, attributes.getIndexKind());
+ return this;
+ }
+
+ /** Assert the salt bucket count of the scanned table (null when not
salted). */
+ public ExplainPlanAssert saltBuckets(Integer expected) {
+ assertEquals(at("saltBuckets"), expected, attributes.getSaltBuckets());
+ return this;
+ }
+
+ /** Assert the number of regions the scan is planned to hit (null when
unknown). */
+ public ExplainPlanAssert regionsPlanned(Integer expected) {
+ assertEquals(at("regionsPlanned"), expected,
attributes.getRegionsPlanned());
+ return this;
+ }
+
/** Assert the hex-string row-value-constructor offset marker. */
public ExplainPlanAssert hexStringRVCOffset(String expected) {
assertEquals(at("hexStringRVCOffset"), expected,
attributes.getHexStringRVCOffset());
diff --git
a/phoenix-core/src/test/java/org/apache/phoenix/query/explain/ExplainTextNormalizer.java
b/phoenix-core/src/test/java/org/apache/phoenix/query/explain/ExplainTextNormalizer.java
index 24c3bee4a9..c8e1d1ef46 100644
---
a/phoenix-core/src/test/java/org/apache/phoenix/query/explain/ExplainTextNormalizer.java
+++
b/phoenix-core/src/test/java/org/apache/phoenix/query/explain/ExplainTextNormalizer.java
@@ -33,9 +33,12 @@ public final class ExplainTextNormalizer {
// Matches the iterator parallelism count.
private static final Pattern WAY_COUNT = Pattern.compile("\\b\\d+-WAY\\b");
- // Matches the stats-row-count gated row count and byte count.
+ // Matches the stats row and byte counts.
private static final Pattern ROWS_BYTES = Pattern.compile("\\d+ ROWS \\d+
BYTES\\s*");
+ // Matches the planned regions count on the REGIONS PLANNED line.
+ private static final Pattern REGIONS_PLANNED = Pattern.compile("REGIONS
PLANNED \\d+");
+
// Matches the region locations line.
private static final String REGION_LOCATIONS_PREFIX = " (region locations =
";
@@ -62,6 +65,7 @@ public final class ExplainTextNormalizer {
normalized = CHUNK_COUNT.matcher(normalized).replaceAll("<N>-CHUNK");
normalized = WAY_COUNT.matcher(normalized).replaceAll("<N>-WAY");
normalized = ROWS_BYTES.matcher(normalized).replaceAll("");
+ normalized = REGIONS_PLANNED.matcher(normalized).replaceAll("REGIONS
PLANNED <N>");
normalized = aliases.rewrite(normalized);
out.add(normalized);
}