http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/compile/UpsertCompiler.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/compile/UpsertCompiler.java 
b/phoenix-core/src/main/java/org/apache/phoenix/compile/UpsertCompiler.java
index 7b39a28..e12f5a4 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/UpsertCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/UpsertCompiler.java
@@ -150,8 +150,10 @@ public class UpsertCompiler {
                             
SQLExceptionCode.DATA_EXCEEDS_MAX_CAPACITY).setColumnName(column.getName().getString())
                             .setMessage("value=" + 
column.getDataType().toStringLiteral(ptr, null)).build()
                             .buildException(); }
-                    column.getDataType().coerceBytes(ptr, value, 
column.getDataType(), precision, scale,
-                            SortOrder.getDefault(), column.getMaxLength(), 
column.getScale(), column.getSortOrder());
+                    column.getDataType().coerceBytes(ptr, value, 
column.getDataType(), 
+                            precision, scale, SortOrder.getDefault(), 
+                            column.getMaxLength(), column.getScale(), 
column.getSortOrder(),
+                            table.rowKeyOrderOptimizable());
                     values[i] = ByteUtil.copyKeyBytesIfNecessary(ptr);
                 }
                 setValues(values, pkSlotIndexes, columnIndexes, table, 
mutation, statement);
@@ -772,6 +774,7 @@ public class UpsertCompiler {
                 final SequenceManager sequenceManager = 
context.getSequenceManager();
                 // Next evaluate all the expressions
                 int nodeIndex = nodeIndexOffset;
+                PTable table = tableRef.getTable();
                 Tuple tuple = sequenceManager.getSequenceCount() == 0 ? null :
                     sequenceManager.newSequenceTuple(null);
                 for (Expression constantExpression : constantExpressions) {
@@ -793,9 +796,10 @@ public class UpsertCompiler {
                                 .setMessage("value=" + 
constantExpression.toString()).build().buildException();
                         }
                     }
-                    column.getDataType().coerceBytes(ptr, value,
-                            constantExpression.getDataType(), 
constantExpression.getMaxLength(), constantExpression.getScale(), 
constantExpression.getSortOrder(),
-                            column.getMaxLength(), 
column.getScale(),column.getSortOrder());
+                    column.getDataType().coerceBytes(ptr, value, 
constantExpression.getDataType(), 
+                            constantExpression.getMaxLength(), 
constantExpression.getScale(), constantExpression.getSortOrder(),
+                            column.getMaxLength(), 
column.getScale(),column.getSortOrder(),
+                            table.rowKeyOrderOptimizable());
                     if (overlapViewColumns.contains(column) && 
Bytes.compareTo(ptr.get(), ptr.getOffset(), ptr.getLength(), 
column.getViewConstant(), 0, column.getViewConstant().length-1) != 0) {
                         throw new SQLExceptionInfo.Builder(
                                 SQLExceptionCode.CANNOT_UPDATE_VIEW_COLUMN)
@@ -814,7 +818,7 @@ public class UpsertCompiler {
                     }
                 }
                 Map<ImmutableBytesPtr, RowMutationState> mutation = 
Maps.newHashMapWithExpectedSize(1);
-                setValues(values, pkSlotIndexes, columnIndexes, 
tableRef.getTable(), mutation, statement);
+                setValues(values, pkSlotIndexes, columnIndexes, table, 
mutation, statement);
                 return new MutationState(tableRef, mutation, 0, maxSize, 
connection);
             }
 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/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 0cbef11..332f293 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
@@ -61,7 +61,9 @@ import org.apache.phoenix.schema.PTable;
 import org.apache.phoenix.schema.RowKeySchema;
 import org.apache.phoenix.schema.SaltingUtil;
 import org.apache.phoenix.schema.SortOrder;
+import org.apache.phoenix.schema.ValueSchema.Field;
 import org.apache.phoenix.schema.tuple.Tuple;
+import org.apache.phoenix.schema.types.PArrayDataType;
 import org.apache.phoenix.schema.types.PChar;
 import org.apache.phoenix.schema.types.PDataType;
 import org.apache.phoenix.schema.types.PVarbinary;
@@ -194,8 +196,9 @@ public class WhereOptimizer {
             if (hasMinMaxRange) {
                 System.arraycopy(tenantIdBytes, 0, minMaxRangePrefix, 
minMaxRangeOffset, tenantIdBytes.length);
                 minMaxRangeOffset += tenantIdBytes.length;
-                if (!schema.getField(pkPos).getDataType().isFixedWidth()) {
-                    minMaxRangePrefix[minMaxRangeOffset] = 
QueryConstants.SEPARATOR_BYTE;
+                Field f = schema.getField(pkPos);
+                if (!f.getDataType().isFixedWidth()) {
+                    minMaxRangePrefix[minMaxRangeOffset] = 
SchemaUtil.getSeparatorByte(schema.rowKeyOrderOptimizable(), 
tenantIdBytes.length==0, f);
                     minMaxRangeOffset++;
                 }
             }
@@ -259,6 +262,8 @@ public class WhereOptimizer {
                     hasNonPointKey = true;
                 }
             }
+            keyRanges = transformKeyRangesIfNecessary(slot, context);
+            
             hasMultiRanges |= keyRanges.size() > 1;
             // Force a range scan if we've encountered a multi-span slot (i.e. 
RVC)
             // and a non point key, as our skip scan only handles fully 
qualified
@@ -317,6 +322,45 @@ public class WhereOptimizer {
         }
     }
     
