You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@phoenix.apache.org by ja...@apache.org on 2014/02/04 11:01:13 UTC
[1/2] git commit: Fix for optimizer not choosing point lookup and
optimized out order
Updated Branches:
refs/heads/master d40c5066e -> bc21d8e41
Fix for optimizer not choosing point lookup and optimized out order
Project: http://git-wip-us.apache.org/repos/asf/incubator-phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-phoenix/commit/0b469d47
Tree: http://git-wip-us.apache.org/repos/asf/incubator-phoenix/tree/0b469d47
Diff: http://git-wip-us.apache.org/repos/asf/incubator-phoenix/diff/0b469d47
Branch: refs/heads/master
Commit: 0b469d47e76fe62ecc60592dcb8340803d79888e
Parents: fc96ccb
Author: James Taylor <ja...@apache.org>
Authored: Tue Feb 4 02:00:27 2014 -0800
Committer: James Taylor <ja...@apache.org>
Committed: Tue Feb 4 02:00:27 2014 -0800
----------------------------------------------------------------------
.../apache/phoenix/compile/DeleteCompiler.java | 18 +++--
.../org/apache/phoenix/compile/ScanRanges.java | 76 ++++++++++++++++----
.../phoenix/compile/StatementContext.java | 6 --
.../apache/phoenix/compile/WhereOptimizer.java | 45 +++++-------
.../iterate/MergeSortTopNResultIterator.java | 3 +-
.../apache/phoenix/optimize/QueryOptimizer.java | 52 +++++++++-----
.../org/apache/phoenix/schema/SaltingUtil.java | 45 +-----------
.../java/org/apache/phoenix/util/ScanUtil.java | 10 ++-
.../phoenix/compile/QueryOptimizerTest.java | 31 ++++++--
.../phoenix/compile/WhereClauseCompileTest.java | 14 ++--
.../compile/WhereClauseOptimizerTest.java | 8 +--
.../end2end/index/ImmutableIndexTest.java | 36 ++++------
.../end2end/index/MutableSaltedIndexTest.java | 30 ++++----
13 files changed, 197 insertions(+), 177 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/0b469d47/phoenix-core/src/main/java/org/apache/phoenix/compile/DeleteCompiler.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/DeleteCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/DeleteCompiler.java
index a73c111..b71a836 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/DeleteCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/DeleteCompiler.java
@@ -29,6 +29,7 @@ import java.util.Map;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.client.Scan;
+import org.apache.hadoop.hbase.filter.FilterList;
import org.apache.hadoop.hbase.index.util.ImmutableBytesPtr;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.phoenix.cache.ServerCacheClient.ServerCache;
@@ -176,7 +177,7 @@ public class DeleteCompiler {
final ConnectionQueryServices services = connection.getQueryServices();
final ColumnResolver resolver = FromCompiler.getResolver(delete, connection);
final TableRef tableRef = resolver.getTables().get(0);
- PTable table = tableRef.getTable();
+ final PTable table = tableRef.getTable();
if (table.getType() == PTableType.VIEW && table.getViewType().isReadOnly()) {
throw new ReadOnlyTableException(table.getSchemaName().getString(),table.getTableName().getString());
}
@@ -217,11 +218,11 @@ public class DeleteCompiler {
}
final StatementContext context = plan.getContext();
- // If we're doing a query for a single row with no where clause, then we don't need to contact the server at all.
+ // If we're doing a query for a set of rows with no where clause, then we don't need to contact the server at all.
// A simple check of the none existence of a where clause in the parse node is not sufficient, as the where clause
- // may have been optimized out.
- if (noQueryReqd && context.isSingleRowScan()) {
- final ImmutableBytesPtr key = new ImmutableBytesPtr(context.getScan().getStartRow());
+ // may have been optimized out. Instead, we check that there's a single filter which must be the SkipScanFilter
+ // in this case.
+ if (noQueryReqd && ! (context.getScan().getFilter() instanceof FilterList) && context.getScanRanges().isPointLookup()) {
return new MutationPlan() {
@Override
@@ -231,8 +232,11 @@ public class DeleteCompiler {
@Override
public MutationState execute() {
- Map<ImmutableBytesPtr,Map<PColumn,byte[]>> mutation = Maps.newHashMapWithExpectedSize(1);
- mutation.put(key, PRow.DELETE_MARKER);
+ List<byte[]> keys = context.getScanRanges().getPointKeys(table.getBucketNum());
+ Map<ImmutableBytesPtr,Map<PColumn,byte[]>> mutation = Maps.newHashMapWithExpectedSize(keys.size());
+ for (byte[] key : keys) {
+ mutation.put(new ImmutableBytesPtr(key), PRow.DELETE_MARKER);
+ }
return new MutationState(tableRef, mutation, 0, maxSize, connection);
}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/0b469d47/phoenix-core/src/main/java/org/apache/phoenix/compile/ScanRanges.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/ScanRanges.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/ScanRanges.java
index 44f4473..73edaf8 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/ScanRanges.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/ScanRanges.java
@@ -19,18 +19,22 @@
*/
package org.apache.phoenix.compile;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
import org.apache.phoenix.filter.SkipScanFilter;
import org.apache.phoenix.query.KeyRange;
+import org.apache.phoenix.query.KeyRange.Bound;
import org.apache.phoenix.schema.RowKeySchema;
+import org.apache.phoenix.schema.SaltingUtil;
import org.apache.phoenix.util.ScanUtil;
+import org.apache.phoenix.util.SchemaUtil;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
public class ScanRanges {
@@ -115,21 +119,67 @@ public class ScanRanges {
return false;
}
- /**
- * @return true if this represents the full key to a single row
- */
- public boolean isSingleRowScan() {
- if (schema == null || ranges.size() < schema.getMaxFields()) {
+ public static boolean isPointLookup(RowKeySchema schema, List<List<KeyRange>> ranges) {
+ if (ranges.size() < schema.getMaxFields()) {
return false;
}
- boolean isSingleKey = true;
for (List<KeyRange> orRanges : ranges) {
- if (orRanges.size() > 1) {
- return false;
+ for (KeyRange keyRange : orRanges) {
+ if (!keyRange.isSingleKey()) {
+ return false;
+ }
}
- isSingleKey &= orRanges.get(0).isSingleKey();
}
- return isSingleKey;
+ return true;
+ }
+
+
+ private static boolean incrementKey(List<List<KeyRange>> slots, int[] position) {
+ int idx = slots.size() - 1;
+ while (idx >= 0 && (position[idx] = (position[idx] + 1) % slots.get(idx).size()) == 0) {
+ idx--;
+ }
+ return idx >= 0;
+ }
+
+ /**
+ * @return true if this represents a set of complete keys
+ */
+ public List<byte[]> getPointKeys(Integer bucketNum) {
+ return getPointKeys(this.getRanges(), this.getSchema(), bucketNum);
+ }
+
+ public static List<byte[]> getPointKeys(List<List<KeyRange>> ranges, RowKeySchema schema, Integer bucketNum) {
+ if (ranges == null || ranges.isEmpty()) {
+ return Collections.emptyList();
+ }
+ boolean isSalted = bucketNum != null;
+ int count = 1;
+ int offset = isSalted ? 1 : 0;
+ // Skip salt byte range in the first position if salted
+ for (int i = offset; i < ranges.size(); i++) {
+ count *= ranges.get(i).size();
+ }
+ List<byte[]> keys = Lists.newArrayListWithExpectedSize(count);
+ int[] position = new int[ranges.size()];
+ int maxKeyLength = SchemaUtil.getMaxKeyLength(schema, ranges);
+ int length;
+ byte[] key = new byte[maxKeyLength];
+ do {
+ length = ScanUtil.setKey(schema, ranges, position, Bound.LOWER, key, offset, offset, ranges.size(), offset);
+ if (isSalted) {
+ key[0] = SaltingUtil.getSaltingByte(key, offset, length, bucketNum);
+ }
+ keys.add(Arrays.copyOf(key, length + offset));
+ } while (incrementKey(ranges, position));
+ return keys;
+ }
+
+ /**
+ * @return true if this represents a set of complete keys
+ */
+ public boolean isPointLookup() {
+ return schema != null && isPointLookup(schema, ranges);
}
public void setScanStartStopRow(Scan scan) {
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/0b469d47/phoenix-core/src/main/java/org/apache/phoenix/compile/StatementContext.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/StatementContext.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/StatementContext.java
index 5736536..c702985 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/StatementContext.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/StatementContext.java
@@ -24,9 +24,7 @@ import java.text.Format;
import java.util.List;
import org.apache.hadoop.hbase.client.Scan;
-import org.apache.hadoop.hbase.filter.FilterList;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
-
import org.apache.phoenix.jdbc.PhoenixConnection;
import org.apache.phoenix.jdbc.PhoenixStatement;
import org.apache.phoenix.query.KeyRange;
@@ -217,10 +215,6 @@ public class StatementContext {
return minMaxRange;
}
- public boolean isSingleRowScan() {
- return this.getScanRanges().isSingleRowScan() && ! (this.getScan().getFilter() instanceof FilterList);
- }
-
public SequenceManager getSequenceManager(){
return sequences;
}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/0b469d47/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereOptimizer.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereOptimizer.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereOptimizer.java
index 9bfbca3..bc868b1 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereOptimizer.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereOptimizer.java
@@ -32,6 +32,7 @@ import java.util.Set;
import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.hadoop.hbase.util.Bytes;
import org.apache.phoenix.expression.AndExpression;
import org.apache.phoenix.expression.BaseTerminalExpression;
import org.apache.phoenix.expression.CoerceExpression;
@@ -78,6 +79,7 @@ import com.google.common.collect.Lists;
*/
public class WhereOptimizer {
private static final List<KeyRange> SALT_PLACEHOLDER = Collections.singletonList(PDataType.CHAR.getKeyRange(QueryConstants.SEPARATOR_BYTE_ARRAY));
+
private WhereOptimizer() {
}
@@ -204,12 +206,21 @@ public class WhereOptimizer {
// If we have all single keys, we can optimize by adding the salt byte up front
if (schema == SchemaUtil.VAR_BINARY_SCHEMA) {
ranges = SaltingUtil.setSaltByte(ranges, table.getBucketNum());
- } else if (isAllSingleRowScan(cnf, table)) {
- cnf.addFirst(SALT_PLACEHOLDER);
- ranges = SaltingUtil.flattenRanges(cnf, table.getRowKeySchema(), table.getBucketNum());
- schema = SchemaUtil.VAR_BINARY_SCHEMA;
} else {
- cnf.addFirst(SaltingUtil.generateAllSaltingRanges(table.getBucketNum()));
+ List<KeyRange> saltRanges = SALT_PLACEHOLDER;
+ cnf.addFirst(saltRanges);
+ if (ScanRanges.isPointLookup(schema, cnf)) {
+ List<byte[]> keys = ScanRanges.getPointKeys(cnf, schema, table.getBucketNum());
+ Collections.sort(keys, Bytes.BYTES_COMPARATOR);
+ List<KeyRange> keyRanges = Lists.newArrayListWithExpectedSize(keys.size());
+ for (byte[] key : keys) {
+ keyRanges.add(KeyRange.getKeyRange(key));
+ }
+ ranges = Collections.singletonList(keyRanges);
+ schema = SchemaUtil.VAR_BINARY_SCHEMA;
+ } else {
+ cnf.set(0, SaltingUtil.generateAllSaltingRanges(table.getBucketNum()));
+ }
}
}
}
@@ -223,30 +234,6 @@ public class WhereOptimizer {
}
}
- /**
- * Calculate whether or not the list of ranges represents the full primary key
- * of one or more rows
- * @param ranges the list of ranges WITHOUT the salt KeyRange inserted yet
- * @param table
- * @return true if the list of ranges represents the full primary key of one or
- * more ranges and false otherwise.
- */
- private static boolean isAllSingleRowScan(List<List<KeyRange>> ranges, PTable table) {
- RowKeySchema schema = table.getRowKeySchema();
- if (ranges.size() + ( table.getBucketNum() == null ? 0 : 1) < schema.getMaxFields()) {
- return false;
- }
- for (int i = 0; i < ranges.size(); i++) {
- List<KeyRange> orRanges = ranges.get(i);
- for (KeyRange range: orRanges) {
- if (!range.isSingleKey()) {
- return false;
- }
- }
- }
- return true;
- }
-
private static class RemoveExtractedNodesVisitor extends TraverseNoExpressionVisitor<Expression> {
private final Set<Expression> nodesToRemove;
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/0b469d47/phoenix-core/src/main/java/org/apache/phoenix/iterate/MergeSortTopNResultIterator.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/iterate/MergeSortTopNResultIterator.java b/phoenix-core/src/main/java/org/apache/phoenix/iterate/MergeSortTopNResultIterator.java
index 77d1c62..f611fc8 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/iterate/MergeSortTopNResultIterator.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/iterate/MergeSortTopNResultIterator.java
@@ -23,7 +23,6 @@ import java.sql.SQLException;
import java.util.List;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
-
import org.apache.phoenix.expression.Expression;
import org.apache.phoenix.expression.OrderByExpression;
import org.apache.phoenix.schema.tuple.Tuple;
@@ -94,7 +93,7 @@ public class MergeSortTopNResultIterator extends MergeSortResultIterator {
@Override
public void explain(List<String> planSteps) {
resultIterators.explain(planSteps);
- planSteps.add(" SERVER TOP " + limit + " ROW" + (limit == 1 ? "" : "S") + " SORTED BY " + orderByColumns.toString());
+ planSteps.add(" SERVER" + (limit == -1 ? "" : " TOP " + limit + " ROW" + (limit == 1 ? "" : "S")) + " SORTED BY " + orderByColumns.toString());
planSteps.add("CLIENT MERGE SORT");
}
}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/0b469d47/phoenix-core/src/main/java/org/apache/phoenix/optimize/QueryOptimizer.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/optimize/QueryOptimizer.java b/phoenix-core/src/main/java/org/apache/phoenix/optimize/QueryOptimizer.java
index b60f742..7dc6b04 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/optimize/QueryOptimizer.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/optimize/QueryOptimizer.java
@@ -5,7 +5,6 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.List;
-import com.google.common.collect.Lists;
import org.apache.phoenix.compile.ColumnProjector;
import org.apache.phoenix.compile.IndexStatementRewriter;
import org.apache.phoenix.compile.QueryCompiler;
@@ -26,6 +25,8 @@ import org.apache.phoenix.schema.PIndexState;
import org.apache.phoenix.schema.PTable;
import org.apache.phoenix.schema.PTableType;
+import com.google.common.collect.Lists;
+
public class QueryOptimizer {
private static final ParseNodeFactory FACTORY = new ParseNodeFactory();
@@ -170,9 +171,10 @@ public class QueryOptimizer {
/**
* Choose the best plan among all the possible ones.
* Since we don't keep stats yet, we use the following simple algorithm:
- * 1) If the query has an ORDER BY and a LIMIT, choose the plan that has all the ORDER BY expression
+ * 1) If the query is a point lookup (i.e. we have a set of exact row keys), choose among those.
+ * 2) If the query has an ORDER BY and a LIMIT, choose the plan that has all the ORDER BY expression
* in the same order as the row key columns.
- * 2) If there are more than one plan that meets (1), choose the plan with:
+ * 3) If there are more than one plan that meets (1&2), choose the plan with:
* a) the most row key columns that may be used to form the start/stop scan key.
* b) the plan that preserves ordering for a group by.
* c) the data table plan
@@ -185,22 +187,41 @@ public class QueryOptimizer {
return firstPlan;
}
+ /**
+ * If we have a plan(s) that are just point lookups (i.e. fully qualified row
+ * keys), then favor those first.
+ */
List<QueryPlan> candidates = Lists.newArrayListWithExpectedSize(plans.size());
- if (firstPlan.getLimit() == null) {
- candidates.addAll(plans);
- } else {
- for (QueryPlan plan : plans) {
- // If ORDER BY optimized out (or not present at all)
- if (plan.getOrderBy().getOrderByExpressions().isEmpty()) {
- candidates.add(plan);
- }
+ for (QueryPlan plan : plans) {
+ if (plan.getContext().getScanRanges().isPointLookup()) {
+ candidates.add(plan);
}
- if (candidates.isEmpty()) {
- candidates.addAll(plans);
+ }
+ /**
+ * If we have a plan(s) that removes the order by, choose from among these,
+ * as this is typically the most expensive operation. Once we have stats, if
+ * there's a limit on the query, we might choose a different plan. For example
+ * if the limit was a very large number and the combination of applying other
+ * filters on the row key are estimated to choose fewer rows, we'd choose that
+ * one.
+ */
+ List<QueryPlan> stillCandidates = plans;
+ List<QueryPlan> bestCandidates = candidates;
+ if (!candidates.isEmpty()) {
+ stillCandidates = candidates;
+ bestCandidates = Lists.<QueryPlan>newArrayListWithExpectedSize(candidates.size());
+ }
+ for (QueryPlan plan : stillCandidates) {
+ // If ORDER BY optimized out (or not present at all)
+ if (plan.getOrderBy().getOrderByExpressions().isEmpty()) {
+ bestCandidates.add(plan);
}
}
+ if (bestCandidates.isEmpty()) {
+ bestCandidates.addAll(stillCandidates);
+ }
final int comparisonOfDataVersusIndexTable = select.getHint().hasHint(Hint.USE_DATA_OVER_INDEX_TABLE) ? -1 : 1;
- Collections.sort(candidates, new Comparator<QueryPlan>() {
+ Collections.sort(bestCandidates, new Comparator<QueryPlan>() {
@Override
public int compare(QueryPlan plan1, QueryPlan plan2) {
@@ -217,8 +238,7 @@ public class QueryOptimizer {
c = (table1.getColumns().size() - table1.getPKColumns().size()) - (table2.getColumns().size() - table2.getPKColumns().size());
if (c != 0) return c;
- // All things being equal, just use the index table
- // TODO: have hint that drives this
+ // All things being equal, just use the table based on the Hint.USE_DATA_OVER_INDEX_TABLE
if (plan1.getTableRef().getTable().getType() == PTableType.INDEX) {
return comparisonOfDataVersusIndexTable;
}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/0b469d47/phoenix-core/src/main/java/org/apache/phoenix/schema/SaltingUtil.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/SaltingUtil.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/SaltingUtil.java
index 12ab4e7..53a6ac0 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/SaltingUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/SaltingUtil.java
@@ -19,16 +19,12 @@
*/
package org.apache.phoenix.schema;
-import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.phoenix.compile.ScanRanges;
import org.apache.phoenix.query.KeyRange;
-import org.apache.phoenix.query.KeyRange.Bound;
-import org.apache.phoenix.util.ScanUtil;
-import org.apache.phoenix.util.SchemaUtil;
import com.google.common.collect.Lists;
@@ -45,7 +41,7 @@ public class SaltingUtil {
PNameFactory.newName(SALTING_COLUMN_NAME), null, PDataType.BINARY, 1, 0, false, 0, null, 0);
public static List<KeyRange> generateAllSaltingRanges(int bucketNum) {
- List<KeyRange> allRanges = Lists.<KeyRange>newArrayListWithExpectedSize(bucketNum);
+ List<KeyRange> allRanges = Lists.newArrayListWithExpectedSize(bucketNum);
for (int i=0; i<bucketNum; i++) {
byte[] saltByte = new byte[] {(byte) i};
allRanges.add(SALTING_COLUMN.getDataType().getKeyRange(
@@ -114,45 +110,6 @@ public class SaltingUtil {
return Collections.singletonList(newRanges);
}
- public static List<List<KeyRange>> flattenRanges(List<List<KeyRange>> ranges, RowKeySchema schema, int bucketNum) {
- if (ranges == null || ranges.isEmpty()) {
- return ScanRanges.NOTHING.getRanges();
- }
- int count = 1;
- // Skip salt byte range in the first position
- for (int i = 1; i < ranges.size(); i++) {
- count *= ranges.get(i).size();
- }
- KeyRange[] expandedRanges = new KeyRange[count];
- int[] position = new int[ranges.size()];
- int maxKeyLength = SchemaUtil.getMaxKeyLength(schema, ranges);
- int idx = 0, length;
- byte saltByte;
- byte[] key = new byte[maxKeyLength];
- do {
- length = ScanUtil.setKey(schema, ranges, position, Bound.LOWER, key, NUM_SALTING_BYTES, 1, ranges.size(), 1);
- saltByte = SaltingUtil.getSaltingByte(key, 1, length, bucketNum);
- key[0] = saltByte;
- byte[] saltedStartKey = Arrays.copyOf(key, length + 1);
- length = ScanUtil.setKey(schema, ranges, position, Bound.UPPER, key, NUM_SALTING_BYTES, 1, ranges.size(), 1);
- byte[] saltedEndKey = Arrays.copyOf(key, length + 1);
- KeyRange range = PDataType.VARBINARY.getKeyRange(saltedStartKey, true, saltedEndKey, false);
- expandedRanges[idx++] = range;
- } while (incrementKey(ranges, position));
- // The comparator is imperfect, but sufficient for all single keys.
- Arrays.sort(expandedRanges, KeyRange.COMPARATOR);
- List<KeyRange> expandedRangesList = Arrays.asList(expandedRanges);
- return Collections.singletonList(expandedRangesList);
- }
-
- private static boolean incrementKey(List<List<KeyRange>> slots, int[] position) {
- int idx = slots.size() - 1;
- while (idx >= 0 && (position[idx] = (position[idx] + 1) % slots.get(idx).size()) == 0) {
- idx--;
- }
- return idx >= 0;
- }
-
public static KeyRange addSaltByte(byte[] startKey, KeyRange minMaxRange) {
byte saltByte = startKey.length == 0 ? 0 : startKey[0];
byte[] lowerRange = minMaxRange.getLowerRange();
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/0b469d47/phoenix-core/src/main/java/org/apache/phoenix/util/ScanUtil.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/ScanUtil.java b/phoenix-core/src/main/java/org/apache/phoenix/util/ScanUtil.java
index 3531306..528eda3 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/util/ScanUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/util/ScanUtil.java
@@ -283,9 +283,6 @@ public class ScanUtil {
*/
boolean inclusiveUpper = range.isInclusive(bound) && bound == Bound.UPPER;
boolean exclusiveLower = !range.isInclusive(bound) && bound == Bound.LOWER;
- if (!isFixedWidth && ( i < schema.getMaxFields()-1 || inclusiveUpper || exclusiveLower)) {
- key[offset++] = QueryConstants.SEPARATOR_BYTE;
- }
// If we are setting the upper bound of using inclusive single key, we remember
// to increment the key if we exit the loop after this iteration.
//
@@ -298,6 +295,13 @@ public class ScanUtil {
// key slots would cause the flag to become true.
lastInclusiveUpperSingleKey = range.isSingleKey() && inclusiveUpper;
anyInclusiveUpperRangeKey |= !range.isSingleKey() && inclusiveUpper;
+
+ if (!isFixedWidth && ( i < schema.getMaxFields()-1 || inclusiveUpper || exclusiveLower)) {
+ key[offset++] = QueryConstants.SEPARATOR_BYTE;
+ // Set lastInclusiveUpperSingleKey back to false if this is the last pk column
+ // as we don't want to increment the null byte in this case
+ lastInclusiveUpperSingleKey &= i < schema.getMaxFields()-1;
+ }
// If we are setting the lower bound with an exclusive range key, we need to bump the
// slot up for each key part. For an upper bound, we bump up an inclusive key, but
// only after the last key part.
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/0b469d47/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryOptimizerTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryOptimizerTest.java b/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryOptimizerTest.java
index f3359da..8eaa4f2 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryOptimizerTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryOptimizerTest.java
@@ -25,12 +25,11 @@ import static org.junit.Assert.assertFalse;
import java.sql.Connection;
import java.sql.DriverManager;
-import org.junit.Test;
-
import org.apache.phoenix.compile.OrderByCompiler.OrderBy;
import org.apache.phoenix.jdbc.PhoenixStatement;
import org.apache.phoenix.query.BaseConnectionlessQueryTest;
import org.apache.phoenix.util.SchemaUtil;
+import org.junit.Test;
public class QueryOptimizerTest extends BaseConnectionlessQueryTest {
@@ -156,17 +155,27 @@ public class QueryOptimizerTest extends BaseConnectionlessQueryTest {
conn.createStatement().execute("CREATE TABLE t (k INTEGER NOT NULL PRIMARY KEY, v1 VARCHAR, v2 VARCHAR) IMMUTABLE_ROWS=true");
conn.createStatement().execute("CREATE INDEX idx ON t(v1)");
PhoenixStatement stmt = conn.createStatement().unwrap(PhoenixStatement.class);
- QueryPlan plan = stmt.optimizeQuery("SELECT k FROM t WHERE k = 30 ORDER BY v1 LIMIT 5");
+ QueryPlan plan = stmt.optimizeQuery("SELECT k FROM t WHERE k > 30 ORDER BY v1 LIMIT 5");
assertEquals("IDX", plan.getTableRef().getTable().getTableName().getString());
}
@Test
+ public void testChoosePointLookupOverOrderByRemoval() throws Exception {
+ Connection conn = DriverManager.getConnection(getUrl());
+ conn.createStatement().execute("CREATE TABLE t (k INTEGER NOT NULL PRIMARY KEY, v1 VARCHAR, v2 VARCHAR) IMMUTABLE_ROWS=true");
+ conn.createStatement().execute("CREATE INDEX idx ON t(v1)");
+ PhoenixStatement stmt = conn.createStatement().unwrap(PhoenixStatement.class);
+ QueryPlan plan = stmt.optimizeQuery("SELECT k FROM t WHERE k = 30 ORDER BY v1 LIMIT 5"); // Prefer
+ assertEquals("T", plan.getTableRef().getTable().getTableName().getString());
+ }
+
+ @Test
public void testChooseIndexFromOrderByDesc() throws Exception {
Connection conn = DriverManager.getConnection(getUrl());
conn.createStatement().execute("CREATE TABLE t (k INTEGER NOT NULL PRIMARY KEY DESC, v1 VARCHAR, v2 VARCHAR) IMMUTABLE_ROWS=true");
conn.createStatement().execute("CREATE INDEX idx ON t(v1)");
PhoenixStatement stmt = conn.createStatement().unwrap(PhoenixStatement.class);
- QueryPlan plan = stmt.optimizeQuery("SELECT k FROM t WHERE k = 30 ORDER BY v1, k DESC LIMIT 5");
+ QueryPlan plan = stmt.optimizeQuery("SELECT k FROM t WHERE k > 30 ORDER BY v1, k DESC LIMIT 5");
assertEquals("IDX", plan.getTableRef().getTable().getTableName().getString());
}
@@ -176,7 +185,7 @@ public class QueryOptimizerTest extends BaseConnectionlessQueryTest {
conn.createStatement().execute("CREATE TABLE t (k INTEGER NOT NULL PRIMARY KEY DESC, v1 VARCHAR, v2 VARCHAR) IMMUTABLE_ROWS=true");
conn.createStatement().execute("CREATE INDEX idx ON t(v1)");
PhoenixStatement stmt = conn.createStatement().unwrap(PhoenixStatement.class);
- QueryPlan plan = stmt.optimizeQuery("SELECT k FROM t WHERE k = 30 ORDER BY v1, k LIMIT 5");
+ QueryPlan plan = stmt.optimizeQuery("SELECT k FROM t WHERE k > 30 ORDER BY v1, k LIMIT 5");
assertEquals("T", plan.getTableRef().getTable().getTableName().getString());
}
@@ -186,10 +195,20 @@ public class QueryOptimizerTest extends BaseConnectionlessQueryTest {
conn.createStatement().execute("CREATE TABLE t (k INTEGER NOT NULL PRIMARY KEY DESC, v1 VARCHAR, v2 VARCHAR) IMMUTABLE_ROWS=true");
conn.createStatement().execute("CREATE INDEX idx ON t(v1, k)");
PhoenixStatement stmt = conn.createStatement().unwrap(PhoenixStatement.class);
- QueryPlan plan = stmt.optimizeQuery("SELECT k FROM t WHERE k = 30 ORDER BY v1, k LIMIT 5");
+ QueryPlan plan = stmt.optimizeQuery("SELECT k FROM t WHERE k > 30 ORDER BY v1, k LIMIT 5");
assertEquals("IDX", plan.getTableRef().getTable().getTableName().getString());
}
+ @Test
+ public void testChoosePointLookupOverOrderByDesc() throws Exception {
+ Connection conn = DriverManager.getConnection(getUrl());
+ conn.createStatement().execute("CREATE TABLE t (k INTEGER NOT NULL PRIMARY KEY DESC, v1 VARCHAR, v2 VARCHAR) IMMUTABLE_ROWS=true");
+ conn.createStatement().execute("CREATE INDEX idx ON t(v1, k)");
+ PhoenixStatement stmt = conn.createStatement().unwrap(PhoenixStatement.class);
+ QueryPlan plan = stmt.optimizeQuery("SELECT k FROM t WHERE k = 30 ORDER BY v1, k LIMIT 5");
+ assertEquals("T", plan.getTableRef().getTable().getTableName().getString());
+ }
+
@Test
public void testChooseIndexWithLongestRowKey() throws Exception {
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/0b469d47/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereClauseCompileTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereClauseCompileTest.java b/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereClauseCompileTest.java
index cfa063f..fb9dffe 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereClauseCompileTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereClauseCompileTest.java
@@ -107,7 +107,7 @@ public class WhereClauseCompileTest extends BaseConnectionlessQueryTest {
PDataType.LONG.toBytes(1L, key, 1);
key[0] = SaltingUtil.getSaltingByte(key, 1, PDataType.LONG.getByteSize(), 20);
byte[] expectedStartKey = key;
- byte[] expectedEndKey = ByteUtil.nextKey(key);
+ byte[] expectedEndKey = ByteUtil.concat(key, QueryConstants.SEPARATOR_BYTE_ARRAY);
byte[] startKey = scan.getStartRow();
byte[] stopKey = scan.getStopRow();
assertTrue(Bytes.compareTo(expectedStartKey, startKey) == 0);
@@ -128,7 +128,7 @@ public class WhereClauseCompileTest extends BaseConnectionlessQueryTest {
PDataType.VARCHAR.toBytes("a", key, 1);
key[0] = SaltingUtil.getSaltingByte(key, 1, 1, 20);
byte[] expectedStartKey = key;
- byte[] expectedEndKey = ByteUtil.concat(key, ByteUtil.nextKey(QueryConstants.SEPARATOR_BYTE_ARRAY));
+ byte[] expectedEndKey = ByteUtil.concat(key, QueryConstants.SEPARATOR_BYTE_ARRAY);
byte[] startKey = scan.getStartRow();
byte[] stopKey = scan.getStopRow();
assertTrue(Bytes.compareTo(expectedStartKey, startKey) == 0);
@@ -148,13 +148,11 @@ public class WhereClauseCompileTest extends BaseConnectionlessQueryTest {
PDataType.LONG.toBytes(1L, key, 1);
key[0] = SaltingUtil.getSaltingByte(key, 1, PDataType.LONG.getByteSize(), 20);
byte[] startKey1 = key;
- byte[] endKey1 = ByteUtil.nextKey(key);
key = new byte[PDataType.LONG.getByteSize() + 1];
PDataType.LONG.toBytes(3L, key, 1);
key[0] = SaltingUtil.getSaltingByte(key, 1, PDataType.LONG.getByteSize(), 20);
byte[] startKey2 = key;
- byte[] endKey2 = ByteUtil.nextKey(key);
byte[] startKey = scan.getStartRow();
byte[] stopKey = scan.getStopRow();
@@ -163,15 +161,15 @@ public class WhereClauseCompileTest extends BaseConnectionlessQueryTest {
byte[] expectedStartKey;
byte[] expectedEndKey;
List<List<KeyRange>> expectedRanges = Collections.singletonList(
- Arrays.asList(KeyRange.getKeyRange(startKey1, true, endKey1, false),
- KeyRange.getKeyRange(startKey2, true, endKey2, false)));
+ Arrays.asList(KeyRange.getKeyRange(startKey1),
+ KeyRange.getKeyRange(startKey2)));
if (Bytes.compareTo(startKey1, startKey2) > 0) {
expectedStartKey = startKey2;
- expectedEndKey = endKey1;
+ expectedEndKey = ByteUtil.concat(startKey1, QueryConstants.SEPARATOR_BYTE_ARRAY);
Collections.reverse(expectedRanges.get(0));
} else {
expectedStartKey = startKey1;
- expectedEndKey = endKey2;
+ expectedEndKey = ByteUtil.concat(startKey2, QueryConstants.SEPARATOR_BYTE_ARRAY);;
}
assertTrue(Bytes.compareTo(expectedStartKey, startKey) == 0);
assertTrue(Bytes.compareTo(expectedEndKey, stopKey) == 0);
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/0b469d47/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereClauseOptimizerTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereClauseOptimizerTest.java b/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereClauseOptimizerTest.java
index 9a3755c..1de13fd 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereClauseOptimizerTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereClauseOptimizerTest.java
@@ -42,9 +42,6 @@ import java.util.Set;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.filter.Filter;
-import org.junit.Test;
-
-import com.google.common.collect.Sets;
import org.apache.phoenix.compile.GroupByCompiler.GroupBy;
import org.apache.phoenix.expression.Expression;
import org.apache.phoenix.expression.OrExpression;
@@ -62,6 +59,9 @@ import org.apache.phoenix.schema.PDataType;
import org.apache.phoenix.util.ByteUtil;
import org.apache.phoenix.util.DateUtil;
import org.apache.phoenix.util.TestUtil;
+import org.junit.Test;
+
+import com.google.common.collect.Sets;
@@ -1615,6 +1615,6 @@ public class WhereClauseOptimizerTest extends BaseConnectionlessQueryTest {
KeyRange.getKeyRange(ByteUtil.concat(PDataType.CHAR.toBytes(secondOrgId), PDataType.CHAR.toBytes(secondParentId)))));
assertEquals(skipScanRanges, context.getScanRanges().getRanges());
assertArrayEquals(ByteUtil.concat(PDataType.CHAR.toBytes(firstOrgId), PDataType.CHAR.toBytes(firstParentId)), scan.getStartRow());
- assertArrayEquals(ByteUtil.nextKey(ByteUtil.concat(PDataType.CHAR.toBytes(secondOrgId), PDataType.CHAR.toBytes(secondParentId), QueryConstants.SEPARATOR_BYTE_ARRAY)), scan.getStopRow());
+ assertArrayEquals(ByteUtil.concat(PDataType.CHAR.toBytes(secondOrgId), PDataType.CHAR.toBytes(secondParentId), QueryConstants.SEPARATOR_BYTE_ARRAY), scan.getStopRow());
}
}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/0b469d47/phoenix-core/src/test/java/org/apache/phoenix/end2end/index/ImmutableIndexTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/end2end/index/ImmutableIndexTest.java b/phoenix-core/src/test/java/org/apache/phoenix/end2end/index/ImmutableIndexTest.java
index ff87b40..0bf0d63 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/end2end/index/ImmutableIndexTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/end2end/index/ImmutableIndexTest.java
@@ -36,10 +36,6 @@ import java.sql.SQLException;
import java.util.Map;
import java.util.Properties;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-import com.google.common.collect.Maps;
import org.apache.phoenix.end2end.BaseHBaseManagedTimeTest;
import org.apache.phoenix.exception.SQLExceptionCode;
import org.apache.phoenix.jdbc.PhoenixConnection;
@@ -47,6 +43,10 @@ import org.apache.phoenix.query.QueryConstants;
import org.apache.phoenix.query.QueryServices;
import org.apache.phoenix.util.QueryUtil;
import org.apache.phoenix.util.ReadOnlyProps;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.google.common.collect.Maps;
public class ImmutableIndexTest extends BaseHBaseManagedTimeTest{
@@ -214,31 +214,25 @@ public class ImmutableIndexTest extends BaseHBaseManagedTimeTest{
"CLIENT MERGE SORT");
assertEquals(expectedPlan,QueryUtil.getExplainPlan(rs));
- // Will still use index, since there's no LIMIT clause
- query = "SELECT k,v FROM t WHERE v >= 'x' ORDER BY k";
+ // Use data table, since point lookup trumps order by
+ query = "SELECT k,v FROM t WHERE k = 'a' ORDER BY v";
rs = conn.createStatement().executeQuery(query);
assertTrue(rs.next());
assertEquals("a",rs.getString(1));
assertEquals("x",rs.getString(2));
- assertTrue(rs.next());
- assertEquals("b",rs.getString(1));
- assertEquals("y",rs.getString(2));
assertFalse(rs.next());
rs = conn.createStatement().executeQuery("EXPLAIN " + query);
- // Turns into an ORDER BY, which could be bad if lots of data is
- // being returned. Without stats we don't know. The alternative
- // would be a full table scan.
- expectedPlan = indexSaltBuckets == null ?
- ("CLIENT PARALLEL 1-WAY RANGE SCAN OVER I [*] - [~'x']\n" +
- " SERVER TOP -1 ROWS SORTED BY [K]\n" +
- "CLIENT MERGE SORT") :
- ("CLIENT PARALLEL 4-WAY SKIP SCAN ON 4 RANGES OVER I [0,*] - [3,~'x']\n" +
- " SERVER TOP -1 ROWS SORTED BY [K]\n" +
- "CLIENT MERGE SORT");
+ expectedPlan = tableSaltBuckets == null ?
+ ("CLIENT PARALLEL 1-WAY RANGE SCAN OVER T ['a']\n" +
+ " SERVER SORTED BY [V]\n" +
+ "CLIENT MERGE SORT") :
+ ("CLIENT PARALLEL 1-WAY RANGE SCAN OVER T [[2,97]]\n" +
+ " SERVER SORTED BY [V]\n" +
+ "CLIENT MERGE SORT");
assertEquals(expectedPlan,QueryUtil.getExplainPlan(rs));
- // Will use data table now, since there's a LIMIT clause and
- // we're able to optimize out the ORDER BY.
+ // Will use data table now, since there's an ORDER BY which can
+ // be optimized out for the data table, but not the index table.
query = "SELECT k,v FROM t WHERE v >= 'x' ORDER BY k LIMIT 2";
rs = conn.createStatement().executeQuery(query);
assertTrue(rs.next());
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/0b469d47/phoenix-core/src/test/java/org/apache/phoenix/end2end/index/MutableSaltedIndexTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/end2end/index/MutableSaltedIndexTest.java b/phoenix-core/src/test/java/org/apache/phoenix/end2end/index/MutableSaltedIndexTest.java
index 5c5ffd6..f6369f7 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/end2end/index/MutableSaltedIndexTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/end2end/index/MutableSaltedIndexTest.java
@@ -31,13 +31,13 @@ import java.sql.ResultSet;
import java.util.Map;
import java.util.Properties;
+import org.apache.phoenix.query.QueryServices;
+import org.apache.phoenix.util.QueryUtil;
+import org.apache.phoenix.util.ReadOnlyProps;
import org.junit.BeforeClass;
import org.junit.Test;
import com.google.common.collect.Maps;
-import org.apache.phoenix.query.QueryServices;
-import org.apache.phoenix.util.QueryUtil;
-import org.apache.phoenix.util.ReadOnlyProps;
public class MutableSaltedIndexTest extends BaseMutableIndexTest{
@@ -138,27 +138,21 @@ public class MutableSaltedIndexTest extends BaseMutableIndexTest{
"CLIENT MERGE SORT");
assertEquals(expectedPlan,QueryUtil.getExplainPlan(rs));
- // Will still use index, since there's no LIMIT clause
- query = "SELECT k,v FROM " + DATA_TABLE_FULL_NAME + " WHERE v >= 'x' ORDER BY k";
+ // Use data table, since point lookup trumps order by
+ query = "SELECT k,v FROM " + DATA_TABLE_FULL_NAME + " WHERE k = 'a' ORDER BY v";
rs = conn.createStatement().executeQuery(query);
assertTrue(rs.next());
assertEquals("a",rs.getString(1));
assertEquals("x",rs.getString(2));
- assertTrue(rs.next());
- assertEquals("b",rs.getString(1));
- assertEquals("y",rs.getString(2));
assertFalse(rs.next());
rs = conn.createStatement().executeQuery("EXPLAIN " + query);
- // Turns into an ORDER BY, which could be bad if lots of data is
- // being returned. Without stats we don't know. The alternative
- // would be a full table scan.
- expectedPlan = indexSaltBuckets == null ?
- ("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + INDEX_TABLE_FULL_NAME + " [*] - [~'x']\n" +
- " SERVER TOP -1 ROWS SORTED BY [K]\n" +
- "CLIENT MERGE SORT") :
- ("CLIENT PARALLEL 4-WAY SKIP SCAN ON 4 RANGES OVER " + INDEX_TABLE_FULL_NAME + " [0,*] - [3,~'x']\n" +
- " SERVER TOP -1 ROWS SORTED BY [K]\n" +
- "CLIENT MERGE SORT");
+ expectedPlan = tableSaltBuckets == null ?
+ "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + DATA_TABLE_FULL_NAME + " ['a']\n" +
+ " SERVER SORTED BY [V]\n" +
+ "CLIENT MERGE SORT" :
+ "CLIENT PARALLEL 1-WAY RANGE SCAN OVER T [[2,97]]\n" +
+ " SERVER SORTED BY [V]\n" +
+ "CLIENT MERGE SORT";
assertEquals(expectedPlan,QueryUtil.getExplainPlan(rs));
// Will use data table now, since there's a LIMIT clause and
[2/2] git commit: Merge branch 'master' of
https://git-wip-us.apache.org/repos/asf/incubator-phoenix
Posted by ja...@apache.org.
Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/incubator-phoenix
Project: http://git-wip-us.apache.org/repos/asf/incubator-phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-phoenix/commit/bc21d8e4
Tree: http://git-wip-us.apache.org/repos/asf/incubator-phoenix/tree/bc21d8e4
Diff: http://git-wip-us.apache.org/repos/asf/incubator-phoenix/diff/bc21d8e4
Branch: refs/heads/master
Commit: bc21d8e418303b6db3a439dc1397750271692456
Parents: 0b469d4 d40c506
Author: James Taylor <ja...@apache.org>
Authored: Tue Feb 4 02:00:35 2014 -0800
Committer: James Taylor <ja...@apache.org>
Committed: Tue Feb 4 02:00:35 2014 -0800
----------------------------------------------------------------------
.../org/apache/phoenix/compile/QueryCompiler.java | 3 ++-
.../org/apache/phoenix/compile/StatementContext.java | 8 ++++++++
.../main/java/org/apache/phoenix/parse/HintNode.java | 4 ++++
.../org/apache/phoenix/compile/QueryCompileTest.java | 14 +++++++++++++-
4 files changed, 27 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/bc21d8e4/phoenix-core/src/main/java/org/apache/phoenix/compile/StatementContext.java
----------------------------------------------------------------------