+    // Special hack for PHOENIX-2067 to change the constant array to match
+    // the separators used for descending, variable length arrays.
+    // Note that there'd already be a coerce expression around the constant
+    // to convert it to the right type, so we shouldn't do that here.
+    private static List<KeyRange> 
transformKeyRangesIfNecessary(KeyExpressionVisitor.KeySlot slot, 
StatementContext context) {
+        KeyPart keyPart = slot.getKeyPart();
+        List<KeyRange> keyRanges = slot.getKeyRanges();
+        PColumn column = keyPart.getColumn();
+        if (column != null) {
+            PDataType type = column.getDataType();
+            PTable table = context.getCurrentTable().getTable();
+            // Constants are always build with rowKeyOptimizable as true, 
using the correct separators
+            // We only need to do this conversion if we have a table that has 
not yet been converted.
+            if (type != null && type.isArrayType() && column.getSortOrder() == 
SortOrder.DESC && !PArrayDataType.arrayBaseType(type).isFixedWidth() && 
!table.rowKeyOrderOptimizable()) {
+                ImmutableBytesWritable ptr = context.getTempPtr();
+                List<KeyRange> newKeyRanges = 
Lists.newArrayListWithExpectedSize(keyRanges.size());
+                for (KeyRange keyRange : keyRanges) {
+                    byte[] lower = keyRange.getLowerRange();
+                    if (!keyRange.lowerUnbound()) {
+                        ptr.set(lower);;
+                        type.coerceBytes(ptr, null, type, null, null, 
SortOrder.DESC, null, null, SortOrder.DESC, false);
+                        lower = ByteUtil.copyKeyBytesIfNecessary(ptr);
+                    }
+                    byte[] upper = keyRange.getUpperRange();
+                    if (!keyRange.upperUnbound()) {
+                        ptr.set(upper);;
+                        type.coerceBytes(ptr, null, type, null, null, 
SortOrder.DESC, null, null, SortOrder.DESC, false);
+                        upper = ByteUtil.copyKeyBytesIfNecessary(ptr);
+                    }
+                    keyRange = KeyRange.getKeyRange(lower, 
keyRange.isLowerInclusive(), upper, keyRange.isUpperInclusive());
+                    newKeyRanges.add(keyRange);
+                }
+                
+                return newKeyRanges;
+            }
+        }
+        return keyRanges;
+    }
+    
     /**
      * Get an optimal combination of key expressions for hash join key range 
optimization.
      * @return returns true if the entire combined expression is covered by 
key range optimization
@@ -996,6 +1040,9 @@ public class WhereOptimizer {
             // Handles cases like WHERE substr(foo,1,3) IN ('aaa','bbb')
             for (Expression key : keyExpressions) {
                 KeyRange range = childPart.getKeyRange(CompareOp.EQUAL, key);
+                if (range == null) {
+                    return null;
+                }
                 if (range != KeyRange.EMPTY_RANGE) { // null means it can't 
possibly be in range
                     ranges.add(range);
                 }
@@ -1393,7 +1440,7 @@ public class WhereOptimizer {
                     return null; // Shouldn't happen
                 }
                 ImmutableBytesWritable ptr = context.getTempPtr();
-                if (!rhs.evaluate(null, ptr) || ptr.getLength()==0) {
+                if (!rhs.evaluate(null, ptr)) { // Don't return if evaluated 
to null
                     return null; 
                 }
                 byte[] key = ByteUtil.copyKeyBytesIfNecessary(ptr);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/BaseScannerRegionObserver.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/BaseScannerRegionObserver.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/BaseScannerRegionObserver.java
index d9e64e2..c8d4926 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/BaseScannerRegionObserver.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/BaseScannerRegionObserver.java
@@ -88,6 +88,8 @@ abstract public class BaseScannerRegionObserver extends 
BaseRegionObserver {
     public static final String ANALYZE_TABLE = "_ANALYZETABLE";
     public static final String GUIDEPOST_WIDTH_BYTES = 
"_GUIDEPOST_WIDTH_BYTES";
     public static final String GUIDEPOST_PER_REGION = "_GUIDEPOST_PER_REGION";
+    public static final String UPGRADE_DESC_ROW_KEY = "_UPGRADE_DESC_ROW_KEY";
+    
     /**
      * Attribute name used to pass custom annotations in Scans and Mutations 
(later). Custom annotations
      * are used to augment log lines emitted by Phoenix. See 
https://issues.apache.org/jira/browse/PHOENIX-1198.

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java
index 1be0548..dcfe61d 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java
@@ -183,6 +183,7 @@ import org.apache.phoenix.util.QueryUtil;
 import org.apache.phoenix.util.ScanUtil;
 import org.apache.phoenix.util.SchemaUtil;
 import org.apache.phoenix.util.ServerUtil;
+import org.apache.phoenix.util.UpgradeUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -213,6 +214,10 @@ import com.google.protobuf.Service;
 public class MetaDataEndpointImpl extends MetaDataProtocol implements 
CoprocessorService, Coprocessor {
     private static final Logger logger = 
LoggerFactory.getLogger(MetaDataEndpointImpl.class);
 
+    // Column to track tables that have been upgraded based on PHOENIX-2067
+    public static final String ROW_KEY_ORDER_OPTIMIZABLE = 
"ROW_KEY_ORDER_OPTIMIZABLE";
+    public static final byte[] ROW_KEY_ORDER_OPTIMIZABLE_BYTES = 
Bytes.toBytes(ROW_KEY_ORDER_OPTIMIZABLE);
+
     // KeyValues for Table
     private static final KeyValue TABLE_TYPE_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
TABLE_TYPE_BYTES);
     private static final KeyValue TABLE_SEQ_NUM_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
TABLE_SEQ_NUM_BYTES);
@@ -233,6 +238,7 @@ public class MetaDataEndpointImpl extends MetaDataProtocol 
implements Coprocesso
     private static final KeyValue STORE_NULLS_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
STORE_NULLS_BYTES);
     private static final KeyValue EMPTY_KEYVALUE_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
QueryConstants.EMPTY_COLUMN_BYTES);
     private static final KeyValue BASE_COLUMN_COUNT_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
PhoenixDatabaseMetaData.BASE_COLUMN_COUNT_BYTES);
+    private static final KeyValue ROW_KEY_ORDER_OPTIMIZABLE_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
ROW_KEY_ORDER_OPTIMIZABLE_BYTES);
     
     private static final List<KeyValue> TABLE_KV_COLUMNS = 
Arrays.<KeyValue>asList(
             EMPTY_KEYVALUE_KV,
@@ -253,7 +259,8 @@ public class MetaDataEndpointImpl extends MetaDataProtocol 
implements Coprocesso
             INDEX_TYPE_KV,
             INDEX_DISABLE_TIMESTAMP_KV,
             STORE_NULLS_KV,
-            BASE_COLUMN_COUNT_KV
+            BASE_COLUMN_COUNT_KV,
+            ROW_KEY_ORDER_OPTIMIZABLE_KV
             );
     static {
         Collections.sort(TABLE_KV_COLUMNS, KeyValue.COMPARATOR);
@@ -276,6 +283,7 @@ public class MetaDataEndpointImpl extends MetaDataProtocol 
implements Coprocesso
     private static final int INDEX_TYPE_INDEX = 
TABLE_KV_COLUMNS.indexOf(INDEX_TYPE_KV);
     private static final int STORE_NULLS_INDEX = 
TABLE_KV_COLUMNS.indexOf(STORE_NULLS_KV);
     private static final int BASE_COLUMN_COUNT_INDEX = 
TABLE_KV_COLUMNS.indexOf(BASE_COLUMN_COUNT_KV);
+    private static final int ROW_KEY_ORDER_OPTIMIZABLE_INDEX = 
TABLE_KV_COLUMNS.indexOf(ROW_KEY_ORDER_OPTIMIZABLE_KV);
 
     // KeyValues for Column
     private static final KeyValue DECIMAL_DIGITS_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
DECIMAL_DIGITS_BYTES);
@@ -782,6 +790,8 @@ public class MetaDataEndpointImpl extends MetaDataProtocol 
implements Coprocesso
         Cell baseColumnCountKv = tableKeyValues[BASE_COLUMN_COUNT_INDEX];
         int baseColumnCount = baseColumnCountKv == null ? 0 : 
PInteger.INSTANCE.getCodec().decodeInt(baseColumnCountKv.getValueArray(),
             baseColumnCountKv.getValueOffset(), SortOrder.getDefault());
+        Cell rowKeyOrderOptimizableKv = 
tableKeyValues[ROW_KEY_ORDER_OPTIMIZABLE_INDEX];
+        boolean rowKeyOrderOptimizable = rowKeyOrderOptimizableKv == null ? 
false : 
Boolean.TRUE.equals(PBoolean.INSTANCE.toObject(rowKeyOrderOptimizableKv.getValueArray(),
 rowKeyOrderOptimizableKv.getValueOffset(), 
rowKeyOrderOptimizableKv.getValueLength()));
 
         List<PColumn> columns = 
Lists.newArrayListWithExpectedSize(columnCount);
         List<PTable> indexes = new ArrayList<PTable>();
@@ -826,7 +836,7 @@ public class MetaDataEndpointImpl extends MetaDataProtocol 
implements Coprocesso
         return PTableImpl.makePTable(tenantId, schemaName, tableName, 
tableType, indexState, timeStamp,
             tableSeqNum, pkName, saltBucketNum, columns, tableType == INDEX ? 
schemaName : null,
             tableType == INDEX ? dataTableName : null, indexes, 
isImmutableRows, physicalTables, defaultFamilyName, viewStatement,
-            disableWAL, multiTenant, storeNulls, viewType, viewIndexId, 
indexType, stats, baseColumnCount);
+            disableWAL, multiTenant, storeNulls, viewType, viewIndexId, 
indexType, stats, baseColumnCount, rowKeyOrderOptimizable);
     }
 
     private PFunction getFunction(RegionScanner scanner, final boolean 
isReplace, long clientTimeStamp, List<Mutation> deleteMutationsForReplace)
@@ -1058,7 +1068,6 @@ public class MetaDataEndpointImpl extends 
MetaDataProtocol implements Coprocesso
             return null;
         }
 
-
     @Override
     public void createTable(RpcController controller, CreateTableRequest 
request,
             RpcCallback<MetaDataResponse> done) {
@@ -1145,6 +1154,14 @@ public class MetaDataEndpointImpl extends 
MetaDataProtocol implements Coprocesso
                         return;
                     }
                 }
+                PTableType tableType = 
MetaDataUtil.getTableType(tableMetadata, GenericKeyValueBuilder.INSTANCE,
+                        new ImmutableBytesWritable());
+                // Add cell for ROW_KEY_ORDER_OPTIMIZABLE = true, as we know 
that new tables
+                // conform the correct row key. The exception is for a VIEW, 
which the client
+                // sends over depending on its base physical table.
+                if (tableType != PTableType.VIEW) {
+                    UpgradeUtil.addRowKeyOrderOptimizableCell(tableMetadata, 
key, clientTimeStamp);
+                }
                 // TODO: Switch this to Region#batchMutate when we want to 
support indexes on the
                 // system
                 // table. Basically, we get all the locks that we don't 
already hold for all the
@@ -1563,7 +1580,7 @@ public class MetaDataEndpointImpl extends 
MetaDataProtocol implements Coprocesso
                 } else {
                     // server-side, except for indexing, we always expect the 
keyvalues to be standard KeyValues
                     PTableType expectedType = 
MetaDataUtil.getTableType(tableMetadata, GenericKeyValueBuilder.INSTANCE,
-                            new ImmutableBytesPtr());
+                            new ImmutableBytesWritable());
                     // We said to drop a table, but found a view or visa versa
                     if (type != expectedType) { return new 
MetaDataMutationResult(MutationCode.TABLE_NOT_FOUND,
                             EnvironmentEdgeManager.currentTimeMillis(), null); 
}
@@ -1612,6 +1629,8 @@ public class MetaDataEndpointImpl extends 
MetaDataProtocol implements Coprocesso
             if (view.getBaseColumnCount() == 
QueryConstants.DIVERGED_VIEW_BASE_COLUMN_COUNT) {
                 // if a view has divorced itself from the base table, we don't 
allow schema changes
                 // to be propagated to it.
+                // FIXME: we should allow the update, but just not propagate 
it to this view
+                // The one exception is PK changes which need to be propagated 
to diverged views as well
                return new 
MetaDataMutationResult(MutationCode.UNALLOWED_TABLE_MUTATION, 
EnvironmentEdgeManager.currentTimeMillis(), null);
             }
             int numColsAddedToBaseTable = 0;
@@ -1846,6 +1865,9 @@ public class MetaDataEndpointImpl extends 
MetaDataProtocol implements Coprocesso
                     
                     // add index row header key to the invalidate list to 
force clients to fetch the latest meta-data
                     invalidateList.add(new 
ImmutableBytesPtr(indexHeaderRowKey));
+                    if (index.rowKeyOrderOptimizable()) {
+                        
UpgradeUtil.addRowKeyOrderOptimizableCell(mutationsForAddingColumnsToViews, 
indexHeaderRowKey, clientTimeStamp);
+                    }
                     
mutationsForAddingColumnsToViews.add(indexHeaderRowMutation);
                 }
             }
@@ -1866,6 +1888,9 @@ public class MetaDataEndpointImpl extends 
MetaDataProtocol implements Coprocesso
             
viewHeaderRowPut.addColumn(PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
                     PhoenixDatabaseMetaData.TABLE_SEQ_NUM_BYTES, 
clientTimeStamp, viewSequencePtr);
             mutationsForAddingColumnsToViews.add(viewHeaderRowPut);
+            if (view.rowKeyOrderOptimizable()) {
+                
UpgradeUtil.addRowKeyOrderOptimizableCell(mutationsForAddingColumnsToViews, 
viewKey, clientTimeStamp);
+            }
 
             // Update positions of view columns
             for (PColumn column : view.getColumns()) {
@@ -1912,7 +1937,10 @@ public class MetaDataEndpointImpl extends 
MetaDataProtocol implements Coprocesso
                     byte[] schemaName = rowKeyMetaData[SCHEMA_NAME_INDEX];
                     byte[] tableName = rowKeyMetaData[TABLE_NAME_INDEX];
                     PTableType type = table.getType();
-                    List<Mutation> mutationsForAddingColumnsToViews = 
Collections.emptyList();
+                    byte[] tableHeaderRowKey = SchemaUtil.getTableKey(tenantId,
+                            schemaName, tableName);
+                    // Size for worst case - all new columns are PK column
+                    List<Mutation> mutationsForAddingColumnsToViews = 
Lists.newArrayListWithExpectedSize(tableMetaData.size() * ( 1 + 
table.getIndexes().size()));
                     /*
                      * If adding a column to a view, we don't want to 
propagate those meta-data changes to the child
                      * view hierarchy. This is because our check of finding 
child views is expensive and we want making
@@ -1973,6 +2001,17 @@ public class MetaDataEndpointImpl extends 
MetaDataProtocol implements Coprocesso
                                 continue;
                             } catch (ColumnNotFoundException e) {
                                 if (addingPKColumn) {
+                                    // We may be adding a DESC column, so if 
table is already
+                                    // able to be rowKeyOptimized, it should 
continue to be so.
+                                    if (table.rowKeyOrderOptimizable()) {
+                                        
UpgradeUtil.addRowKeyOrderOptimizableCell(mutationsForAddingColumnsToViews, 
tableHeaderRowKey, clientTimeStamp);
+                                    } else if (table.getType() == 
PTableType.VIEW){
+                                        // Don't allow view PK to diverge from 
table PK as our upgrade code
+                                        // does not handle this.
+                                        return new MetaDataMutationResult(
+                                                
MutationCode.UNALLOWED_TABLE_MUTATION, EnvironmentEdgeManager
+                                                        .currentTimeMillis(), 
null);
+                                    }
                                     // Add all indexes to invalidate list, as 
they will all be
                                     // adding the same PK column. No need to 
lock them, as we
                                     // have the parent table lock at this 
point.
@@ -1981,6 +2020,13 @@ public class MetaDataEndpointImpl extends 
MetaDataProtocol implements Coprocesso
                                                 .getTableKey(tenantId, 
index.getSchemaName()
                                                         .getBytes(), 
index.getTableName()
                                                         .getBytes())));
+                                        // We may be adding a DESC column, so 
if index is already
+                                        // able to be rowKeyOptimized, it 
should continue to be so.
+                                        if (index.rowKeyOrderOptimizable()) {
+                                            byte[] indexHeaderRowKey = 
SchemaUtil.getTableKey(index.getTenantId() == null ? ByteUtil.EMPTY_BYTE_ARRAY 
: index.getTenantId().getBytes(),
+                                                    
index.getSchemaName().getBytes(), index.getTableName().getBytes());
+                                            
UpgradeUtil.addRowKeyOrderOptimizableCell(mutationsForAddingColumnsToViews, 
indexHeaderRowKey, clientTimeStamp);
+                                        }
                                     }
                                 }
                                 continue;
@@ -2315,7 +2361,7 @@ public class MetaDataEndpointImpl extends 
MetaDataProtocol implements Coprocesso
             byte[] tenantId = 
rowKeyMetaData[PhoenixDatabaseMetaData.TENANT_ID_INDEX];
             schemaName = 
rowKeyMetaData[PhoenixDatabaseMetaData.SCHEMA_NAME_INDEX];
             tableName = 
rowKeyMetaData[PhoenixDatabaseMetaData.TABLE_NAME_INDEX];
-            byte[] key = SchemaUtil.getTableKey(tenantId, schemaName, 
tableName);
+            final byte[] key = SchemaUtil.getTableKey(tenantId, schemaName, 
tableName);
             Region region = env.getRegion();
             MetaDataMutationResult result = checkTableKeyInRegion(key, region);
             if (result != null) {
@@ -2350,6 +2396,7 @@ public class MetaDataEndpointImpl extends 
MetaDataProtocol implements Coprocesso
                 get.addColumn(TABLE_FAMILY_BYTES, DATA_TABLE_NAME_BYTES);
                 get.addColumn(TABLE_FAMILY_BYTES, INDEX_STATE_BYTES);
                 get.addColumn(TABLE_FAMILY_BYTES, 
INDEX_DISABLE_TIMESTAMP_BYTES);
+                get.addColumn(TABLE_FAMILY_BYTES, 
ROW_KEY_ORDER_OPTIMIZABLE_BYTES);
                 Result currentResult = region.get(get);
                 if (currentResult.rawCells().length == 0) {
                     
builder.setReturnCode(MetaDataProtos.MutationCode.TABLE_NOT_FOUND);
@@ -2360,6 +2407,7 @@ public class MetaDataEndpointImpl extends 
MetaDataProtocol implements Coprocesso
                 Cell dataTableKV = 
currentResult.getColumnLatestCell(TABLE_FAMILY_BYTES, DATA_TABLE_NAME_BYTES);
                 Cell currentStateKV = 
currentResult.getColumnLatestCell(TABLE_FAMILY_BYTES, INDEX_STATE_BYTES);
                 Cell currentDisableTimeStamp = 
currentResult.getColumnLatestCell(TABLE_FAMILY_BYTES, 
INDEX_DISABLE_TIMESTAMP_BYTES);
+                boolean rowKeyOrderOptimizable = 
currentResult.getColumnLatestCell(TABLE_FAMILY_BYTES, 
ROW_KEY_ORDER_OPTIMIZABLE_BYTES) != null;
 
                 PIndexState currentState =
                         
PIndexState.fromSerializedValue(currentStateKV.getValueArray()[currentStateKV
@@ -2418,6 +2466,7 @@ public class MetaDataEndpointImpl extends 
MetaDataProtocol implements Coprocesso
                         INDEX_STATE_BYTES, timeStamp, 
Bytes.toBytes(newState.getSerializedValue())));
                 }
 
+                PTable returnTable = null;
                 if (currentState != newState) {
                     byte[] dataTableKey = null;
                     if(dataTableKV != null) {
@@ -2431,6 +2480,12 @@ public class MetaDataEndpointImpl extends 
MetaDataProtocol implements Coprocesso
                         p.add(TABLE_FAMILY_BYTES, 
QueryConstants.EMPTY_COLUMN_BYTES, timeStamp, ByteUtil.EMPTY_BYTE_ARRAY);
                         tableMetadata.add(p);
                     }
+                    boolean setRowKeyOrderOptimizableCell = newState == 
PIndexState.BUILDING && !rowKeyOrderOptimizable;
+                    // We're starting a rebuild of the index, so add our 
rowKeyOrderOptimizable cell
+                    // so that the row keys get generated using the new row 
key format
+                    if (setRowKeyOrderOptimizableCell) {
+                        
UpgradeUtil.addRowKeyOrderOptimizableCell(tableMetadata, key, timeStamp);
+                    }
                     region.mutateRowsWithLocks(tableMetadata, 
Collections.<byte[]> emptySet(), HConstants.NO_NONCE,
                         HConstants.NO_NONCE);
                     // Invalidate from cache
@@ -2439,12 +2494,18 @@ public class MetaDataEndpointImpl extends 
MetaDataProtocol implements Coprocesso
                     if(dataTableKey != null) {
                         metaDataCache.invalidate(new 
ImmutableBytesPtr(dataTableKey));
                     }
+                    if (setRowKeyOrderOptimizableCell) {
+                        returnTable = doGetTable(key, 
HConstants.LATEST_TIMESTAMP, rowLock);
+                    }
                 }
                 // Get client timeStamp from mutations, since it may get 
updated by the
                 // mutateRowsWithLocks call
                 long currentTime = 
MetaDataUtil.getClientTimeStamp(tableMetadata);
                 
builder.setReturnCode(MetaDataProtos.MutationCode.TABLE_ALREADY_EXISTS);
                 builder.setMutationTime(currentTime);
+                if (returnTable != null) {
+                    builder.setTable(PTableImpl.toProto(returnTable));
+                }
                 done.run(builder.build());
                 return;
             } finally {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/UngroupedAggregateRegionObserver.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/UngroupedAggregateRegionObserver.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/UngroupedAggregateRegionObserver.java
index d5cc486..788a342 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/UngroupedAggregateRegionObserver.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/UngroupedAggregateRegionObserver.java
@@ -28,6 +28,7 @@ import java.io.ByteArrayOutputStream;
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.IOException;
+import java.sql.SQLException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -79,9 +80,12 @@ import org.apache.phoenix.schema.PColumn;
 import org.apache.phoenix.schema.PRow;
 import org.apache.phoenix.schema.PTable;
 import org.apache.phoenix.schema.PTableImpl;
+import org.apache.phoenix.schema.RowKeySchema;
 import org.apache.phoenix.schema.SortOrder;
+import org.apache.phoenix.schema.ValueSchema.Field;
 import org.apache.phoenix.schema.stats.StatisticsCollector;
 import org.apache.phoenix.schema.tuple.MultiKeyValueTuple;
+import org.apache.phoenix.schema.types.PArrayDataType;
 import org.apache.phoenix.schema.types.PDataType;
 import org.apache.phoenix.util.ByteUtil;
 import org.apache.phoenix.util.IndexUtil;
@@ -133,6 +137,7 @@ public class UngroupedAggregateRegionObserver extends 
BaseScannerRegionObserver{
       }
       Mutation[] mutationArray = new Mutation[mutations.size()];
       // TODO: should we use the one that is all or none?
+      logger.warn("Committing bactch of " + mutations.size() + " mutations for 
" + region.getRegionInfo().getTable().getNameAsString());
       region.batchMutate(mutations.toArray(mutationArray), 
HConstants.NO_NONCE, HConstants.NO_NONCE);
     }
 
@@ -157,7 +162,6 @@ public class UngroupedAggregateRegionObserver extends 
BaseScannerRegionObserver{
 
     @Override
     protected RegionScanner doPostScannerOpen(final 
ObserverContext<RegionCoprocessorEnvironment> c, final Scan scan, final 
RegionScanner s) throws IOException {
-        int offset = 0;
         Region region = c.getEnvironment().getRegion();
         long ts = scan.getTimeRange().getMax();
         StatisticsCollector stats = null;
@@ -167,16 +171,33 @@ public class UngroupedAggregateRegionObserver extends 
BaseScannerRegionObserver{
             // Let this throw, as this scan is being done for the sole purpose 
of collecting stats
             stats = new StatisticsCollector(c.getEnvironment(), 
region.getRegionInfo().getTable().getNameAsString(), ts, gp_width_bytes, 
gp_per_region_bytes);
         }
+        int offsetToBe = 0;
         if (ScanUtil.isLocalIndex(scan)) {
             /*
              * For local indexes, we need to set an offset on row key 
expressions to skip
              * the region start key.
              */
-            offset = region.getRegionInfo().getStartKey().length != 0 ? 
region.getRegionInfo().getStartKey().length :
+            offsetToBe = region.getRegionInfo().getStartKey().length != 0 ? 
region.getRegionInfo().getStartKey().length :
                 region.getRegionInfo().getEndKey().length;
-            ScanUtil.setRowKeyOffset(scan, offset);
+            ScanUtil.setRowKeyOffset(scan, offsetToBe);
         }
+        final int offset = offsetToBe;
 
+        PTable projectedTable = null;
+        PTable writeToTable = null;
+        byte[][] values = null;
+        byte[] descRowKeyTableBytes = scan.getAttribute(UPGRADE_DESC_ROW_KEY);
+        boolean isDescRowKeyOrderUpgrade = descRowKeyTableBytes != null;
+        if (isDescRowKeyOrderUpgrade) {
+            logger.warn("Upgrading row key for " + 
region.getRegionInfo().getTable().getNameAsString());
+            projectedTable = deserializeTable(descRowKeyTableBytes);
+            try {
+                writeToTable = PTableImpl.makePTable(projectedTable, true);
+            } catch (SQLException e) {
+                ServerUtil.throwIOException("Upgrade failed", e); // Impossible
+            }
+            values = new byte[projectedTable.getPKColumns().size()][];
+        }
         byte[] localIndexBytes = scan.getAttribute(LOCAL_INDEX_BUILD);
         List<IndexMaintainer> indexMaintainers = localIndexBytes == null ? 
null : IndexMaintainer.deserialize(localIndexBytes);
         List<Mutation> indexMutations = localIndexBytes == null ? 
Collections.<Mutation>emptyList() : 
Lists.<Mutation>newArrayListWithExpectedSize(1024);
@@ -184,22 +205,19 @@ public class UngroupedAggregateRegionObserver extends 
BaseScannerRegionObserver{
         RegionScanner theScanner = s;
 
         byte[] indexUUID = scan.getAttribute(PhoenixIndexCodec.INDEX_UUID);
-        PTable projectedTable = null;
         List<Expression> selectExpressions = null;
         byte[] upsertSelectTable = 
scan.getAttribute(BaseScannerRegionObserver.UPSERT_SELECT_TABLE);
         boolean isUpsert = false;
         boolean isDelete = false;
         byte[] deleteCQ = null;
         byte[] deleteCF = null;
-        byte[][] values = null;
         byte[] emptyCF = null;
-        ImmutableBytesWritable ptr = null;
+        ImmutableBytesWritable ptr = new ImmutableBytesWritable();
         if (upsertSelectTable != null) {
             isUpsert = true;
             projectedTable = deserializeTable(upsertSelectTable);
             selectExpressions = 
deserializeExpressions(scan.getAttribute(BaseScannerRegionObserver.UPSERT_SELECT_EXPRS));
             values = new byte[projectedTable.getPKColumns().size()][];
-            ptr = new ImmutableBytesWritable();
         } else {
             byte[] isDeleteAgg = 
scan.getAttribute(BaseScannerRegionObserver.DELETE_AGG);
             isDelete = isDeleteAgg != null && 
Bytes.compareTo(PDataType.TRUE_BYTES, isDeleteAgg) == 0;
@@ -209,9 +227,6 @@ public class UngroupedAggregateRegionObserver extends 
BaseScannerRegionObserver{
             }
             emptyCF = scan.getAttribute(BaseScannerRegionObserver.EMPTY_CF);
         }
-        if(localIndexBytes != null) {
-            ptr = new ImmutableBytesWritable();
-        }
         TupleProjector tupleProjector = null;
         Region dataRegion = null;
         byte[][] viewConstants = null;
@@ -219,7 +234,7 @@ public class UngroupedAggregateRegionObserver extends 
BaseScannerRegionObserver{
         boolean localIndexScan = ScanUtil.isLocalIndex(scan);
         final TupleProjector p = 
TupleProjector.deserializeProjectorFromScan(scan);
         final HashJoinInfo j = HashJoinInfo.deserializeHashJoinFromScan(scan);
-        if ((localIndexScan && !isDelete) || (j == null && p != null)) {
+        if ((localIndexScan && !isDelete && !isDescRowKeyOrderUpgrade) || (j 
== null && p != null)) {
             if (dataColumns != null) {
                 tupleProjector = IndexUtil.getTupleProjector(scan, 
dataColumns);
                 dataRegion = IndexUtil.getDataRegion(c.getEnvironment());
@@ -238,7 +253,7 @@ public class UngroupedAggregateRegionObserver extends 
BaseScannerRegionObserver{
         int batchSize = 0;
         List<Mutation> mutations = Collections.emptyList();
         boolean buildLocalIndex = indexMaintainers != null && 
dataColumns==null && !localIndexScan;
-        if (isDelete || isUpsert || (deleteCQ != null && deleteCF != null) || 
emptyCF != null || buildLocalIndex) {
+        if (isDescRowKeyOrderUpgrade || isDelete || isUpsert || (deleteCQ != 
null && deleteCF != null) || emptyCF != null || buildLocalIndex) {
             // TODO: size better
             mutations = Lists.newArrayListWithExpectedSize(1024);
             batchSize = 
c.getEnvironment().getConfiguration().getInt(MUTATE_BATCH_SIZE_ATTRIB, 
QueryServicesOptions.DEFAULT_MUTATE_BATCH_SIZE);
@@ -270,7 +285,72 @@ public class UngroupedAggregateRegionObserver extends 
BaseScannerRegionObserver{
                         rowCount++;
                         result.setKeyValues(results);
                         try {
-                            if (buildLocalIndex) {
+                            if (isDescRowKeyOrderUpgrade) {
+                                Arrays.fill(values, null);
+                                Cell firstKV = results.get(0);
+                                RowKeySchema schema = 
projectedTable.getRowKeySchema();
+                                int maxOffset = 
schema.iterator(firstKV.getRowArray(), firstKV.getRowOffset() + offset, 
firstKV.getRowLength(), ptr);
+                                for (int i = 0; i < schema.getFieldCount(); 
i++) {
+                                    Boolean hasValue = schema.next(ptr, i, 
maxOffset);
+                                    if (hasValue == null) {
+                                        break;
+                                    }
+                                    Field field = schema.getField(i);
+                                    // Special case for re-writing DESC ARRAY, 
as the actual byte value needs to change in this case
+                                    if (field.getDataType().isArrayType() && 
field.getSortOrder() == SortOrder.DESC && 
!PArrayDataType.arrayBaseType(field.getDataType()).isFixedWidth()) {
+                                        field.getDataType().coerceBytes(ptr, 
null, field.getDataType(),
+                                                field.getMaxLength(), 
field.getScale(), field.getSortOrder(), 
+                                                field.getMaxLength(), 
field.getScale(), field.getSortOrder(), true); // force to use correct 
separator byte
+                                    }
+                                    values[i] = ptr.copyBytes();
+                                }
+                                writeToTable.newKey(ptr, values);
+                                if (Bytes.compareTo(
+                                        firstKV.getRowArray(), 
firstKV.getRowOffset() + offset, firstKV.getRowLength(), 
+                                        ptr.get(),ptr.getOffset() + 
offset,ptr.getLength()) == 0) {
+                                    continue;
+                                }
+                                byte[] newRow = 
ByteUtil.copyKeyBytesIfNecessary(ptr);
+                                if (offset > 0) { // for local indexes 
(prepend region start key)
+                                    byte[] newRowWithOffset = new byte[offset 
+ newRow.length];
+                                    System.arraycopy(firstKV.getRowArray(), 
firstKV.getRowOffset(), newRowWithOffset, 0, offset);;
+                                    System.arraycopy(newRow, 0, 
newRowWithOffset, offset, newRow.length);
+                                    newRow = newRowWithOffset;
+                                }
+                                byte[] oldRow = 
Bytes.copy(firstKV.getRowArray(), firstKV.getRowOffset(), 
firstKV.getRowLength());
+                                for (Cell cell : results) {
+                                    // Copy existing cell but with new row key
+                                    Cell newCell = new KeyValue(newRow, 0, 
newRow.length,
+                                            cell.getFamilyArray(), 
cell.getFamilyOffset(), cell.getFamilyLength(),
+                                            cell.getQualifierArray(), 
cell.getQualifierOffset(), cell.getQualifierLength(),
+                                            cell.getTimestamp(), 
KeyValue.Type.codeToType(cell.getTypeByte()),
+                                            cell.getValueArray(), 
cell.getValueOffset(), cell.getValueLength());
+                                    switch 
(KeyValue.Type.codeToType(cell.getTypeByte())) {
+                                    case Put:
+                                        // If Put, point delete old Put
+                                        Delete del = new Delete(oldRow);
+                                        del.addDeleteMarker(new 
KeyValue(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength(),
+                                                cell.getFamilyArray(), 
cell.getFamilyOffset(), cell.getFamilyLength(),
+                                                cell.getQualifierArray(), 
cell.getQualifierOffset(),
+                                                cell.getQualifierLength(), 
cell.getTimestamp(), KeyValue.Type.Delete,
+                                                ByteUtil.EMPTY_BYTE_ARRAY, 0, 
0));
+                                        mutations.add(del);
+                                        
+                                        Put put = new Put(newRow);
+                                        put.add(newCell);
+                                        mutations.add(put);
+                                        break;
+                                    case Delete:
+                                    case DeleteColumn:
+                                    case DeleteFamily:
+                                    case DeleteFamilyVersion:
+                                        Delete delete = new Delete(newRow);
+                                        delete.addDeleteMarker(newCell);
+                                        mutations.add(delete);
+                                        break;
+                                    }
+                                }
+                            } else if (buildLocalIndex) {
                                 for (IndexMaintainer maintainer : 
indexMaintainers) {
                                     if (!results.isEmpty()) {
                                         result.getKey(ptr);
@@ -333,7 +413,7 @@ public class UngroupedAggregateRegionObserver extends 
BaseScannerRegionObserver{
                                             expression.getDataType(), 
expression.getMaxLength(),
                                             expression.getScale(), 
expression.getSortOrder(),
                                             column.getMaxLength(), 
column.getScale(),
-                                            column.getSortOrder());
+                                            column.getSortOrder(), 
projectedTable.rowKeyOrderOptimizable());
                                         byte[] bytes = 
ByteUtil.copyKeyBytesIfNecessary(ptr);
                                         row.setValue(column, bytes);
                                     }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PTableProtos.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PTableProtos.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PTableProtos.java
index dd6e303..5b98b5e 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PTableProtos.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PTableProtos.java
@@ -3118,6 +3118,16 @@ public final class PTableProtos {
      * <code>optional int32 baseColumnCount = 25;</code>
      */
     int getBaseColumnCount();
+
+    // optional bool rowKeyOrderOptimizable = 26;
+    /**
+     * <code>optional bool rowKeyOrderOptimizable = 26;</code>
+     */
+    boolean hasRowKeyOrderOptimizable();
+    /**
+     * <code>optional bool rowKeyOrderOptimizable = 26;</code>
+     */
+    boolean getRowKeyOrderOptimizable();
   }
   /**
    * Protobuf type {@code PTable}
@@ -3313,6 +3323,11 @@ public final class PTableProtos {
               baseColumnCount_ = input.readInt32();
               break;
             }
+            case 208: {
+              bitField0_ |= 0x00200000;
+              rowKeyOrderOptimizable_ = input.readBool();
+              break;
+            }
           }
         }
       } catch (com.google.protobuf.InvalidProtocolBufferException e) {
@@ -3859,6 +3874,22 @@ public final class PTableProtos {
       return baseColumnCount_;
     }
 
+    // optional bool rowKeyOrderOptimizable = 26;
+    public static final int ROWKEYORDEROPTIMIZABLE_FIELD_NUMBER = 26;
+    private boolean rowKeyOrderOptimizable_;
+    /**
+     * <code>optional bool rowKeyOrderOptimizable = 26;</code>
+     */
+    public boolean hasRowKeyOrderOptimizable() {
+      return ((bitField0_ & 0x00200000) == 0x00200000);
+    }
+    /**
+     * <code>optional bool rowKeyOrderOptimizable = 26;</code>
+     */
+    public boolean getRowKeyOrderOptimizable() {
+      return rowKeyOrderOptimizable_;
+    }
+
     private void initFields() {
       schemaNameBytes_ = com.google.protobuf.ByteString.EMPTY;
       tableNameBytes_ = com.google.protobuf.ByteString.EMPTY;
@@ -3885,6 +3916,7 @@ public final class PTableProtos {
       statsTimeStamp_ = 0L;
       storeNulls_ = false;
       baseColumnCount_ = 0;
+      rowKeyOrderOptimizable_ = false;
     }
     private byte memoizedIsInitialized = -1;
     public final boolean isInitialized() {
@@ -4027,6 +4059,9 @@ public final class PTableProtos {
       if (((bitField0_ & 0x00100000) == 0x00100000)) {
         output.writeInt32(25, baseColumnCount_);
       }
+      if (((bitField0_ & 0x00200000) == 0x00200000)) {
+        output.writeBool(26, rowKeyOrderOptimizable_);
+      }
       getUnknownFields().writeTo(output);
     }
 
@@ -4141,6 +4176,10 @@ public final class PTableProtos {
         size += com.google.protobuf.CodedOutputStream
           .computeInt32Size(25, baseColumnCount_);
       }
+      if (((bitField0_ & 0x00200000) == 0x00200000)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeBoolSize(26, rowKeyOrderOptimizable_);
+      }
       size += getUnknownFields().getSerializedSize();
       memoizedSerializedSize = size;
       return size;
@@ -4277,6 +4316,11 @@ public final class PTableProtos {
         result = result && (getBaseColumnCount()
             == other.getBaseColumnCount());
       }
+      result = result && (hasRowKeyOrderOptimizable() == 
other.hasRowKeyOrderOptimizable());
+      if (hasRowKeyOrderOptimizable()) {
+        result = result && (getRowKeyOrderOptimizable()
+            == other.getRowKeyOrderOptimizable());
+      }
       result = result &&
           getUnknownFields().equals(other.getUnknownFields());
       return result;
@@ -4390,6 +4434,10 @@ public final class PTableProtos {
         hash = (37 * hash) + BASECOLUMNCOUNT_FIELD_NUMBER;
         hash = (53 * hash) + getBaseColumnCount();
       }
+      if (hasRowKeyOrderOptimizable()) {
+        hash = (37 * hash) + ROWKEYORDEROPTIMIZABLE_FIELD_NUMBER;
+        hash = (53 * hash) + hashBoolean(getRowKeyOrderOptimizable());
+      }
       hash = (29 * hash) + getUnknownFields().hashCode();
       memoizedHashCode = hash;
       return hash;
@@ -4564,6 +4612,8 @@ public final class PTableProtos {
         bitField0_ = (bitField0_ & ~0x00800000);
         baseColumnCount_ = 0;
         bitField0_ = (bitField0_ & ~0x01000000);
+        rowKeyOrderOptimizable_ = false;
+        bitField0_ = (bitField0_ & ~0x02000000);
         return this;
       }
 
@@ -4708,6 +4758,10 @@ public final class PTableProtos {
           to_bitField0_ |= 0x00100000;
         }
         result.baseColumnCount_ = baseColumnCount_;
+        if (((from_bitField0_ & 0x02000000) == 0x02000000)) {
+          to_bitField0_ |= 0x00200000;
+        }
+        result.rowKeyOrderOptimizable_ = rowKeyOrderOptimizable_;
         result.bitField0_ = to_bitField0_;
         onBuilt();
         return result;
@@ -4877,6 +4931,9 @@ public final class PTableProtos {
         if (other.hasBaseColumnCount()) {
           setBaseColumnCount(other.getBaseColumnCount());
         }
+        if (other.hasRowKeyOrderOptimizable()) {
+          setRowKeyOrderOptimizable(other.getRowKeyOrderOptimizable());
+        }
         this.mergeUnknownFields(other.getUnknownFields());
         return this;
       }
@@ -6514,6 +6571,39 @@ public final class PTableProtos {
         return this;
       }
 
+      // optional bool rowKeyOrderOptimizable = 26;
+      private boolean rowKeyOrderOptimizable_ ;
+      /**
+       * <code>optional bool rowKeyOrderOptimizable = 26;</code>
+       */
+      public boolean hasRowKeyOrderOptimizable() {
+        return ((bitField0_ & 0x02000000) == 0x02000000);
+      }
+      /**
+       * <code>optional bool rowKeyOrderOptimizable = 26;</code>
+       */
+      public boolean getRowKeyOrderOptimizable() {
+        return rowKeyOrderOptimizable_;
+      }
+      /**
+       * <code>optional bool rowKeyOrderOptimizable = 26;</code>
+       */
+      public Builder setRowKeyOrderOptimizable(boolean value) {
+        bitField0_ |= 0x02000000;
+        rowKeyOrderOptimizable_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>optional bool rowKeyOrderOptimizable = 26;</code>
+       */
+      public Builder clearRowKeyOrderOptimizable() {
+        bitField0_ = (bitField0_ & ~0x02000000);
+        rowKeyOrderOptimizable_ = false;
+        onChanged();
+        return this;
+      }
+
       // @@protoc_insertion_point(builder_scope:PTable)
     }
 
@@ -6560,7 +6650,7 @@ public final class PTableProtos {
       "values\030\002 \003(\014\022\033\n\023guidePostsByteCount\030\003 
\001(" +
       "\003\022\025\n\rkeyBytesCount\030\004 
\001(\003\022\027\n\017guidePostsCo",
       "unt\030\005 \001(\005\022!\n\013pGuidePosts\030\006 
\001(\0132\014.PGuideP" +
-      "osts\"\317\004\n\006PTable\022\027\n\017schemaNameBytes\030\001 
\002(\014" +
+      "osts\"\357\004\n\006PTable\022\027\n\017schemaNameBytes\030\001 
\002(\014" +
       "\022\026\n\016tableNameBytes\030\002 
\002(\014\022\036\n\ttableType\030\003 " +
       "\002(\0162\013.PTableType\022\022\n\nindexState\030\004 
\001(\t\022\026\n\016" +
       "sequenceNumber\030\005 \002(\003\022\021\n\ttimeStamp\030\006 
\002(\003\022" +
@@ -6574,11 +6664,12 @@ public final class PTableProtos {
       "nt\030\022 \001(\014\022\025\n\rphysicalNames\030\023 
\003(\014\022\020\n\010tenan" +
       "tId\030\024 \001(\014\022\023\n\013viewIndexId\030\025 
\001(\005\022\021\n\tindexT" +
       "ype\030\026 \001(\014\022\026\n\016statsTimeStamp\030\027 
\001(\003\022\022\n\nsto" +
-      "reNulls\030\030 \001(\010\022\027\n\017baseColumnCount\030\031 
\001(\005*A" +
-      
"\n\nPTableType\022\n\n\006SYSTEM\020\000\022\010\n\004USER\020\001\022\010\n\004VI"
 +
-      
"EW\020\002\022\t\n\005INDEX\020\003\022\010\n\004JOIN\020\004B@\n(org.apache." 
+
-      "phoenix.coprocessor.generatedB\014PTablePro" +
-      "tosH\001\210\001\001\240\001\001"
+      "reNulls\030\030 \001(\010\022\027\n\017baseColumnCount\030\031 
\001(\005\022\036" +
+      "\n\026rowKeyOrderOptimizable\030\032 \001(\010*A\n\nPTable" +
+      
"Type\022\n\n\006SYSTEM\020\000\022\010\n\004USER\020\001\022\010\n\004VIEW\020\002\022\t\n\005"
 +
+      "INDEX\020\003\022\010\n\004JOIN\020\004B@\n(org.apache.phoenix." +
+      "coprocessor.generatedB\014PTableProtosH\001\210\001\001",
+      "\240\001\001"
     };
     com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner 
assigner =
       new 
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
@@ -6602,7 +6693,7 @@ public final class PTableProtos {
           internal_static_PTable_fieldAccessorTable = new
             com.google.protobuf.GeneratedMessage.FieldAccessorTable(
               internal_static_PTable_descriptor,
-              new java.lang.String[] { "SchemaNameBytes", "TableNameBytes", 
"TableType", "IndexState", "SequenceNumber", "TimeStamp", "PkNameBytes", 
"BucketNum", "Columns", "Indexes", "IsImmutableRows", "GuidePosts", 
"DataTableNameBytes", "DefaultFamilyName", "DisableWAL", "MultiTenant", 
"ViewType", "ViewStatement", "PhysicalNames", "TenantId", "ViewIndexId", 
"IndexType", "StatsTimeStamp", "StoreNulls", "BaseColumnCount", });
+              new java.lang.String[] { "SchemaNameBytes", "TableNameBytes", 
"TableType", "IndexState", "SequenceNumber", "TimeStamp", "PkNameBytes", 
"BucketNum", "Columns", "Indexes", "IsImmutableRows", "GuidePosts", 
"DataTableNameBytes", "DefaultFamilyName", "DisableWAL", "MultiTenant", 
"ViewType", "ViewStatement", "PhysicalNames", "TenantId", "ViewIndexId", 
"IndexType", "StatsTimeStamp", "StoreNulls", "BaseColumnCount", 
"RowKeyOrderOptimizable", });
           return null;
         }
       };

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java 
b/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java
index 195450c..acc3c86 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java
@@ -249,6 +249,7 @@ public enum SQLExceptionCode {
     NO_LOCAL_INDEXES(1054, "43A11", "Local secondary indexes are not supported 
for HBase versions " + 
         
MetaDataUtil.decodeHBaseVersionAsString(PhoenixDatabaseMetaData.MIN_LOCAL_SI_VERSION_DISALLOW)
 + " through " + 
MetaDataUtil.decodeHBaseVersionAsString(PhoenixDatabaseMetaData.MAX_LOCAL_SI_VERSION_DISALLOW)
 + " inclusive."),
     UNALLOWED_LOCAL_INDEXES(1055, "43A12", "Local secondary indexes are 
configured to not be allowed."),
+    DESC_VARBINARY_NOT_SUPPORTED(1056, "43A13", "Descending VARBINARY columns 
not supported"),
 
     /** Sequence related */
     SEQUENCE_ALREADY_EXIST(1200, "42Z00", "Sequence already exists.", new 
Factory() {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/execute/BaseQueryPlan.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/execute/BaseQueryPlan.java 
b/phoenix-core/src/main/java/org/apache/phoenix/execute/BaseQueryPlan.java
index 5739e3e..11da1c9 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/execute/BaseQueryPlan.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/execute/BaseQueryPlan.java
@@ -32,6 +32,7 @@ import org.apache.hadoop.hbase.client.Scan;
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.hadoop.io.WritableUtils;
+import org.apache.htrace.TraceScope;
 import org.apache.phoenix.compile.ExplainPlan;
 import org.apache.phoenix.compile.FromCompiler;
 import org.apache.phoenix.compile.GroupByCompiler.GroupBy;
@@ -68,7 +69,6 @@ import org.apache.phoenix.util.LogUtil;
 import org.apache.phoenix.util.SQLCloseable;
 import org.apache.phoenix.util.SQLCloseables;
 import org.apache.phoenix.util.ScanUtil;
-import org.apache.htrace.TraceScope;
 
 import com.google.common.collect.Lists;
 
@@ -156,6 +156,7 @@ public abstract class BaseQueryPlan implements QueryPlan {
         return iterator(Collections.<SQLCloseable>emptyList(), scanGrouper);
     }
     
+    @Override
     public final ResultIterator iterator() throws SQLException {
         return iterator(Collections.<SQLCloseable>emptyList(), 
DefaultParallelScanGrouper.getInstance());
     }
@@ -172,6 +173,7 @@ public abstract class BaseQueryPlan implements QueryPlan {
         // Set miscellaneous scan attributes. This is the last chance to set 
them before we
         // clone the scan for each parallelized chunk.
         Scan scan = context.getScan();
+        PTable table = context.getCurrentTable().getTable();
         
         if (OrderBy.REV_ROW_KEY_ORDER_BY.equals(orderBy)) {
             ScanUtil.setReversed(scan);
@@ -190,8 +192,7 @@ public abstract class BaseQueryPlan implements QueryPlan {
         PhoenixConnection connection = context.getConnection();
 
         // set read consistency
-        if (context.getCurrentTable() != null
-                && context.getCurrentTable().getTable().getType() != 
PTableType.SYSTEM) {
+        if (table.getType() != PTableType.SYSTEM) {
             scan.setConsistency(connection.getConsistency());
         }
         if (context.getScanTimeRange() == null) {
@@ -203,11 +204,12 @@ public abstract class BaseQueryPlan implements QueryPlan {
         } else {
             ScanUtil.setTimeRange(scan, context.getScanTimeRange());
         }
+        
         ScanUtil.setTenantId(scan, connection.getTenantId() == null ? null : 
connection.getTenantId().getBytes());
         String customAnnotations = 
LogUtil.customAnnotationsToString(connection);
         ScanUtil.setCustomAnnotations(scan, customAnnotations == null ? null : 
customAnnotations.getBytes());
         // Set local index related scan attributes. 
-        if (context.getCurrentTable().getTable().getIndexType() == 
IndexType.LOCAL) {
+        if (table.getIndexType() == IndexType.LOCAL) {
             ScanUtil.setLocalIndex(scan);
             Set<PColumn> dataColumns = context.getDataColumns();
             // If any data columns to join back from data table are present 
then we set following attributes
@@ -221,8 +223,8 @@ public abstract class BaseQueryPlan implements QueryPlan {
                 KeyValueSchema schema = 
ProjectedColumnExpression.buildSchema(dataColumns);
                 // Set key value schema of the data columns.
                 serializeSchemaIntoScan(scan, schema);
-                String parentSchema = 
context.getCurrentTable().getTable().getParentSchemaName().getString();
-                String parentTable = 
context.getCurrentTable().getTable().getParentTableName().getString();
+                String parentSchema = table.getParentSchemaName().getString();
+                String parentTable = table.getParentTableName().getString();
                 final ParseNodeFactory FACTORY = new ParseNodeFactory();
                 TableRef dataTableRef =
                         FromCompiler.getResolver(

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/execute/DescVarLengthFastByteComparisons.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/execute/DescVarLengthFastByteComparisons.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/execute/DescVarLengthFastByteComparisons.java
new file mode 100644
index 0000000..40960e0
--- /dev/null
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/execute/DescVarLengthFastByteComparisons.java
@@ -0,0 +1,219 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.execute;
+
+import java.lang.reflect.Field;
+import java.nio.ByteOrder;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import sun.misc.Unsafe;
+
+import com.google.common.primitives.Longs;
+import com.google.common.primitives.UnsignedBytes;
+
+/**
+ * Utility code to do optimized byte-array comparison.
+ * This is borrowed from org.apache.hadoop.io.FastByteComparisons
+ * which was borrowed and slightly modified from Guava's {@link UnsignedBytes}
+ * class to be able to compare arrays that start at non-zero offsets.
+ * 
+ * The only difference is that we sort a smaller length bytes as *larger*
+ * than longer length bytes when all the bytes are the same.
+ */
+@SuppressWarnings("restriction")
+public class DescVarLengthFastByteComparisons {
+
+    private DescVarLengthFastByteComparisons() {}
+
+    /**
+     * Lexicographically compare two byte arrays.
+     */
+    public static int compareTo(byte[] b1, int s1, int l1, byte[] b2, int s2, 
int l2) {
+        return LexicographicalComparerHolder.BEST_COMPARER.compareTo(b1, s1, 
l1, b2, s2, l2);
+    }
+
+    private interface Comparer<T> {
+        abstract public int compareTo(T buffer1, int offset1, int length1, T 
buffer2, int offset2, int length2);
+    }
+
+    private static Comparer<byte[]> lexicographicalComparerJavaImpl() {
+        return LexicographicalComparerHolder.PureJavaComparer.INSTANCE;
+    }
+
+    /**
+     * Provides a lexicographical comparer implementation; either a Java 
implementation or a faster implementation based
+     * on {@link Unsafe}.
+     * <p>
+     * Uses reflection to gracefully fall back to the Java implementation if 
{@code Unsafe} isn't available.
+     */
+    private static class LexicographicalComparerHolder {
+        static final String UNSAFE_COMPARER_NAME = 
LexicographicalComparerHolder.class.getName() + "$UnsafeComparer";
+
+        static final Comparer<byte[]> BEST_COMPARER = getBestComparer();
+
+        /**
+         * Returns the Unsafe-using Comparer, or falls back to the pure-Java 
implementation if unable to do so.
+         */
+        static Comparer<byte[]> getBestComparer() {
+            try {
+                Class<?> theClass = Class.forName(UNSAFE_COMPARER_NAME);
+
+                // yes, UnsafeComparer does implement Comparer<byte[]>
+                @SuppressWarnings("unchecked")
+                Comparer<byte[]> comparer = 
(Comparer<byte[]>)theClass.getEnumConstants()[0];
+                return comparer;
+            } catch (Throwable t) { // ensure we really catch *everything*
+                return lexicographicalComparerJavaImpl();
+            }
+        }
+
+        private enum PureJavaComparer implements Comparer<byte[]> {
+            INSTANCE;
+
+            @Override
+            public int compareTo(byte[] buffer1, int offset1, int length1, 
byte[] buffer2, int offset2, int length2) {
+                // Short circuit equal case
+                if (buffer1 == buffer2 && offset1 == offset2 && length1 == 
length2) { return 0; }
+                // Bring WritableComparator code local
+                int end1 = offset1 + length1;
+                int end2 = offset2 + length2;
+                for (int i = offset1, j = offset2; i < end1 && j < end2; i++, 
j++) {
+                    int a = (buffer1[i] & 0xff);
+                    int b = (buffer2[j] & 0xff);
+                    if (a != b) { return a - b; }
+                }
+                return length2 - length1;
+            }
+        }
+
+        @SuppressWarnings("unused")
+        // used via reflection
+        private enum UnsafeComparer implements Comparer<byte[]> {
+            INSTANCE;
+
+            static final Unsafe theUnsafe;
+
+            /** The offset to the first element in a byte array. */
+            static final int BYTE_ARRAY_BASE_OFFSET;
+
+            static {
+                theUnsafe = (Unsafe)AccessController.doPrivileged(new 
PrivilegedAction<Object>() {
+                    @Override
+                    public Object run() {
+                        try {
+                            Field f = 
Unsafe.class.getDeclaredField("theUnsafe");
+                            f.setAccessible(true);
+                            return f.get(null);
+                        } catch (NoSuchFieldException e) {
+                            // It doesn't matter what we throw;
+                            // it's swallowed in getBestComparer().
+                            throw new Error();
+                        } catch (IllegalAccessException e) {
+                            throw new Error();
+                        }
+                    }
+                });
+
+                BYTE_ARRAY_BASE_OFFSET = 
theUnsafe.arrayBaseOffset(byte[].class);
+
+                // sanity check - this should never fail
+                if (theUnsafe.arrayIndexScale(byte[].class) != 1) { throw new 
AssertionError(); }
+            }
+
+            static final boolean littleEndian = 
ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN);
+
+            /**
+             * Returns true if x1 is less than x2, when both values are 
treated as unsigned.
+             */
+            static boolean lessThanUnsigned(long x1, long x2) {
+                return (x1 + Long.MIN_VALUE) < (x2 + Long.MIN_VALUE);
+            }
+
+            /**
+             * Lexicographically compare two arrays.
+             *
+             * @param buffer1
+             *            left operand
+             * @param buffer2
+             *            right operand
+             * @param offset1
+             *            Where to start comparing in the left buffer
+             * @param offset2
+             *            Where to start comparing in the right buffer
+             * @param length1
+             *            How much to compare from the left buffer
+             * @param length2
+             *            How much to compare from the right buffer
+             * @return 0 if equal, < 0 if left is less than right, etc.
+             */
+            @Override
+            public int compareTo(byte[] buffer1, int offset1, int length1, 
byte[] buffer2, int offset2, int length2) {
+                // Short circuit equal case
+                if (buffer1 == buffer2 && offset1 == offset2 && length1 == 
length2) { return 0; }
+                int minLength = Math.min(length1, length2);
+                int minWords = minLength / Longs.BYTES;
+                int offset1Adj = offset1 + BYTE_ARRAY_BASE_OFFSET;
+                int offset2Adj = offset2 + BYTE_ARRAY_BASE_OFFSET;
+
+                /*
+                 * Compare 8 bytes at a time. Benchmarking shows comparing 8 
bytes at a time is no slower than comparing
+                 * 4 bytes at a time even on 32-bit. On the other hand, it is 
substantially faster on 64-bit.
+                 */
+                for (int i = 0; i < minWords * Longs.BYTES; i += Longs.BYTES) {
+                    long lw = theUnsafe.getLong(buffer1, offset1Adj + (long)i);
+                    long rw = theUnsafe.getLong(buffer2, offset2Adj + (long)i);
+                    long diff = lw ^ rw;
+
+                    if (diff != 0) {
+                        if (!littleEndian) { return lessThanUnsigned(lw, rw) ? 
-1 : 1; }
+
+                        // Use binary search
+                        int n = 0;
+                        int y;
+                        int x = (int)diff;
+                        if (x == 0) {
+                            x = (int)(diff >>> 32);
+                            n = 32;
+                        }
+
+                        y = x << 16;
+                        if (y == 0) {
+                            n += 16;
+                        } else {
+                            x = y;
+                        }
+
+                        y = x << 8;
+                        if (y == 0) {
+                            n += 8;
+                        }
+                        return (int)(((lw >>> n) & 0xFFL) - ((rw >>> n) & 
0xFFL));
+                    }
+                }
+
+                // The epilogue to cover the last (minLength % 8) elements.
+                for (int i = minWords * Longs.BYTES; i < minLength; i++) {
+                    int result = UnsignedBytes.compare(buffer1[offset1 + i], 
buffer2[offset2 + i]);
+                    if (result != 0) { return result; }
+                }
+                return length2 - length1;
+            }
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/expression/ArrayConstructorExpression.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/expression/ArrayConstructorExpression.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/expression/ArrayConstructorExpression.java
index da581ce..d1b4a0e 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/expression/ArrayConstructorExpression.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/expression/ArrayConstructorExpression.java
@@ -123,7 +123,7 @@ public class ArrayConstructorExpression extends 
BaseCompoundExpression {
             if (position >= 0) position = elements.length;
             if (!baseType.isFixedWidth()) {
                 // Double seperator byte to show end of the non null array
-                PArrayDataType.writeEndSeperatorForVarLengthArray(oStream);
+                PArrayDataType.writeEndSeperatorForVarLengthArray(oStream, 
getSortOrder());
                 noOfElements = 
PArrayDataType.serailizeOffsetArrayIntoStream(oStream, byteStream, noOfElements,
                         offsetPos[offsetPos.length - 1], offsetPos);
                 PArrayDataType.serializeHeaderInfoIntoStream(oStream, 
noOfElements);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/expression/OrderByExpression.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/expression/OrderByExpression.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/expression/OrderByExpression.java
index 14bba68..456e58b 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/expression/OrderByExpression.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/expression/OrderByExpression.java
@@ -26,6 +26,7 @@ import java.io.IOException;
 
 import org.apache.hadoop.io.Writable;
 import org.apache.hadoop.io.WritableUtils;
+import org.apache.phoenix.schema.SortOrder;
 
 /**
  * A container for a column that appears in ORDER BY clause.
@@ -83,7 +84,17 @@ public class OrderByExpression implements Writable {
     
     @Override
     public String toString() {
-        return this.getExpression() + (isAscending ? "" : " DESC") + 
(isNullsLast ? " NULLS LAST" : "");
+        Expression e = this.getExpression();
+        boolean isNullsLast = this.isNullsLast;
+        boolean isAscending = this.isAscending;
+        // Flip back here based on sort order, as the compiler
+        // flips this, but we want to display the original back
+        // to the user.
+        if (e.getSortOrder() == SortOrder.DESC) {
+            isAscending = !isAscending;
+            isNullsLast = !isNullsLast;
+        }
+        return e + (isAscending ? "" : " DESC") + (isNullsLast ? " NULLS LAST" 
: "");
     }
     
     @Override

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/expression/RowValueConstructorExpression.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/expression/RowValueConstructorExpression.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/expression/RowValueConstructorExpression.java
index 940f909..481368c 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/expression/RowValueConstructorExpression.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/expression/RowValueConstructorExpression.java
@@ -184,7 +184,7 @@ public class RowValueConstructorExpression extends 
BaseCompoundExpression {
                         } else {
                             output.write(tempPtr.get(), tempPtr.getOffset(), 
tempPtr.getLength());
                             if (!childType.isFixedWidth()) {
-                                output.write(QueryConstants.SEPARATOR_BYTE);
+                                output.write(SchemaUtil.getSeparatorByte(true, 
false, child));
                             }
                             if (previousCarryOver) {
                                 previousCarryOver = 
!ByteUtil.previousKey(output.getBuffer(), output.size());
@@ -193,8 +193,12 @@ public class RowValueConstructorExpression extends 
BaseCompoundExpression {
                     }
                     int outputSize = output.size();
                     byte[] outputBytes = output.getBuffer();
+                    // Don't remove trailing separator byte unless it's the 
one for ASC
+                    // as otherwise we need it to ensure sort order is correct
                     for (int k = expressionCount -1 ; 
-                            k >=0 &&  getChildren().get(k).getDataType() != 
null && !getChildren().get(k).getDataType().isFixedWidth() && 
outputBytes[outputSize-1] == QueryConstants.SEPARATOR_BYTE ; k--) {
+                            k >=0 &&  getChildren().get(k).getDataType() != 
null 
+                                  && 
!getChildren().get(k).getDataType().isFixedWidth() 
+                                  && outputBytes[outputSize-1] == 
QueryConstants.SEPARATOR_BYTE ; k--) {
                         outputSize--;
                     }
                     ptr.set(outputBytes, 0, outputSize);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayConcatFunction.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayConcatFunction.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayConcatFunction.java
index d2b846a..77790b9 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayConcatFunction.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayConcatFunction.java
@@ -50,6 +50,7 @@ public class ArrayConcatFunction extends 
ArrayModifierFunction {
         if (!getLHSExpr().evaluate(tuple, ptr)|| ptr.getLength() == 0){
             return false;
         }
+        boolean isLHSRowKeyOrderOptimized = 
PArrayDataType.isRowKeyOrderOptimized(getLHSExpr().getDataType(), 
getLHSExpr().getSortOrder(), ptr);
 
         int actualLengthOfArray1 = Math.abs(PArrayDataType.getArrayLength(ptr, 
getLHSBaseType(), getLHSExpr().getMaxLength()));
         int lengthArray1 = ptr.getLength();
@@ -62,8 +63,13 @@ public class ArrayConcatFunction extends 
ArrayModifierFunction {
 
         checkSizeCompatibility(ptr, getLHSExpr(), getLHSExpr().getDataType(), 
getRHSExpr(),getRHSExpr().getDataType());
 
-        // Coerce array2 to array1 type
-        coerceBytes(ptr, getLHSExpr(), getLHSExpr().getDataType(), 
getRHSExpr(),getRHSExpr().getDataType());
+        // FIXME: calling version of coerceBytes that takes into account the 
separator used by LHS
+        // If the RHS does not have the same separator, it'll be coerced to 
use it. It's unclear
+        // if we should do the same for all classes derived from the base 
class.
+        // Coerce RHS to LHS type
+        getLHSExpr().getDataType().coerceBytes(ptr, null, 
getRHSExpr().getDataType(), getRHSExpr().getMaxLength(),
+                getRHSExpr().getScale(), getRHSExpr().getSortOrder(), 
getLHSExpr().getMaxLength(),
+                getLHSExpr().getScale(), getLHSExpr().getSortOrder(), 
isLHSRowKeyOrderOptimized);
         return modifierFunction(ptr, lengthArray1, offsetArray1, array1Bytes, 
getLHSBaseType(), actualLengthOfArray1, getMaxLength(), getLHSExpr());
     }
 
@@ -72,6 +78,7 @@ public class ArrayConcatFunction extends 
ArrayModifierFunction {
                                        byte[] array1Bytes, PDataType 
baseDataType, int actualLengthOfArray1, Integer maxLength,
                                        Expression array1Exp) {
         int actualLengthOfArray2 = Math.abs(PArrayDataType.getArrayLength(ptr, 
baseDataType, array1Exp.getMaxLength()));
+        // FIXME: concatArrays will be fine if it's copying the separator 
bytes, including the terminating bytes.
         return PArrayDataType.concatArrays(ptr, len, offset, array1Bytes, 
baseDataType, actualLengthOfArray1, actualLengthOfArray2);
     }
 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayModifierFunction.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayModifierFunction.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayModifierFunction.java
index 3177c29..9bd7372 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayModifierFunction.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayModifierFunction.java
@@ -27,7 +27,8 @@ import org.apache.phoenix.expression.LiteralExpression;
 import org.apache.phoenix.schema.SortOrder;
 import org.apache.phoenix.schema.TypeMismatchException;
 import org.apache.phoenix.schema.tuple.Tuple;
-import org.apache.phoenix.schema.types.*;
+import org.apache.phoenix.schema.types.PArrayDataType;
+import org.apache.phoenix.schema.types.PDataType;
 
 public abstract class ArrayModifierFunction extends ScalarFunction {
 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/expression/function/LpadFunction.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/LpadFunction.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/LpadFunction.java
index 9dfc8fc..eee03bf 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/LpadFunction.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/LpadFunction.java
@@ -22,12 +22,12 @@ import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.phoenix.expression.Expression;
 import org.apache.phoenix.parse.FunctionParseNode.Argument;
 import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunction;
+import org.apache.phoenix.schema.SortOrder;
+import org.apache.phoenix.schema.tuple.Tuple;
 import org.apache.phoenix.schema.types.PChar;
-import org.apache.phoenix.schema.types.PInteger;
 import org.apache.phoenix.schema.types.PDataType;
+import org.apache.phoenix.schema.types.PInteger;
 import org.apache.phoenix.schema.types.PVarchar;
-import org.apache.phoenix.schema.SortOrder;
-import org.apache.phoenix.schema.tuple.Tuple;
 import org.apache.phoenix.util.StringUtil;
 
 /**
@@ -146,7 +146,7 @@ public class LpadFunction extends ScalarFunction {
         int numFillCharsPrepended = padLen % fillLen;
 
         // byte length of the input string
-        int strByteLength = getSubstringByteLength(ptr, ptr.getLength(), 
strSortOrder, isStrCharType);
+        int strByteLength = ptr.getLength();
         // byte length of the fill string
         int fillByteLength = getSubstringByteLength(fillPtr, 
fillPtr.getLength(), fillSortOrder, isFillCharType);
         // byte length of the full fills to be prepended

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/expression/util/regex/JONIPattern.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/expression/util/regex/JONIPattern.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/expression/util/regex/JONIPattern.java
index 69f9eaf..af5bc2b 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/expression/util/regex/JONIPattern.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/expression/util/regex/JONIPattern.java
@@ -158,8 +158,9 @@ public class JONIPattern extends AbstractBasePattern 
implements AbstractBaseSpli
 
     private boolean
             split(byte[] srcBytes, int srcOffset, int srcLen, 
ImmutableBytesWritable outPtr) {
+        SortOrder sortOrder = SortOrder.ASC;
         PArrayDataTypeBytesArrayBuilder builder =
-                new PArrayDataTypeBytesArrayBuilder(PVarchar.INSTANCE, 
SortOrder.ASC);
+                new PArrayDataTypeBytesArrayBuilder(PVarchar.INSTANCE, 
sortOrder);
         int srcRange = srcOffset + srcLen;
         Matcher matcher = pattern.matcher(srcBytes, 0, srcRange);
         int cur = srcOffset;
@@ -191,7 +192,7 @@ public class JONIPattern extends AbstractBasePattern 
implements AbstractBaseSpli
                 break;
             }
         }
-        byte[] bytes = builder.getBytesAndClose();
+        byte[] bytes = builder.getBytesAndClose(SortOrder.ASC);
         if (bytes == null) return false;
         outPtr.set(bytes);
         return true;

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/filter/SkipScanFilter.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/filter/SkipScanFilter.java 
b/phoenix-core/src/main/java/org/apache/phoenix/filter/SkipScanFilter.java
index 1923856..4dc888d 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/filter/SkipScanFilter.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/filter/SkipScanFilter.java
@@ -37,7 +37,6 @@ import org.apache.hadoop.hbase.util.Writables;
 import org.apache.hadoop.io.Writable;
 import org.apache.phoenix.query.KeyRange;
 import org.apache.phoenix.query.KeyRange.Bound;
-import org.apache.phoenix.query.QueryConstants;
 import org.apache.phoenix.schema.RowKeySchema;
 import org.apache.phoenix.util.ByteUtil;
 import org.apache.phoenix.util.ScanUtil;
@@ -517,7 +516,7 @@ public class SkipScanFilter extends FilterBase implements 
Writable {
         startKeyLength = length;
         // Add separator byte if we're at the end of the buffer, since 
trailing separator bytes are stripped
         if (ptr.getOffset() + ptr.getLength() == offset + length && i-1 > 0 && 
!schema.getField(i-1).getDataType().isFixedWidth()) {
-            startKey[startKeyLength++] = QueryConstants.SEPARATOR_BYTE;
+            startKey[startKeyLength++] = 
SchemaUtil.getSeparatorByte(schema.rowKeyOrderOptimizable(), 
ptr.getLength()==0, schema.getField(i-1));
         }
         startKeyLength += setKey(Bound.LOWER, startKey, startKeyLength, i);
         return length;

Reply via email to