You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@phoenix.apache.org by ma...@apache.org on 2015/07/20 19:14:31 UTC

[01/50] [abbrv] phoenix git commit: PHOENIX-2036 - PhoenixConfigurationUtil should provide a pre-normalize table name to PhoenixRuntime

Repository: phoenix
Updated Branches:
  refs/heads/calcite 0641043a4 -> 76e92a961


PHOENIX-2036 - PhoenixConfigurationUtil should provide a pre-normalize table name to PhoenixRuntime


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/be5aba5b
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/be5aba5b
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/be5aba5b

Branch: refs/heads/calcite
Commit: be5aba5bcdbbf28c4faa04414e9e4f4079276614
Parents: 1c10fda
Author: ravimagham <ra...@apache.org>
Authored: Sat Jul 4 08:11:25 2015 -0700
Committer: ravimagham <ra...@apache.org>
Committed: Sat Jul 4 08:11:25 2015 -0700

----------------------------------------------------------------------
 .../org/apache/phoenix/util/PhoenixRuntime.java |  3 +-
 .../java/org/apache/phoenix/util/QueryUtil.java |  4 +-
 .../org/apache/phoenix/util/SchemaUtil.java     | 18 ++++++-
 .../phoenix/pig/PhoenixHBaseLoaderIT.java       |  9 ++--
 .../phoenix/pig/PhoenixHBaseStorerIT.java       |  4 +-
 .../apache/phoenix/pig/PhoenixHBaseStorage.java | 51 ++++++++------------
 6 files changed, 47 insertions(+), 42 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/be5aba5b/phoenix-core/src/main/java/org/apache/phoenix/util/PhoenixRuntime.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/PhoenixRuntime.java b/phoenix-core/src/main/java/org/apache/phoenix/util/PhoenixRuntime.java
index 4347acd..92bb1d8 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/util/PhoenixRuntime.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/util/PhoenixRuntime.java
@@ -354,8 +354,7 @@ public class PhoenixRuntime {
     public static List<ColumnInfo> generateColumnInfo(Connection conn,
             String tableName, List<String> columns)
             throws SQLException {
-
-        PTable table = PhoenixRuntime.getTable(conn, tableName);
+        PTable table = PhoenixRuntime.getTable(conn, SchemaUtil.normalizeFullTableName(tableName));
         List<ColumnInfo> columnInfoList = Lists.newArrayList();
         Set<String> unresolvedColumnNames = new TreeSet<String>();
         if (columns == null) {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/be5aba5b/phoenix-core/src/main/java/org/apache/phoenix/util/QueryUtil.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/QueryUtil.java b/phoenix-core/src/main/java/org/apache/phoenix/util/QueryUtil.java
index bc2141c..d7f9cea 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/util/QueryUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/util/QueryUtil.java
@@ -201,8 +201,6 @@ public final class QueryUtil {
         if(columnInfos == null || columnInfos.isEmpty()) {
              throw new IllegalArgumentException("At least one column must be provided");
         }
-        // escape the table name to ensure it is case sensitive.
-        final String escapedFullTableName = SchemaUtil.getEscapedFullTableName(fullTableName);
         StringBuilder query = new StringBuilder();
         query.append("SELECT ");
         for (ColumnInfo cinfo : columnInfos) {
@@ -215,7 +213,7 @@ public final class QueryUtil {
         // Remove the trailing comma
         query.setLength(query.length() - 1);
         query.append(" FROM ");
-        query.append(escapedFullTableName);
+        query.append(fullTableName);
         if(conditions != null && conditions.length() > 0) {
             query.append(" WHERE (").append(conditions).append(")");
         }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/be5aba5b/phoenix-core/src/main/java/org/apache/phoenix/util/SchemaUtil.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/SchemaUtil.java b/phoenix-core/src/main/java/org/apache/phoenix/util/SchemaUtil.java
index aff6b51..c674140 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/util/SchemaUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/util/SchemaUtil.java
@@ -21,9 +21,8 @@ import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Strings.isNullOrEmpty;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SYSTEM_CATALOG_NAME_BYTES;
-import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SYSTEM_STATS_NAME_BYTES;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SYSTEM_FUNCTION_NAME_BYTES;
-
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SYSTEM_STATS_NAME_BYTES;
 
 import java.sql.SQLException;
 import java.sql.Statement;
@@ -174,6 +173,21 @@ public class SchemaUtil {
         }
         return name.toUpperCase();
     }
+    
+    /**
+     * Normalizes the fulltableName . Uses {@linkplain normalizeIdentifier}
+     * @param fullTableName
+     * @return
+     */
+    public static String normalizeFullTableName(String fullTableName) {
+        String schemaName = SchemaUtil.getSchemaNameFromFullName(fullTableName);
+        String tableName = SchemaUtil.getTableNameFromFullName(fullTableName);
+        String normalizedTableName = StringUtil.EMPTY_STRING;
+        if(!schemaName.isEmpty()) {
+            normalizedTableName =  normalizeIdentifier(schemaName) + QueryConstants.NAME_SEPARATOR;
+        }
+        return normalizedTableName + normalizeIdentifier(tableName);
+    }
 
     public static boolean isCaseSensitive(String name) {
         return name!=null && name.length() > 0 && name.charAt(0)=='"';

http://git-wip-us.apache.org/repos/asf/phoenix/blob/be5aba5b/phoenix-pig/src/it/java/org/apache/phoenix/pig/PhoenixHBaseLoaderIT.java
----------------------------------------------------------------------
diff --git a/phoenix-pig/src/it/java/org/apache/phoenix/pig/PhoenixHBaseLoaderIT.java b/phoenix-pig/src/it/java/org/apache/phoenix/pig/PhoenixHBaseLoaderIT.java
index 594abe6..7fcf6ac 100644
--- a/phoenix-pig/src/it/java/org/apache/phoenix/pig/PhoenixHBaseLoaderIT.java
+++ b/phoenix-pig/src/it/java/org/apache/phoenix/pig/PhoenixHBaseLoaderIT.java
@@ -74,6 +74,8 @@ public class PhoenixHBaseLoaderIT {
     private static final String TABLE_NAME = "A";
     private static final String INDEX_NAME = "I";
     private static final String TABLE_FULL_NAME = SchemaUtil.getTableName(SCHEMA_NAME, TABLE_NAME);
+    private static final String CASE_SENSITIVE_TABLE_NAME = SchemaUtil.getEscapedArgument("a");
+    private static final String CASE_SENSITIVE_TABLE_FULL_NAME = SchemaUtil.getTableName(SCHEMA_NAME,CASE_SENSITIVE_TABLE_NAME);
     private static HBaseTestingUtility hbaseTestUtil;
     private static String zkQuorum;
     private static Connection conn;
@@ -234,13 +236,13 @@ public class PhoenixHBaseLoaderIT {
     public void testDataForTable() throws Exception {
         
          //create the table
-         String ddl = "CREATE TABLE  " + TABLE_FULL_NAME 
+         String ddl = "CREATE TABLE  " + CASE_SENSITIVE_TABLE_FULL_NAME 
                 + "  (ID  INTEGER NOT NULL PRIMARY KEY, NAME VARCHAR, AGE INTEGER) ";
                 
         conn.createStatement().execute(ddl);
         
         //prepare data with 10 rows having age 25 and the other 30.
-        final String dml = "UPSERT INTO " + TABLE_FULL_NAME + " VALUES(?,?,?)";
+        final String dml = "UPSERT INTO " + CASE_SENSITIVE_TABLE_FULL_NAME + " VALUES(?,?,?)";
         PreparedStatement stmt = conn.prepareStatement(dml);
         int rows = 20;
         for(int i = 0 ; i < rows; i++) {
@@ -253,7 +255,7 @@ public class PhoenixHBaseLoaderIT {
          
         //load data and filter rows whose age is > 25
         pigServer.registerQuery(String.format(
-                "A = load 'hbase://table/%s' using "  + PhoenixHBaseLoader.class.getName() + "('%s');", TABLE_FULL_NAME,
+                "A = load 'hbase://table/%s' using "  + PhoenixHBaseLoader.class.getName() + "('%s');", CASE_SENSITIVE_TABLE_FULL_NAME,
                 zkQuorum));
         pigServer.registerQuery("B = FILTER A BY AGE > 25;");
         
@@ -603,6 +605,7 @@ public class PhoenixHBaseLoaderIT {
     @After
     public void tearDown() throws Exception {
         dropTable(TABLE_FULL_NAME);
+        dropTable(CASE_SENSITIVE_TABLE_FULL_NAME);
         pigServer.shutdown();
     }
 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/be5aba5b/phoenix-pig/src/it/java/org/apache/phoenix/pig/PhoenixHBaseStorerIT.java
----------------------------------------------------------------------
diff --git a/phoenix-pig/src/it/java/org/apache/phoenix/pig/PhoenixHBaseStorerIT.java b/phoenix-pig/src/it/java/org/apache/phoenix/pig/PhoenixHBaseStorerIT.java
index 67abf62..9106cdd 100644
--- a/phoenix-pig/src/it/java/org/apache/phoenix/pig/PhoenixHBaseStorerIT.java
+++ b/phoenix-pig/src/it/java/org/apache/phoenix/pig/PhoenixHBaseStorerIT.java
@@ -33,6 +33,7 @@ import java.util.Collection;
 
 import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.phoenix.end2end.BaseHBaseManagedTimeIT;
+import org.apache.phoenix.util.SchemaUtil;
 import org.apache.pig.ExecType;
 import org.apache.pig.PigServer;
 import org.apache.pig.backend.executionengine.ExecJob.JOB_STATUS;
@@ -140,12 +141,11 @@ public class PhoenixHBaseStorerIT extends BaseHBaseManagedTimeIT {
      */
     @Test
     public void testStorerForSpecificColumns() throws Exception {
-        final String tableName = "TABLE2";
+        final String tableName = SchemaUtil.getTableName("TABLE2", SchemaUtil.getEscapedArgument("zo2"));
         final Statement stmt = conn.createStatement();
 
         stmt.execute("CREATE TABLE " + tableName +
                  " (ID INTEGER NOT NULL PRIMARY KEY, NAME VARCHAR, AGE INTEGER)");
-
         final Data data = Storage.resetData(pigServer);
         final Collection<Tuple> list = Lists.newArrayList();
 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/be5aba5b/phoenix-pig/src/main/java/org/apache/phoenix/pig/PhoenixHBaseStorage.java
----------------------------------------------------------------------
diff --git a/phoenix-pig/src/main/java/org/apache/phoenix/pig/PhoenixHBaseStorage.java b/phoenix-pig/src/main/java/org/apache/phoenix/pig/PhoenixHBaseStorage.java
index 72d958b..4ada303 100644
--- a/phoenix-pig/src/main/java/org/apache/phoenix/pig/PhoenixHBaseStorage.java
+++ b/phoenix-pig/src/main/java/org/apache/phoenix/pig/PhoenixHBaseStorage.java
@@ -18,8 +18,6 @@
 package org.apache.phoenix.pig;
 
 import java.io.IOException;
-import java.net.URI;
-import java.net.URISyntaxException;
 import java.sql.SQLException;
 import java.util.List;
 import java.util.Properties;
@@ -33,12 +31,14 @@ import org.apache.commons.cli.ParseException;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.util.Pair;
 import org.apache.hadoop.io.NullWritable;
 import org.apache.hadoop.mapreduce.Job;
 import org.apache.hadoop.mapreduce.OutputFormat;
 import org.apache.hadoop.mapreduce.RecordWriter;
 import org.apache.phoenix.mapreduce.PhoenixOutputFormat;
 import org.apache.phoenix.mapreduce.util.PhoenixConfigurationUtil;
+import org.apache.phoenix.pig.util.TableSchemaParserFunction;
 import org.apache.phoenix.pig.writable.PhoenixPigDBWritable;
 import org.apache.phoenix.util.ColumnInfo;
 import org.apache.pig.ResourceSchema;
@@ -88,12 +88,12 @@ public class PhoenixHBaseStorage implements StoreFuncInterface {
     private ResourceSchema schema;  
     private long batchSize;
     private final PhoenixOutputFormat outputFormat = new PhoenixOutputFormat();
-
     // Set of options permitted
     private final static Options validOptions = new Options();
     private final static CommandLineParser parser = new GnuParser();
     private final static String SCHEMA = "_schema";
-
+    private final static String PHOENIX_TABLE_NAME_SCHEME = "hbase://";
+    
     private final CommandLine configuredOptions;
     private final String server;
 
@@ -134,33 +134,24 @@ public class PhoenixHBaseStorage implements StoreFuncInterface {
      */
     @Override
     public void setStoreLocation(String location, Job job) throws IOException {
-        URI locationURI;
-        try {
-            locationURI = new URI(location);
-            if (!"hbase".equals(locationURI.getScheme())) {
-                throw new IOException(String.format("Location must use the hbase protocol, hbase://tableName[/columnList]. Supplied location=%s",location));
-            }
-
-            PhoenixConfigurationUtil.loadHBaseConfiguration(job);
-            config = job.getConfiguration();
-            config.set(HConstants.ZOOKEEPER_QUORUM, server);
-            String tableName = locationURI.getAuthority();
-            // strip off the leading path token '/'
-            String columns = null;
-            if(!locationURI.getPath().isEmpty()) {
-                columns = locationURI.getPath().substring(1);
-                PhoenixConfigurationUtil.setUpsertColumnNames(config, columns);
-            }
-            PhoenixConfigurationUtil.setOutputTableName(config,tableName);
-            PhoenixConfigurationUtil.setBatchSize(config,batchSize);
-            String serializedSchema = getUDFProperties().getProperty(contextSignature + SCHEMA);
-            if (serializedSchema != null) {
-                schema = (ResourceSchema) ObjectSerializer.deserialize(serializedSchema);
-            }
-        } catch (URISyntaxException e) {
-            throw new IOException(String.format("Location must use the hbase protocol, hbase://tableName[/columnList]. Supplied location=%s",location),e);
+        String tableSchema = location.substring(PHOENIX_TABLE_NAME_SCHEME.length());
+        final TableSchemaParserFunction parseFunction = new TableSchemaParserFunction();
+        Pair<String,String> pair =  parseFunction.apply(tableSchema);
+        PhoenixConfigurationUtil.loadHBaseConfiguration(job);
+        config = job.getConfiguration();
+        config.set(HConstants.ZOOKEEPER_QUORUM, server);
+        String tableName = pair.getFirst();
+        String columns = pair.getSecond(); 
+        if(columns != null && columns.length() > 0) {
+            PhoenixConfigurationUtil.setUpsertColumnNames(config, columns);
         }
-    }
+        PhoenixConfigurationUtil.setOutputTableName(config,tableName);
+        PhoenixConfigurationUtil.setBatchSize(config,batchSize);
+        String serializedSchema = getUDFProperties().getProperty(contextSignature + SCHEMA);
+        if (serializedSchema != null) {
+            schema = (ResourceSchema) ObjectSerializer.deserialize(serializedSchema);
+        }
+     }
 
     @SuppressWarnings("unchecked")
     @Override


[50/50] [abbrv] phoenix git commit: Fix compilation errors after merge

Posted by ma...@apache.org.
Fix compilation errors after merge


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/76e92a96
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/76e92a96
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/76e92a96

Branch: refs/heads/calcite
Commit: 76e92a96127fb9c828dab95dc7e6e66e0e4adc12
Parents: 597e001
Author: maryannxue <we...@intel.com>
Authored: Mon Jul 20 13:14:07 2015 -0400
Committer: maryannxue <we...@intel.com>
Committed: Mon Jul 20 13:14:07 2015 -0400

----------------------------------------------------------------------
 .../it/java/org/apache/phoenix/calcite/CalciteTest.java | 12 ++++++------
 .../phoenix/calcite/rel/PhoenixRelImplementorImpl.java  |  2 +-
 2 files changed, 7 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/76e92a96/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteTest.java b/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteTest.java
index 1452ac9..08d272f 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteTest.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteTest.java
@@ -34,7 +34,7 @@ public class CalciteTest extends BaseClientManagedTimeIT {
     public static final String ATABLE_NAME = "ATABLE";
 
     public static Start start() {
-        return new Start(new Properties(), false);
+        return new Start(getConnectionProps(false), false);
     }
     
     public static Start start(Properties props, boolean connectUsingModel) {
@@ -238,11 +238,11 @@ public class CalciteTest extends BaseClientManagedTimeIT {
         return connection;
     }
 
-    private static Properties getMaterializationEnabledProps() {
+    private static Properties getConnectionProps(boolean enableMaterialization) {
         Properties props = new Properties();
         props.setProperty(
                 CalciteConnectionProperty.MATERIALIZATIONS_ENABLED.camelName(),
-                Boolean.toString(true));
+                Boolean.toString(enableMaterialization));
         props.setProperty(
                 CalciteConnectionProperty.CREATE_MATERIALIZATIONS.camelName(),
                 Boolean.toString(false));
@@ -859,7 +859,7 @@ public class CalciteTest extends BaseClientManagedTimeIT {
         } catch (Exception e) {
             throw new RuntimeException(e);
         }
-        final Start start = start(getMaterializationEnabledProps(), false);
+        final Start start = start(getConnectionProps(true), false);
         start.sql("select x_integer from aTable")
             .explainIs("PhoenixToEnumerableConverter\n" +
                        "  PhoenixToClientConverter\n" +
@@ -888,7 +888,7 @@ public class CalciteTest extends BaseClientManagedTimeIT {
         start.sql("select a_string, b_string from aTable where a_string = 'a'")
             .explainIs("PhoenixToEnumerableConverter\n" +
                        "  PhoenixToClientConverter\n" +
-                       "    PhoenixServerProject(0:A_STRING=[$0], 0:B_STRING=[$3])\n" +
+                       "    PhoenixServerProject(A_STRING=[$0], B_STRING=[$3])\n" +
                        "      PhoenixTableScan(table=[[phoenix, IDX1]], filter=[=($0, 'a')])\n")
             .close();
         start.sql("select a_string, b_string from aTable where b_string = 'b'")
@@ -902,7 +902,7 @@ public class CalciteTest extends BaseClientManagedTimeIT {
                        "  PhoenixToClientConverter\n" +
                        "    PhoenixServerProject(A_STRING=[$3], B_STRING=[$0], X_INTEGER=[$10], Y_INTEGER=[$11])\n" +
                        "      PhoenixTableScan(table=[[phoenix, IDX_FULL]], filter=[=($0, 'b')])\n")
-        .close();
+            .close();
     }
     
     @Test public void testConnectJoinHsqldb() {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/76e92a96/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixRelImplementorImpl.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixRelImplementorImpl.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixRelImplementorImpl.java
index 2ae3838..28b4f51 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixRelImplementorImpl.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixRelImplementorImpl.java
@@ -119,7 +119,7 @@ public class PhoenixRelImplementorImpl implements PhoenixRel.Implementor {
                     PTableType.SUBQUERY, null, MetaDataProtocol.MIN_TABLE_TIMESTAMP, PTable.INITIAL_SEQ_NUM,
                     null, null, columns, null, null, Collections.<PTable>emptyList(),
                     false, Collections.<PName>emptyList(), null, null, false, false, false, null,
-                    null, null);
+                    null, null, true);
             this.setTableRef(new TableRef(CalciteUtils.createTempAlias(), pTable, HConstants.LATEST_TIMESTAMP, false));
         } catch (SQLException e) {
             throw new RuntimeException(e);


[48/50] [abbrv] phoenix git commit: PHOENIX-2131 Closing paren in CastParseNode SQL

Posted by ma...@apache.org.
PHOENIX-2131 Closing paren in CastParseNode SQL

Add a missing closing parenthesis in CastParseNode.toSQL.


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/b38a6243
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/b38a6243
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/b38a6243

Branch: refs/heads/calcite
Commit: b38a62431ee44df171c913097d18e2433c951466
Parents: b329e85
Author: Gabriel Reid <gr...@apache.org>
Authored: Sun Jul 19 17:46:48 2015 +0200
Committer: Gabriel Reid <ga...@ngdata.com>
Committed: Mon Jul 20 15:25:01 2015 +0200

----------------------------------------------------------------------
 .../org/apache/phoenix/parse/CastParseNode.java |  2 +-
 .../apache/phoenix/parse/CastParseNodeTest.java | 57 ++++++++++++++++++++
 2 files changed, 58 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/b38a6243/phoenix-core/src/main/java/org/apache/phoenix/parse/CastParseNode.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/CastParseNode.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/CastParseNode.java
index 78be616..3e03613 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/CastParseNode.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/CastParseNode.java
@@ -133,7 +133,7 @@ public class CastParseNode extends UnaryParseNode {
         if (isArray) {
             buf.append(' ');
             buf.append(PDataType.ARRAY_TYPE_SUFFIX);
-            buf.append(' ');
         }
+        buf.append(")");
     }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b38a6243/phoenix-core/src/test/java/org/apache/phoenix/parse/CastParseNodeTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/parse/CastParseNodeTest.java b/phoenix-core/src/test/java/org/apache/phoenix/parse/CastParseNodeTest.java
new file mode 100644
index 0000000..b62d9a9
--- /dev/null
+++ b/phoenix-core/src/test/java/org/apache/phoenix/parse/CastParseNodeTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.parse;
+
+import org.apache.phoenix.schema.types.PDataType;
+import org.apache.phoenix.schema.types.PDecimal;
+import org.apache.phoenix.schema.types.PDouble;
+import org.apache.phoenix.schema.types.PLong;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class CastParseNodeTest {
+
+    @Test
+    public void testToSQL() {
+        ColumnParseNode columnParseNode = new ColumnParseNode(TableName.create("SCHEMA1", "TABLE1"), "V");
+        CastParseNode castParseNode = new CastParseNode(columnParseNode, PLong.INSTANCE, null, null, false);
+        StringBuilder stringBuilder = new StringBuilder();
+        castParseNode.toSQL(null, stringBuilder);
+        assertEquals(" CAST(TABLE1.V AS BIGINT)", stringBuilder.toString());
+    }
+
+    @Test
+    public void testToSQL_WithLengthAndScale() {
+        ColumnParseNode columnParseNode = new ColumnParseNode(TableName.create("SCHEMA1", "TABLE1"), "V");
+        CastParseNode castParseNode = new CastParseNode(columnParseNode, PDecimal.INSTANCE, 5, 3, false);
+        StringBuilder stringBuilder = new StringBuilder();
+        castParseNode.toSQL(null, stringBuilder);
+        assertEquals(" CAST(TABLE1.V AS DECIMAL(5,3))", stringBuilder.toString());
+    }
+
+    @Test
+    public void testToSQL_ArrayType() {
+        ColumnParseNode columnParseNode = new ColumnParseNode(TableName.create("SCHEMA1", "TABLE1"), "V");
+        CastParseNode castParseNode = new CastParseNode(columnParseNode, PLong.INSTANCE, null, null, true);
+        StringBuilder stringBuilder = new StringBuilder();
+        castParseNode.toSQL(null, stringBuilder);
+        assertEquals(" CAST(TABLE1.V AS BIGINT ARRAY)", stringBuilder.toString());
+    }
+}
\ No newline at end of file


[28/50] [abbrv] phoenix git commit: PHOENIX-2058 Check for existence and compatibility of columns being added in view

Posted by ma...@apache.org.
PHOENIX-2058 Check for existence and compatibility of columns being added in view


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/01b4f605
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/01b4f605
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/01b4f605

Branch: refs/heads/calcite
Commit: 01b4f6055911aa5036aa1e3a79340f80eb08396a
Parents: 66705d5
Author: Thomas D'Silva <td...@salesforce.com>
Authored: Fri Jul 10 11:55:55 2015 -0700
Committer: Thomas D'Silva <td...@salesforce.com>
Committed: Mon Jul 13 17:37:58 2015 -0700

----------------------------------------------------------------------
 .../apache/phoenix/end2end/AlterTableIT.java    | 311 ++++++++++++++++---
 .../java/org/apache/phoenix/end2end/ViewIT.java |   6 +
 .../coprocessor/MetaDataEndpointImpl.java       |  64 ++--
 .../apache/phoenix/schema/MetaDataClient.java   |   9 +-
 4 files changed, 310 insertions(+), 80 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/01b4f605/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java
index 0425933..607f52a 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java
@@ -2009,7 +2009,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
     }
     
     @Test
-    public void testAddNewColumnToBaseTableWithViews() throws Exception {
+    public void testAddNewColumnsToBaseTableWithViews() throws Exception {
         Connection conn = DriverManager.getConnection(getUrl());
         try {       
             conn.createStatement().execute("CREATE TABLE IF NOT EXISTS TABLEWITHVIEW ("
@@ -2018,13 +2018,15 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
                     + " COL2 bigint NOT NULL,"
                     + " CONSTRAINT NAME_PK PRIMARY KEY (ID, COL1, COL2)"
                     + " )");
-            assertTableDefinition(conn, "TABLEWITHVIEW", PTableType.TABLE, null, 0, 3, -1, "ID", "COL1", "COL2");
+            assertTableDefinition(conn, "TABLEWITHVIEW", PTableType.TABLE, null, 0, 3, QueryConstants.BASE_TABLE_BASE_COLUMN_COUNT, "ID", "COL1", "COL2");
             
             conn.createStatement().execute("CREATE VIEW VIEWOFTABLE ( VIEW_COL1 DECIMAL(10,2), VIEW_COL2 VARCHAR ) AS SELECT * FROM TABLEWITHVIEW");
             assertTableDefinition(conn, "VIEWOFTABLE", PTableType.VIEW, "TABLEWITHVIEW", 0, 5, 3, "ID", "COL1", "COL2", "VIEW_COL1", "VIEW_COL2");
             
-            conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD COL3 char(10)");
-            assertTableDefinition(conn, "VIEWOFTABLE", PTableType.VIEW, "TABLEWITHVIEW", 1, 6, 4, "ID", "COL1", "COL2", "COL3", "VIEW_COL1", "VIEW_COL2");
+            // adding a new pk column and a new regular column
+            conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD COL3 varchar(10) PRIMARY KEY, COL4 integer");
+            assertTableDefinition(conn, "TABLEWITHVIEW", PTableType.TABLE, null, 1, 5, QueryConstants.BASE_TABLE_BASE_COLUMN_COUNT, "ID", "COL1", "COL2", "COL3", "COL4");
+            assertTableDefinition(conn, "VIEWOFTABLE", PTableType.VIEW, "TABLEWITHVIEW", 1, 7, 5, "ID", "COL1", "COL2", "COL3", "COL4", "VIEW_COL1", "VIEW_COL2");
         } finally {
             conn.close();
         }
@@ -2040,13 +2042,13 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
                     + " COL2 bigint NOT NULL,"
                     + " CONSTRAINT NAME_PK PRIMARY KEY (ID, COL1, COL2)"
                     + " )");
-            assertTableDefinition(conn, "TABLEWITHVIEW", PTableType.TABLE, null, 0, 3, -1, "ID", "COL1", "COL2");
+            assertTableDefinition(conn, "TABLEWITHVIEW", PTableType.TABLE, null, 0, 3, QueryConstants.BASE_TABLE_BASE_COLUMN_COUNT, "ID", "COL1", "COL2");
             
-            conn.createStatement().execute("CREATE VIEW VIEWOFTABLE ( VIEW_COL1 DECIMAL(10,2), VIEW_COL2 VARCHAR(256), VIEW_COL3 VARCHAR, VIEW_COL4 DECIMAL ) AS SELECT * FROM TABLEWITHVIEW");
-            assertTableDefinition(conn, "VIEWOFTABLE", PTableType.VIEW, "TABLEWITHVIEW", 0, 7, 3, "ID", "COL1", "COL2", "VIEW_COL1", "VIEW_COL2", "VIEW_COL3", "VIEW_COL4");
+            conn.createStatement().execute("CREATE VIEW VIEWOFTABLE ( VIEW_COL1 DECIMAL(10,2), VIEW_COL2 VARCHAR(256), VIEW_COL3 VARCHAR, VIEW_COL4 DECIMAL, VIEW_COL5 DECIMAL(10,2), VIEW_COL6 VARCHAR, CONSTRAINT pk PRIMARY KEY (VIEW_COL5, VIEW_COL6) ) AS SELECT * FROM TABLEWITHVIEW");
+            assertTableDefinition(conn, "VIEWOFTABLE", PTableType.VIEW, "TABLEWITHVIEW", 0, 9, 3, "ID", "COL1", "COL2", "VIEW_COL1", "VIEW_COL2", "VIEW_COL3", "VIEW_COL4", "VIEW_COL5", "VIEW_COL6");
             
             // upsert single row into view
-            String dml = "UPSERT INTO VIEWOFTABLE VALUES(?,?,?,?,?, ?, ?)";
+            String dml = "UPSERT INTO VIEWOFTABLE VALUES(?,?,?,?,?, ?, ?, ?, ?)";
             PreparedStatement stmt = conn.prepareStatement(dml);
             stmt.setString(1, "view1");
             stmt.setInt(2, 12);
@@ -2055,6 +2057,8 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
             stmt.setString(5, "view5");
             stmt.setString(6, "view6");
             stmt.setInt(7, 17);
+            stmt.setInt(8, 18);
+            stmt.setString(9, "view9");
             stmt.execute();
             conn.commit();
             
@@ -2068,7 +2072,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
             }           
             
             try {
-            	// should fail because there is already a view column with same name with a higher scale
+            	// should fail because there is already a view column with same name with different scale
             	conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD VIEW_COL1 DECIMAL(10,1)");
             	fail();
             }
@@ -2077,8 +2081,8 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
             } 
             
             try {
-            	// should fail because there is already a view column with same name with a greater length
-            	conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD VIEW_COL1 VARCHAR(256)");
+            	// should fail because there is already a view column with same name with different length
+            	conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD VIEW_COL1 DECIMAL(9,2)");
             	fail();
             }
             catch (SQLException e) {
@@ -2086,8 +2090,8 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
             } 
             
             try {
-            	// should fail because there is already a view column with null length
-            	conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD VIEW_COL2 DECIMAL(9,2)");
+            	// should fail because there is already a view column with different length
+            	conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD VIEW_COL2 VARCHAR");
             	fail();
             }
             catch (SQLException e) {
@@ -2095,35 +2099,36 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
             }
             
             // validate that there were no columns added to the table or view
-            assertTableDefinition(conn, "TABLEWITHVIEW", PTableType.TABLE, null, 0, 3, -1, "ID", "COL1", "COL2");
-            assertTableDefinition(conn, "VIEWOFTABLE", PTableType.VIEW, "TABLEWITHVIEW", 0, 7, 3, "ID", "COL1", "COL2", "VIEW_COL1", "VIEW_COL2", "VIEW_COL3", "VIEW_COL4");
+            assertTableDefinition(conn, "TABLEWITHVIEW", PTableType.TABLE, null, 0, 3, QueryConstants.BASE_TABLE_BASE_COLUMN_COUNT, "ID", "COL1", "COL2");
+            assertTableDefinition(conn, "VIEWOFTABLE", PTableType.VIEW, "TABLEWITHVIEW", 0, 9, 3, "ID", "COL1", "COL2", "VIEW_COL1", "VIEW_COL2", "VIEW_COL3", "VIEW_COL4", "VIEW_COL5", "VIEW_COL6");
             
-            // should succeed because even though there already is a view column with same name,
-            // the column being added has a scale and max length that is equal to or greater
-            conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD VIEW_COL2 VARCHAR, VIEW_COL4 DECIMAL");
-            assertTableDefinition(conn, "TABLEWITHVIEW", PTableType.TABLE, null, 1, 4, -1, "ID", "COL1", "COL2", "VIEW_COL2", "VIEW_COL4");
-            assertTableDefinition(conn, "VIEWOFTABLE", PTableType.VIEW, "TABLEWITHVIEW", 1, 7, 5, "ID", "COL1", "COL2", "VIEW_COL1", "VIEW_COL2", "VIEW_COL3", "VIEW_COL4");
+            // should succeed 
+            conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD VIEW_COL4 DECIMAL, VIEW_COL2 VARCHAR(256)");
+            assertTableDefinition(conn, "TABLEWITHVIEW", PTableType.TABLE, null, 1, 5, QueryConstants.BASE_TABLE_BASE_COLUMN_COUNT, "ID", "COL1", "COL2", "VIEW_COL4", "VIEW_COL2");
+            assertTableDefinition(conn, "VIEWOFTABLE", PTableType.VIEW, "TABLEWITHVIEW", 1, 9, 5, "ID", "COL1", "COL2", "VIEW_COL1", "VIEW_COL2", "VIEW_COL3", "VIEW_COL4", "VIEW_COL5", "VIEW_COL6");
             
             // query table
             ResultSet rs = stmt.executeQuery("SELECT * FROM TABLEWITHVIEW");
             assertTrue(rs.next());
-            assertEquals(rs.getString(1), "view1");
-            assertEquals(rs.getInt(2), 12);
-            assertEquals(rs.getInt(3), 13);
-            assertEquals(rs.getString(4), "view5");
-            assertEquals(rs.getInt(5), 17);
+            assertEquals("view1", rs.getString("ID"));
+            assertEquals(12, rs.getInt("COL1"));
+            assertEquals(13, rs.getInt("COL2"));
+            assertEquals("view5", rs.getString("VIEW_COL2"));
+            assertEquals(17, rs.getInt("VIEW_COL4"));
             assertFalse(rs.next());
 
             // query view
             rs = stmt.executeQuery("SELECT * FROM VIEWOFTABLE");
             assertTrue(rs.next());
-            assertEquals(rs.getString(1), "view1");
-            assertEquals(rs.getInt(2), 12);
-            assertEquals(rs.getInt(3), 13);
-            assertEquals(rs.getInt(4), 14);
-            assertEquals(rs.getString(5),"view5");
-            assertEquals(rs.getString(6),"view6");
-            assertEquals(rs.getInt(7), 17);
+            assertEquals("view1", rs.getString("ID"));
+            assertEquals(12, rs.getInt("COL1"));
+            assertEquals(13, rs.getInt("COL2"));
+            assertEquals(14, rs.getInt("VIEW_COL1"));
+            assertEquals("view5", rs.getString("VIEW_COL2"));
+            assertEquals("view6", rs.getString("VIEW_COL3"));
+            assertEquals(17, rs.getInt("VIEW_COL4"));
+            assertEquals(18, rs.getInt("VIEW_COL5"));
+            assertEquals("view9", rs.getString("VIEW_COL6"));
             assertFalse(rs.next());
         } finally {
             conn.close();
@@ -2140,7 +2145,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
                     + " COL2 integer NOT NULL,"
                     + " CONSTRAINT NAME_PK PRIMARY KEY (ID, COL1, COL2)"
                     + " )");
-            assertTableDefinition(conn, "TABLEWITHVIEW", PTableType.TABLE, null, 0, 3, -1, "ID", "COL1", "COL2");
+            assertTableDefinition(conn, "TABLEWITHVIEW", PTableType.TABLE, null, 0, 3, QueryConstants.BASE_TABLE_BASE_COLUMN_COUNT, "ID", "COL1", "COL2");
             
             conn.createStatement().execute("CREATE VIEW VIEWOFTABLE ( VIEW_COL1 DECIMAL(10,2), VIEW_COL2 VARCHAR(256) CONSTRAINT pk PRIMARY KEY (VIEW_COL1, VIEW_COL2)) AS SELECT * FROM TABLEWITHVIEW");
             assertTableDefinition(conn, "VIEWOFTABLE", PTableType.VIEW, "TABLEWITHVIEW", 0, 5, 3, "ID", "COL1", "COL2", "VIEW_COL1", "VIEW_COL2");
@@ -2157,8 +2162,53 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
             conn.commit();
             
             try {
-            	// should fail because there is already a pk view column with same name is a different slot 
-            	conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD VIEW_COL2 VARCHAR PRIMARY KEY");
+            	// should fail because there we have to add both VIEW_COL1 and VIEW_COL2 to the pk
+            	conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD VIEW_COL2 VARCHAR(256) PRIMARY KEY");
+            	fail();
+            }
+            catch (SQLException e) {
+            	assertEquals("Unexpected exception", CANNOT_MUTATE_TABLE.getErrorCode(), e.getErrorCode());
+            }
+            
+            try {
+            	// should fail because there we have to add both VIEW_COL1 and VIEW_COL2  to the pk 
+            	conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD VIEW_COL1 DECIMAL(10,2) PRIMARY KEY");
+            	fail();
+            }
+            catch (SQLException e) {
+            	assertEquals("Unexpected exception", CANNOT_MUTATE_TABLE.getErrorCode(), e.getErrorCode());
+            }
+            
+            try {
+            	// should fail because there we have to add both VIEW_COL1 and VIEW_COL2 to the pk
+            	conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD VIEW_COL1 DECIMAL(10,2), VIEW_COL2 VARCHAR(256) PRIMARY KEY");
+            	fail();
+            }
+            catch (SQLException e) {
+            	assertEquals("Unexpected exception", CANNOT_MUTATE_TABLE.getErrorCode(), e.getErrorCode());
+            }
+            
+            try {
+            	// should fail because there we have to add both VIEW_COL1 and VIEW_COL2  to the pk 
+            	conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD VIEW_COL1 DECIMAL(10,2) PRIMARY KEY, VIEW_COL2 VARCHAR(256)");
+            	fail();
+            }
+            catch (SQLException e) {
+            	assertEquals("Unexpected exception", CANNOT_MUTATE_TABLE.getErrorCode(), e.getErrorCode());
+            }
+            
+            try {
+            	// should fail because there we have to add both VIEW_COL1 and VIEW_COL2 to the pk in the right order
+            	conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD VIEW_COL2 VARCHAR(256) PRIMARY KEY, VIEW_COL1 DECIMAL(10,2) PRIMARY KEY");
+            	fail();
+            }
+            catch (SQLException e) {
+            	assertEquals("Unexpected exception", CANNOT_MUTATE_TABLE.getErrorCode(), e.getErrorCode());
+            }
+            
+            try {
+            	// should fail because there we have to add both VIEW_COL1 and VIEW_COL2 with the right sort order
+            	conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD VIEW_COL1 DECIMAL(10,2) PRIMARY KEY DESC, VIEW_COL2 VARCHAR(256) PRIMARY KEY");
             	fail();
             }
             catch (SQLException e) {
@@ -2166,33 +2216,194 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
             }
             
             // add the pk column of the view to the base table
-            conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD VIEW_COL1 DECIMAL(10,2) PRIMARY KEY");
-            assertTableDefinition(conn, "TABLEWITHVIEW", PTableType.TABLE, null, 1, 4, -1, "ID", "COL1", "COL2", "VIEW_COL1");
-            assertTableDefinition(conn, "VIEWOFTABLE", PTableType.VIEW, "TABLEWITHVIEW", 1, 5, 4, "ID", "COL1", "COL2", "VIEW_COL1", "VIEW_COL2");
+            conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD VIEW_COL1 DECIMAL(10,2) PRIMARY KEY, VIEW_COL2 VARCHAR(256) PRIMARY KEY");
+            assertTableDefinition(conn, "TABLEWITHVIEW", PTableType.TABLE, null, 1, 5, QueryConstants.BASE_TABLE_BASE_COLUMN_COUNT, "ID", "COL1", "COL2", "VIEW_COL1", "VIEW_COL2");
+            assertTableDefinition(conn, "VIEWOFTABLE", PTableType.VIEW, "TABLEWITHVIEW", 1, 5, 5, "ID", "COL1", "COL2", "VIEW_COL1", "VIEW_COL2");
             
             // query table
             ResultSet rs = stmt.executeQuery("SELECT * FROM TABLEWITHVIEW");
             assertTrue(rs.next());
-            assertEquals(rs.getString(1), "view1");
-            assertEquals(rs.getInt(2), 12);
-            assertEquals(rs.getInt(3), 13);
-            assertEquals(rs.getBigDecimal(4).intValue(), 14);
+            assertEquals("view1", rs.getString("ID"));
+            assertEquals(12, rs.getInt("COL1"));
+            assertEquals(13, rs.getInt("COL2"));
+            assertEquals(14, rs.getInt("VIEW_COL1"));
+            assertEquals("view5", rs.getString("VIEW_COL2"));
             assertFalse(rs.next());
 
             // query view
             rs = stmt.executeQuery("SELECT * FROM VIEWOFTABLE");
             assertTrue(rs.next());
-            assertEquals(rs.getString(1), "view1");
-            assertEquals(rs.getInt(2), 12);
-            assertEquals(rs.getInt(3), 13);
-            assertEquals(rs.getInt(4), 14);
-            assertEquals(rs.getString(5),"view5");
+            assertEquals("view1", rs.getString("ID"));
+            assertEquals(12, rs.getInt("COL1"));
+            assertEquals(13, rs.getInt("COL2"));
+            assertEquals(14, rs.getInt("VIEW_COL1"));
+            assertEquals("view5", rs.getString("VIEW_COL2"));
             assertFalse(rs.next());
         } finally {
             conn.close();
         }
     }
+    
+    @Test
+    public void testAddExistingViewPkColumnToBaseTableWithMultipleViews() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        try {       
+            conn.createStatement().execute("CREATE TABLE IF NOT EXISTS TABLEWITHVIEW ("
+                    + " ID char(10) NOT NULL,"
+                    + " COL1 integer NOT NULL,"
+                    + " COL2 integer NOT NULL,"
+                    + " CONSTRAINT NAME_PK PRIMARY KEY (ID, COL1, COL2)"
+                    + " )");
+            assertTableDefinition(conn, "TABLEWITHVIEW", PTableType.TABLE, null, 0, 3, QueryConstants.BASE_TABLE_BASE_COLUMN_COUNT, "ID", "COL1", "COL2");
+            
+            conn.createStatement().execute("CREATE VIEW VIEWOFTABLE1 ( VIEW_COL1 DECIMAL(10,2), VIEW_COL2 VARCHAR(256) CONSTRAINT pk PRIMARY KEY (VIEW_COL1, VIEW_COL2)) AS SELECT * FROM TABLEWITHVIEW");
+            assertTableDefinition(conn, "VIEWOFTABLE1", PTableType.VIEW, "TABLEWITHVIEW", 0, 5, 3, "ID", "COL1", "COL2", "VIEW_COL1", "VIEW_COL2");
+            
+            conn.createStatement().execute("CREATE VIEW VIEWOFTABLE2 ( VIEW_COL3 VARCHAR(256), VIEW_COL4 DECIMAL(10,2) CONSTRAINT pk PRIMARY KEY (VIEW_COL3, VIEW_COL4)) AS SELECT * FROM TABLEWITHVIEW");
+            assertTableDefinition(conn, "VIEWOFTABLE2", PTableType.VIEW, "TABLEWITHVIEW", 0, 5, 3, "ID", "COL1", "COL2", "VIEW_COL3", "VIEW_COL4");
+            
+            try {
+            	// should fail because there are two view with different pk columns
+            	conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD VIEW_COL1 DECIMAL PRIMARY KEY, VIEW_COL2 VARCHAR PRIMARY KEY");
+            	fail();
+            }
+            catch (SQLException e) {
+            	assertEquals("Unexpected exception", CANNOT_MUTATE_TABLE.getErrorCode(), e.getErrorCode());
+            }
+            
+            try {
+            	// should fail because there are two view with different pk columns
+            	conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD VIEW_COL3 VARCHAR PRIMARY KEY, VIEW_COL4 DECIMAL PRIMARY KEY");
+            	fail();
+            }
+            catch (SQLException e) {
+            	assertEquals("Unexpected exception", CANNOT_MUTATE_TABLE.getErrorCode(), e.getErrorCode());
+            }
+            
+            try {
+            	// should fail because slot positions of pks are different
+            	conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD VIEW_COL1 DECIMAL PRIMARY KEY, VIEW_COL2 VARCHAR PRIMARY KEY, VIEW_COL3 VARCHAR PRIMARY KEY, VIEW_COL4 DECIMAL PRIMARY KEY");
+            	fail();
+            }
+            catch (SQLException e) {
+            	assertEquals("Unexpected exception", CANNOT_MUTATE_TABLE.getErrorCode(), e.getErrorCode());
+            }
+            
+            try {
+            	// should fail because slot positions of pks are different
+            	conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD VIEW_COL3 VARCHAR PRIMARY KEY, VIEW_COL4 DECIMAL PRIMARY KEY, VIEW_COL1 DECIMAL PRIMARY KEY, VIEW_COL2 VARCHAR PRIMARY KEY");
+            	fail();
+            }
+            catch (SQLException e) {
+            	assertEquals("Unexpected exception", CANNOT_MUTATE_TABLE.getErrorCode(), e.getErrorCode());
+            }
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testAddExistingViewPkColumnToBaseTableWithMultipleViewsHavingSamePks() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        try {       
+            conn.createStatement().execute("CREATE TABLE IF NOT EXISTS TABLEWITHVIEW ("
+                    + " ID char(10) NOT NULL,"
+                    + " COL1 integer NOT NULL,"
+                    + " COL2 integer NOT NULL,"
+                    + " CONSTRAINT NAME_PK PRIMARY KEY (ID, COL1, COL2)"
+                    + " )");
+            assertTableDefinition(conn, "TABLEWITHVIEW", PTableType.TABLE, null, 0, 3, QueryConstants.BASE_TABLE_BASE_COLUMN_COUNT, "ID", "COL1", "COL2");
+            
+            conn.createStatement().execute("CREATE VIEW VIEWOFTABLE1 ( VIEW_COL1 DECIMAL(10,2), VIEW_COL2 VARCHAR(256) CONSTRAINT pk PRIMARY KEY (VIEW_COL1, VIEW_COL2)) AS SELECT * FROM TABLEWITHVIEW");
+            assertTableDefinition(conn, "VIEWOFTABLE1", PTableType.VIEW, "TABLEWITHVIEW", 0, 5, 3, "ID", "COL1", "COL2", "VIEW_COL1", "VIEW_COL2");
+            
+            conn.createStatement().execute("CREATE VIEW VIEWOFTABLE2 ( VIEW_COL1 DECIMAL(10,2), VIEW_COL2 VARCHAR(256) CONSTRAINT pk PRIMARY KEY (VIEW_COL1, VIEW_COL2)) AS SELECT * FROM TABLEWITHVIEW");
+            assertTableDefinition(conn, "VIEWOFTABLE2", PTableType.VIEW, "TABLEWITHVIEW", 0, 5, 3, "ID", "COL1", "COL2", "VIEW_COL1", "VIEW_COL2");
+            
+            // upsert single row into both view
+            String dml = "UPSERT INTO VIEWOFTABLE1 VALUES(?,?,?,?,?)";
+            PreparedStatement stmt = conn.prepareStatement(dml);
+            stmt.setString(1, "view1");
+            stmt.setInt(2, 12);
+            stmt.setInt(3, 13);
+            stmt.setInt(4, 14);
+            stmt.setString(5, "view5");
+            stmt.execute();
+            conn.commit();
+            dml = "UPSERT INTO VIEWOFTABLE2 VALUES(?,?,?,?,?)";
+            stmt = conn.prepareStatement(dml);
+            stmt.setString(1, "view1");
+            stmt.setInt(2, 12);
+            stmt.setInt(3, 13);
+            stmt.setInt(4, 14);
+            stmt.setString(5, "view5");
+            stmt.execute();
+            conn.commit();
+            
+            try {
+            	// should fail because the view have two extra columns in their pk
+            	conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD VIEW_COL1 DECIMAL(10,2) PRIMARY KEY");
+            	fail();
+            }
+            catch (SQLException e) {
+            	assertEquals("Unexpected exception", CANNOT_MUTATE_TABLE.getErrorCode(), e.getErrorCode());
+            }
+            
+            try {
+            	// should fail because the view have two extra columns in their pk
+            	conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD VIEW_COL2 VARCHAR(256) PRIMARY KEY");
+            	fail();
+            }
+            catch (SQLException e) {
+            	assertEquals("Unexpected exception", CANNOT_MUTATE_TABLE.getErrorCode(), e.getErrorCode());
+            }
+            
+            try {
+            	// should fail because slot positions of pks are different
+            	conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD VIEW_COL2 DECIMAL(10,2) PRIMARY KEY, VIEW_COL1 VARCHAR(256) PRIMARY KEY");
+            	fail();
+            }
+            catch (SQLException e) {
+            	assertEquals("Unexpected exception", CANNOT_MUTATE_TABLE.getErrorCode(), e.getErrorCode());
+            }
+            
+            conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD VIEW_COL1 DECIMAL(10,2) PRIMARY KEY, VIEW_COL2 VARCHAR(256) PRIMARY KEY");
+            assertTableDefinition(conn, "TABLEWITHVIEW", PTableType.TABLE, null, 1, 5, QueryConstants.BASE_TABLE_BASE_COLUMN_COUNT, "ID", "COL1", "COL2", "VIEW_COL1", "VIEW_COL2");
+            assertTableDefinition(conn, "VIEWOFTABLE1", PTableType.VIEW, "TABLEWITHVIEW", 1, 5, 5, "ID", "COL1", "COL2", "VIEW_COL1", "VIEW_COL2");
+            assertTableDefinition(conn, "VIEWOFTABLE2", PTableType.VIEW, "TABLEWITHVIEW", 1, 5, 5, "ID", "COL1", "COL2", "VIEW_COL1", "VIEW_COL2");
+            
+            // query table
+            ResultSet rs = stmt.executeQuery("SELECT * FROM TABLEWITHVIEW");
+            assertTrue(rs.next());
+            assertEquals("view1", rs.getString("ID"));
+            assertEquals(12, rs.getInt("COL1"));
+            assertEquals(13, rs.getInt("COL2"));
+            assertEquals(14, rs.getInt("VIEW_COL1"));
+            assertEquals("view5", rs.getString("VIEW_COL2"));
+            assertFalse(rs.next());
 
+            // query both views
+            rs = stmt.executeQuery("SELECT * FROM VIEWOFTABLE1");
+            assertTrue(rs.next());
+            assertEquals("view1", rs.getString("ID"));
+            assertEquals(12, rs.getInt("COL1"));
+            assertEquals(13, rs.getInt("COL2"));
+            assertEquals(14, rs.getInt("VIEW_COL1"));
+            assertEquals("view5", rs.getString("VIEW_COL2"));
+            assertFalse(rs.next());
+            rs = stmt.executeQuery("SELECT * FROM VIEWOFTABLE2");
+            assertTrue(rs.next());
+            assertEquals("view1", rs.getString("ID"));
+            assertEquals(12, rs.getInt("COL1"));
+            assertEquals(13, rs.getInt("COL2"));
+            assertEquals(14, rs.getInt("VIEW_COL1"));
+            assertEquals("view5", rs.getString("VIEW_COL2"));
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+    
     private void assertTableDefinition(Connection conn, String tableName, PTableType tableType, String parentTableName, int sequenceNumber, int columnCount, int baseColumnCount, String... columnName) throws Exception {
         PreparedStatement p = conn.prepareStatement("SELECT * FROM SYSTEM.CATALOG WHERE TABLE_NAME=? AND TABLE_TYPE=?");
         p.setString(1, tableName);
@@ -2234,10 +2445,8 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
                                 assertEquals(parentTableName, parentTableColumnValue);
                                 assertEquals(tableName, viewColumnValue);
                             } 
-                            else if (parentTableColumnsMetadata.getColumnName(columnIndex).equals(PhoenixDatabaseMetaData.ORDINAL_POSITION) && parentTableColumnsRs.getString("COLUMN_FAMILY")!=null) {
-                            	// its ok if the ordinal positions don't match for non-pk columns 
-                            }
-                            else {
+                            // its ok if the ordinal positions don't match for non-pk columns
+                            else if (!(parentTableColumnsMetadata.getColumnName(columnIndex).equals(PhoenixDatabaseMetaData.ORDINAL_POSITION) && parentTableColumnsRs.getString(PhoenixDatabaseMetaData.COLUMN_FAMILY)!=null)) {
                                 fail(parentTableColumnsMetadata.getColumnName(columnIndex) + " of base table " + parentTableColumnValue + " does not match view "+viewColumnValue) ;
                             }
                         }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/01b4f605/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewIT.java
index 1d8af35..3254c2e 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewIT.java
@@ -537,6 +537,12 @@ public class ViewIT extends BaseViewIT {
         } catch (SQLException e) {
             assertEquals(CANNOT_MODIFY_VIEW_PK.getErrorCode(), e.getErrorCode());
         }
+        ddl = "CREATE VIEW v2 (k3 VARCHAR PRIMARY KEY)  AS SELECT * FROM tp WHERE v1 = 1.0";
+        try {
+        	conn.createStatement().execute(ddl);
+        } catch (SQLException e) {
+            assertEquals(CANNOT_MODIFY_VIEW_PK.getErrorCode(), e.getErrorCode());
+        }
     }
     
     @Test(expected=ColumnAlreadyExistsException.class)

http://git-wip-us.apache.org/repos/asf/phoenix/blob/01b4f605/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 399d56e..1be0548 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
@@ -186,6 +186,7 @@ import org.apache.phoenix.util.ServerUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Objects;
 import com.google.common.cache.Cache;
 import com.google.common.collect.Lists;
 import com.google.protobuf.ByteString;
@@ -1591,17 +1592,11 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
         }
     }
     
-    private boolean isValidLengthOrScale(Integer viewColVal, Cell cell) {
-    	Integer baseColValue = cell.getValueArray()==null ? null :
-			PInteger.INSTANCE.getCodec().decodeInt(cell.getValueArray(), cell.getValueOffset(), SortOrder.getDefault());
-    	if ( (baseColValue!=null && viewColVal!=null && baseColValue<viewColVal) || // if the base column has a scale/length less then the view column
-    			(viewColVal==null && baseColValue!=null)) {  // or if the view column has no scale/length, but the base column has a scale/length
-    		return false;
-    	}
-    	return true;
+    private boolean isvalidAttribute(Object obj1, Object obj2) {
+    	return (obj1!=null && obj1.equals(obj2)) || (obj1==null && obj2==null); 
     }
 
-    private MetaDataMutationResult addRowsToChildViews(List<Mutation> tableMetadata, List<Mutation> mutationsForAddingColumnsToViews, byte[] schemaName, byte[] tableName,
+    private MetaDataMutationResult addRowsToChildViews(PTable table, List<Mutation> tableMetadata, List<Mutation> mutationsForAddingColumnsToViews, byte[] schemaName, byte[] tableName,
             List<ImmutableBytesPtr> invalidateList, long clientTimeStamp, TableViewFinderResult childViewsResult,
             Region region, List<RowLock> locks) throws IOException, SQLException {
         for (Result viewResult : childViewsResult.getResults()) {
@@ -1623,6 +1618,8 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
             int numColsAddedToView = 0;
             short deltaNumPkColsSoFar = 0;
             PColumn existingViewColumn = null;
+            List<PColumn> viewPkColumns = Lists.newArrayList(view.getPKColumns());
+            boolean addingPKColumn=false;
             for (Mutation m : tableMetadata) {
                 byte[][] rkmd = new byte[5][];
                 int pkCount = getVarChars(m.getRow(), rkmd);
@@ -1635,7 +1632,10 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
                         existingViewColumn = rkmd[FAMILY_NAME_INDEX] == null 
                             ? view.getPKColumn(columnName) 
                             : view.getColumnFamily(rkmd[FAMILY_NAME_INDEX]).getColumn(columnName);
-                    } catch (ColumnNotFoundException e) {
+                    } 
+                	catch (ColumnFamilyNotFoundException e) {
+                    }
+                	catch (ColumnNotFoundException e) {
                     } // Ignore - means column family or column name don't exist
                 	
                     Put p = (Put)m;
@@ -1663,6 +1663,7 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
                     			 return new MetaDataMutationResult(MutationCode.UNALLOWED_TABLE_MUTATION, EnvironmentEdgeManager.currentTimeMillis(), null);
                     		}
                 		}
+                		// if adding a pk column to the base table
                 		if (rkmd[FAMILY_NAME_INDEX] == null && rkmd[COLUMN_NAME_INDEX] != null) {
 	                        List<Cell> keySeqCells = viewColumnDefinitionPut.get(
 	                                PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
@@ -1675,35 +1676,45 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
 	                     			return new MetaDataMutationResult(MutationCode.UNALLOWED_TABLE_MUTATION, EnvironmentEdgeManager.currentTimeMillis(), null);
 	                     		}
 	                        }
+	                        List<Cell> sortOrders = viewColumnDefinitionPut.get(PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
+                                    PhoenixDatabaseMetaData.SORT_ORDER_BYTES);
+	                        SortOrder sortOrder = SortOrder.getDefault();
+                            if (sortOrders != null && sortOrders.size() > 0) {
+                            	Cell cell = sortOrders.get(0);
+                            	int sortOrderInt = PInteger.INSTANCE.getCodec().decodeInt(cell.getValueArray(), cell.getValueOffset(), SortOrder.getDefault());
+                            	sortOrder = SortOrder.fromSystemValue(sortOrderInt);                            	
+                            }
+                            if (!Objects.equal(sortOrder,existingViewColumn.getSortOrder())) {
+                        		return new MetaDataMutationResult(MutationCode.UNALLOWED_TABLE_MUTATION, EnvironmentEdgeManager.currentTimeMillis(), null);
+                        	}
+	                        viewPkColumns.remove(existingViewColumn);
                         }
                 		// if there is an existing view column that matches the column being added to the base table and if the column being added has a null
                     	// scale or maxLength, we need to explicitly do a put to set the scale or maxLength to null (in case the view column has the scale or 
                     	// max length set)
+                		Integer maxLength = null;
                 		List<Cell> columnSizes = viewColumnDefinitionPut.get(
                                 PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
                                 PhoenixDatabaseMetaData.COLUMN_SIZE_BYTES);
                         if (columnSizes != null && columnSizes.size() > 0) {
                             Cell cell = columnSizes.get(0);
-                            if (!isValidLengthOrScale(existingViewColumn.getMaxLength(),cell)) {
-                            	return new MetaDataMutationResult(MutationCode.UNALLOWED_TABLE_MUTATION, EnvironmentEdgeManager.currentTimeMillis(), null); 
-                            }
+                            maxLength = cell.getValueArray()==null ? null :
+                    		PInteger.INSTANCE.getCodec().decodeInt(cell.getValueArray(), cell.getValueOffset(), SortOrder.getDefault());
                         }
-                        else if (existingViewColumn.getMaxLength()!=null) {
-                        	viewColumnDefinitionPut.add(PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
-                                    PhoenixDatabaseMetaData.COLUMN_SIZE_BYTES, null);
+                        if (!Objects.equal(maxLength,existingViewColumn.getMaxLength())) {
+                        	return new MetaDataMutationResult(MutationCode.UNALLOWED_TABLE_MUTATION, EnvironmentEdgeManager.currentTimeMillis(), null); 
                         }
+                        Integer scale = null;
                         List<Cell> decimalDigits = viewColumnDefinitionPut.get(
                                 PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
                                 PhoenixDatabaseMetaData.DECIMAL_DIGITS_BYTES);
                         if (decimalDigits != null && decimalDigits.size() > 0) {
                             Cell cell = decimalDigits.get(0);
-                            if (!isValidLengthOrScale(existingViewColumn.getScale(),cell)) {
-                            	return new MetaDataMutationResult(MutationCode.UNALLOWED_TABLE_MUTATION, EnvironmentEdgeManager.currentTimeMillis(), null); 
-                            }
+                            scale = cell.getValueArray()==null ? null :
+                        	PInteger.INSTANCE.getCodec().decodeInt(cell.getValueArray(), cell.getValueOffset(), SortOrder.getDefault());
                         }
-                        else if (existingViewColumn.getScale()!=null) {
-                        	viewColumnDefinitionPut.add(PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
-                                    PhoenixDatabaseMetaData.DECIMAL_DIGITS_BYTES, null);
+                        if (!Objects.equal(scale,existingViewColumn.getScale())) {	
+                        	return new MetaDataMutationResult(MutationCode.UNALLOWED_TABLE_MUTATION, EnvironmentEdgeManager.currentTimeMillis(), null); 
                         }
                 	}
                 	else {
@@ -1719,6 +1730,7 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
                          * If adding a pk column to the base table (and hence the view), see if there are any indexes on
                          * the view. If yes, then generate puts for the index header row and column rows.
                          */
+                    	addingPKColumn=true;
                         deltaNumPkColsSoFar++;
                         for (PTable index : view.getIndexes()) {
                             int oldNumberOfColsInIndex = index.getColumns().size();
@@ -1807,6 +1819,12 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
                     }
                 }
             }
+            // allow adding a pk columns to base table :
+            // 1. if all the view pk columns are exactly the same as the base table pk columns
+            // 2. if we are adding all the existing view pk columns to the base table 
+            if (addingPKColumn && !viewPkColumns.equals(table.getPKColumns())) {
+            	return new MetaDataMutationResult(MutationCode.UNALLOWED_TABLE_MUTATION, EnvironmentEdgeManager.currentTimeMillis(), null);
+            }
             if (deltaNumPkColsSoFar > 0) {
                 for (PTable index : view.getIndexes()) {
                     byte[] indexHeaderRowKey = getViewIndexHeaderRowKey(index);
@@ -1919,7 +1937,7 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
                                         EnvironmentEdgeManager.currentTimeMillis(), null);
                             } else {
                                 mutationsForAddingColumnsToViews = new ArrayList<>(childViewsResult.getResults().size() * tableMetaData.size());
-                                MetaDataMutationResult mutationResult = addRowsToChildViews(tableMetaData, mutationsForAddingColumnsToViews, schemaName, tableName, invalidateList, clientTimeStamp,
+                                MetaDataMutationResult mutationResult = addRowsToChildViews(table, tableMetaData, mutationsForAddingColumnsToViews, schemaName, tableName, invalidateList, clientTimeStamp,
                                         childViewsResult, region, locks);
                                 // return if we were not able to add the column successfully
                                 if (mutationResult!=null)

http://git-wip-us.apache.org/repos/asf/phoenix/blob/01b4f605/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
index 56c1aa2..9ad52a5 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
@@ -2505,7 +2505,7 @@ public class MetaDataClient {
                 }
                 long seqNum = table.getSequenceNumber();
                 if (changingPhoenixTableProperty || columnDefs.size() > 0) { 
-                    seqNum = incrementTableSeqNum(table, statement.getTableType(), 1, isImmutableRows, disableWAL, multiTenant, storeNulls);
+                    seqNum = incrementTableSeqNum(table, statement.getTableType(), columnDefs.size(), isImmutableRows, disableWAL, multiTenant, storeNulls);
                     tableMetaData.addAll(connection.getMutationState().toMutations().next().getSecond());
                     connection.rollback();
                 }
@@ -2977,14 +2977,11 @@ public class MetaDataClient {
     
     private boolean isLastPKVariableLength(PTable table) {
         List<PColumn> pkColumns = table.getPKColumns();
-        if (pkColumns.size() < 1) {
-            return false;
-        } else {
-            return !pkColumns.get(pkColumns.size()-1).getDataType().isFixedWidth();
-        }
+        return !pkColumns.get(pkColumns.size()-1).getDataType().isFixedWidth();
     }
     
     private PTable getParentOfView(PTable view) throws SQLException {
+    	//TODO just use view.getParentName().getString() after implementing https://issues.apache.org/jira/browse/PHOENIX-2114 
         SelectStatement select = new SQLParser(view.getViewStatement()).parseQuery();
         String parentName = SchemaUtil.normalizeFullTableName(select.getFrom().toString().trim());
         return connection.getMetaDataCache().getTable(new PTableKey(view.getTenantId(), parentName));


[04/50] [abbrv] phoenix git commit: Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/phoenix

Posted by ma...@apache.org.
Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/phoenix


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/ade12a78
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/ade12a78
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/ade12a78

Branch: refs/heads/calcite
Commit: ade12a787401d0bc67feb7e9c9dc171ca6cc27fb
Parents: df42fa1 be5aba5
Author: maryannxue <we...@intel.com>
Authored: Sat Jul 4 13:11:21 2015 -0400
Committer: maryannxue <we...@intel.com>
Committed: Sat Jul 4 13:11:21 2015 -0400

----------------------------------------------------------------------
 .../org/apache/phoenix/end2end/ArrayIT.java     | 119 +++++++++++++++++++
 .../expression/ArrayConstructorExpression.java  |  19 +--
 .../org/apache/phoenix/util/PhoenixRuntime.java |   3 +-
 .../java/org/apache/phoenix/util/QueryUtil.java |   4 +-
 .../org/apache/phoenix/util/SchemaUtil.java     |  18 ++-
 .../phoenix/pig/PhoenixHBaseLoaderIT.java       |   9 +-
 .../phoenix/pig/PhoenixHBaseStorerIT.java       |   4 +-
 .../apache/phoenix/pig/PhoenixHBaseStorage.java |  51 ++++----
 8 files changed, 178 insertions(+), 49 deletions(-)
----------------------------------------------------------------------



[33/50] [abbrv] phoenix git commit: PHOENIX-2067 Sort order incorrect for variable length DESC columns

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/index/IndexMaintainer.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/index/IndexMaintainer.java b/phoenix-core/src/main/java/org/apache/phoenix/index/IndexMaintainer.java
index 0956753..a12f633 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/index/IndexMaintainer.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/index/IndexMaintainer.java
@@ -59,14 +59,10 @@ import org.apache.phoenix.hbase.index.util.ImmutableBytesPtr;
 import org.apache.phoenix.hbase.index.util.KeyValueBuilder;
 import org.apache.phoenix.jdbc.PhoenixConnection;
 import org.apache.phoenix.jdbc.PhoenixStatement;
-import org.apache.phoenix.parse.AndParseNode;
-import org.apache.phoenix.parse.BaseParseNodeVisitor;
-import org.apache.phoenix.parse.BooleanParseNodeVisitor;
 import org.apache.phoenix.parse.FunctionParseNode;
 import org.apache.phoenix.parse.ParseNode;
 import org.apache.phoenix.parse.SQLParser;
 import org.apache.phoenix.parse.StatelessTraverseAllParseNodeVisitor;
-import org.apache.phoenix.parse.TraverseAllParseNodeVisitor;
 import org.apache.phoenix.parse.UDFParseNode;
 import org.apache.phoenix.query.QueryConstants;
 import org.apache.phoenix.schema.ColumnNotFoundException;
@@ -265,6 +261,7 @@ public class IndexMaintainer implements Writable, Iterable<ColumnReference> {
     private int[] dataPkPosition;
     private int maxTrailingNulls;
     private ColumnReference dataEmptyKeyValueRef;
+    private boolean rowKeyOrderOptimizable;
     
     private IndexMaintainer(RowKeySchema dataRowKeySchema, boolean isDataTableSalted) {
         this.dataRowKeySchema = dataRowKeySchema;
@@ -273,6 +270,7 @@ public class IndexMaintainer implements Writable, Iterable<ColumnReference> {
 
     private IndexMaintainer(PTable dataTable, PTable index, PhoenixConnection connection) {
         this(dataTable.getRowKeySchema(), dataTable.getBucketNum() != null);
+        this.rowKeyOrderOptimizable = index.rowKeyOrderOptimizable();
         this.isMultiTenant = dataTable.isMultiTenant();
         this.viewIndexId = index.getViewIndexId() == null ? null : MetaDataUtil.getViewIndexIdDataType().toBytes(index.getViewIndexId());
         this.isLocalIndex = index.getIndexType() == IndexType.LOCAL;
@@ -434,7 +432,7 @@ public class IndexMaintainer implements Writable, Iterable<ColumnReference> {
                 dataRowKeySchema.next(ptr, dataPosOffset, maxRowKeyOffset);
                 output.write(ptr.get(), ptr.getOffset(), ptr.getLength());
                 if (!dataRowKeySchema.getField(dataPosOffset).getDataType().isFixedWidth()) {
-                    output.writeByte(QueryConstants.SEPARATOR_BYTE);
+                    output.writeByte(SchemaUtil.getSeparatorByte(rowKeyOrderOptimizable, ptr.getLength()==0, dataRowKeySchema.getField(dataPosOffset)));
                 }
                 dataPosOffset++;
             }
@@ -481,21 +479,22 @@ public class IndexMaintainer implements Writable, Iterable<ColumnReference> {
                 }
                 boolean isDataColumnInverted = dataSortOrder != SortOrder.ASC;
                 PDataType indexColumnType = IndexUtil.getIndexColumnDataType(isNullable, dataColumnType);
-                boolean isBytesComparable = dataColumnType.isBytesComparableWith(indexColumnType) ;
-                if (isBytesComparable && isDataColumnInverted == descIndexColumnBitSet.get(i)) {
+                boolean isBytesComparable = dataColumnType.isBytesComparableWith(indexColumnType);
+                boolean isIndexColumnDesc = descIndexColumnBitSet.get(i);
+                if (isBytesComparable && isDataColumnInverted == isIndexColumnDesc) {
                     output.write(ptr.get(), ptr.getOffset(), ptr.getLength());
                 } else {
                     if (!isBytesComparable)  {
                         indexColumnType.coerceBytes(ptr, dataColumnType, dataSortOrder, SortOrder.getDefault());
                     }
-                    if (descIndexColumnBitSet.get(i) != isDataColumnInverted) {
+                    if (isDataColumnInverted != isIndexColumnDesc) {
                         writeInverted(ptr.get(), ptr.getOffset(), ptr.getLength(), output);
                     } else {
                         output.write(ptr.get(), ptr.getOffset(), ptr.getLength());
                     }
                 }
                 if (!indexColumnType.isFixedWidth()) {
-                    output.writeByte(QueryConstants.SEPARATOR_BYTE);
+                    output.writeByte(SchemaUtil.getSeparatorByte(rowKeyOrderOptimizable, ptr.getLength() == 0, isIndexColumnDesc ? SortOrder.DESC : SortOrder.ASC));
                 }
             }
             int length = stream.size();
@@ -545,7 +544,7 @@ public class IndexMaintainer implements Writable, Iterable<ColumnReference> {
                 indexRowKeySchema.next(ptr, indexPosOffset, maxRowKeyOffset);
                 output.write(ptr.get(), ptr.getOffset(), ptr.getLength());
                 if (!dataRowKeySchema.getField(dataPosOffset).getDataType().isFixedWidth()) {
-                    output.writeByte(QueryConstants.SEPARATOR_BYTE);
+                    output.writeByte(SchemaUtil.getSeparatorByte(rowKeyOrderOptimizable, ptr.getLength() == 0, dataRowKeySchema.getField(dataPosOffset)));
                 }
                 indexPosOffset++;
                 dataPosOffset++;
@@ -587,8 +586,11 @@ public class IndexMaintainer implements Writable, Iterable<ColumnReference> {
                         }
                     }
                 }
-                if (!dataRowKeySchema.getField(i).getDataType().isFixedWidth() && ((i+1) !=  dataRowKeySchema.getFieldCount())) {
-                    output.writeByte(QueryConstants.SEPARATOR_BYTE);
+                // Write separator byte if variable length unless it's the last field in the schema
+                // (but we still need to write it if it's DESC to ensure sort order is correct).
+                byte sepByte = SchemaUtil.getSeparatorByte(rowKeyOrderOptimizable, ptr.getLength() == 0, dataRowKeySchema.getField(i));
+                if (!dataRowKeySchema.getField(i).getDataType().isFixedWidth() && (((i+1) !=  dataRowKeySchema.getFieldCount()) || sepByte == QueryConstants.DESC_SEPARATOR_BYTE)) {
+                    output.writeByte(sepByte);
                 }
             }
             int length = stream.size();
@@ -658,6 +660,7 @@ public class IndexMaintainer implements Writable, Iterable<ColumnReference> {
     private RowKeySchema generateIndexRowKeySchema() {
         int nIndexedColumns = getIndexPkColumnCount() + (isMultiTenant ? 1 : 0) + (!isLocalIndex && nIndexSaltBuckets > 0 ? 1 : 0) + (viewIndexId != null ? 1 : 0) - getNumViewConstants();
         RowKeySchema.RowKeySchemaBuilder builder = new RowKeySchema.RowKeySchemaBuilder(nIndexedColumns);
+        builder.rowKeyOrderOptimizable(rowKeyOrderOptimizable);
         if (!isLocalIndex && nIndexSaltBuckets > 0) {
             builder.addField(SaltingUtil.SALTING_COLUMN, false, SortOrder.ASC);
             nIndexedColumns--;
@@ -708,44 +711,67 @@ public class IndexMaintainer implements Writable, Iterable<ColumnReference> {
             // same for all rows in this index)
             if (!viewConstantColumnBitSet.get(i)) {
                 int pos = rowKeyMetaData.getIndexPkPosition(i-dataPosOffset);
-                indexFields[pos] = dataRowKeySchema.getField(i);
+                Field dataField = dataRowKeySchema.getField(i);
+                indexFields[pos] = 
+                        dataRowKeySchema.getField(i);
             } 
         }
+        BitSet descIndexColumnBitSet = rowKeyMetaData.getDescIndexColumnBitSet();
         Iterator<Expression> expressionItr = indexedExpressions.iterator();
-        for (Field indexField : indexFields) {
-            if (indexField == null) { // Add field for kv column in index
-                final PDataType dataType = expressionItr.next().getDataType();
-                builder.addField(new PDatum() {
+        for (int i = 0; i < indexFields.length; i++) {
+            Field indexField = indexFields[i];
+            PDataType dataTypeToBe;
+            SortOrder sortOrderToBe;
+            boolean isNullableToBe;
+            Integer maxLengthToBe;
+            Integer scaleToBe;
+            if (indexField == null) {
+                Expression e = expressionItr.next();
+                isNullableToBe = true;
+                dataTypeToBe = IndexUtil.getIndexColumnDataType(isNullableToBe, e.getDataType());
+                sortOrderToBe = descIndexColumnBitSet.get(i) ? SortOrder.DESC : SortOrder.ASC;
+                maxLengthToBe = e.getMaxLength();
+                scaleToBe = e.getScale();
+            } else {
+                isNullableToBe = indexField.isNullable();
+                dataTypeToBe = IndexUtil.getIndexColumnDataType(isNullableToBe, indexField.getDataType());
+                sortOrderToBe = descIndexColumnBitSet.get(i) ? SortOrder.DESC : SortOrder.ASC;
+                maxLengthToBe = indexField.getMaxLength();
+                scaleToBe = indexField.getScale();
+            }
+            final PDataType dataType = dataTypeToBe;
+            final SortOrder sortOrder = sortOrderToBe;
+            final boolean isNullable = isNullableToBe;
+            final Integer maxLength = maxLengthToBe;
+            final Integer scale = scaleToBe;
+            builder.addField(new PDatum() {
 
-                    @Override
-                    public boolean isNullable() {
-                        return true;
-                    }
+                @Override
+                public boolean isNullable() {
+                    return isNullable;
+                }
 
-                    @Override
-                    public PDataType getDataType() {
-                        return IndexUtil.getIndexColumnDataType(true, dataType);
-                    }
+                @Override
+                public PDataType getDataType() {
+                    return dataType;
+                }
 
-                    @Override
-                    public Integer getMaxLength() {
-                        return null;
-                    }
+                @Override
+                public Integer getMaxLength() {
+                    return maxLength;
+                }
 
-                    @Override
-                    public Integer getScale() {
-                        return null;
-                    }
+                @Override
+                public Integer getScale() {
+                    return scale;
+                }
 
-                    @Override
-                    public SortOrder getSortOrder() {
-                        return SortOrder.getDefault();
-                    }
-                    
-                }, true, SortOrder.getDefault());
-            } else { // add field from data row key
-                builder.addField(indexField);
-            }
+                @Override
+                public SortOrder getSortOrder() {
+                    return sortOrder;
+                }
+                
+            }, true, sortOrder);
         }
         return builder.build();
     }
@@ -928,9 +954,18 @@ public class IndexMaintainer implements Writable, Iterable<ColumnReference> {
             byte[] cq = Bytes.readByteArray(input);
             coveredColumns.add(new ColumnReference(cf,cq));
         }
-        indexTableName = Bytes.readByteArray(input);
-        dataEmptyKeyValueCF = Bytes.readByteArray(input);
+        // Hack to serialize whether the index row key is optimizable
         int len = WritableUtils.readVInt(input);
+        if (len < 0) {
+            rowKeyOrderOptimizable = false;
+            len *= -1;
+        } else {
+            rowKeyOrderOptimizable = true;
+        }
+        indexTableName = new byte[len];
+        input.readFully(indexTableName, 0, len);
+        dataEmptyKeyValueCF = Bytes.readByteArray(input);
+        len = WritableUtils.readVInt(input);
         //TODO remove this in the next major release
         boolean isNewClient = false;
         if (len < 0) {
@@ -1023,7 +1058,9 @@ public class IndexMaintainer implements Writable, Iterable<ColumnReference> {
             Bytes.writeByteArray(output, ref.getFamily());
             Bytes.writeByteArray(output, ref.getQualifier());
         }
-        Bytes.writeByteArray(output, indexTableName);
+        // TODO: remove when rowKeyOrderOptimizable hack no longer needed
+        WritableUtils.writeVInt(output,indexTableName.length * (rowKeyOrderOptimizable ? 1 : -1));
+        output.write(indexTableName, 0, indexTableName.length);
         Bytes.writeByteArray(output, dataEmptyKeyValueCF);
         // TODO in order to maintain b/w compatibility encode emptyKeyValueCFPtr.getLength() as a negative value (so we can distinguish between new and old clients)
         // when indexedColumnTypes is removed, remove this 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/iterate/BaseResultIterators.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/iterate/BaseResultIterators.java b/phoenix-core/src/main/java/org/apache/phoenix/iterate/BaseResultIterators.java
index cf66d93..a0aefaa 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/iterate/BaseResultIterators.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/iterate/BaseResultIterators.java
@@ -51,6 +51,8 @@ import org.apache.phoenix.compile.QueryPlan;
 import org.apache.phoenix.compile.RowProjector;
 import org.apache.phoenix.compile.ScanRanges;
 import org.apache.phoenix.compile.StatementContext;
+import org.apache.phoenix.coprocessor.BaseScannerRegionObserver;
+import org.apache.phoenix.coprocessor.UngroupedAggregateRegionObserver;
 import org.apache.phoenix.exception.SQLExceptionCode;
 import org.apache.phoenix.exception.SQLExceptionInfo;
 import org.apache.phoenix.filter.ColumnProjectionFilter;
@@ -67,7 +69,6 @@ import org.apache.phoenix.schema.PColumnFamily;
 import org.apache.phoenix.schema.PTable;
 import org.apache.phoenix.schema.PTable.IndexType;
 import org.apache.phoenix.schema.PTable.ViewType;
-import org.apache.phoenix.schema.SaltingUtil;
 import org.apache.phoenix.schema.StaleRegionBoundaryCacheException;
 import org.apache.phoenix.schema.TableRef;
 import org.apache.phoenix.schema.stats.GuidePostsInfo;
@@ -134,6 +135,68 @@ public abstract class BaseResultIterators extends ExplainTable implements Result
         return true;
     }
     
+    private static void initializeScan(QueryPlan plan, Integer perScanLimit) {
+        StatementContext context = plan.getContext();
+        TableRef tableRef = plan.getTableRef();
+        PTable table = tableRef.getTable();
+        Scan scan = context.getScan();
+
+        Map<byte [], NavigableSet<byte []>> familyMap = scan.getFamilyMap();
+        // Hack for PHOENIX-2067 to force raw scan over all KeyValues to fix their row keys
+        if (context.getConnection().isDescVarLengthRowKeyUpgrade()) {
+            // We project *all* KeyValues across all column families as we make a pass over
+            // a physical table and we want to make sure we catch all KeyValues that may be
+            // dynamic or part of an updatable view.
+            familyMap.clear();
+            scan.setMaxVersions();
+            scan.setFilter(null); // Remove any filter
+            scan.setRaw(true); // Traverse (and subsequently clone) all KeyValues
+            // Pass over PTable so we can re-write rows according to the row key schema
+            scan.setAttribute(BaseScannerRegionObserver.UPGRADE_DESC_ROW_KEY, UngroupedAggregateRegionObserver.serialize(table));
+        } else {
+            FilterableStatement statement = plan.getStatement();
+            RowProjector projector = plan.getProjector();
+            boolean keyOnlyFilter = familyMap.isEmpty() && context.getWhereCoditionColumns().isEmpty();
+            if (projector.isProjectEmptyKeyValue()) {
+                // If nothing projected into scan and we only have one column family, just allow everything
+                // to be projected and use a FirstKeyOnlyFilter to skip from row to row. This turns out to
+                // be quite a bit faster.
+                // Where condition columns also will get added into familyMap
+                // When where conditions are present, we can not add FirstKeyOnlyFilter at beginning.
+                if (familyMap.isEmpty() && context.getWhereCoditionColumns().isEmpty()
+                        && table.getColumnFamilies().size() == 1) {
+                    // Project the one column family. We must project a column family since it's possible
+                    // that there are other non declared column families that we need to ignore.
+                    scan.addFamily(table.getColumnFamilies().get(0).getName().getBytes());
+                } else {
+                    byte[] ecf = SchemaUtil.getEmptyColumnFamily(table);
+                    // Project empty key value unless the column family containing it has
+                    // been projected in its entirety.
+                    if (!familyMap.containsKey(ecf) || familyMap.get(ecf) != null) {
+                        scan.addColumn(ecf, QueryConstants.EMPTY_COLUMN_BYTES);
+                    }
+                }
+            } else if (table.getViewType() == ViewType.MAPPED) {
+                // Since we don't have the empty key value in MAPPED tables, we must select all CFs in HRS. But only the
+                // selected column values are returned back to client
+                for (PColumnFamily family : table.getColumnFamilies()) {
+                    scan.addFamily(family.getName().getBytes());
+                }
+            }
+            // Add FirstKeyOnlyFilter if there are no references to key value columns
+            if (keyOnlyFilter) {
+                ScanUtil.andFilterAtBeginning(scan, new FirstKeyOnlyFilter());
+            }
+            
+            // TODO adding all CFs here is not correct. It should be done only after ColumnProjectionOptimization.
+            if (perScanLimit != null) {
+                ScanUtil.andFilterAtEnd(scan, new PageFilter(perScanLimit));
+            }
+    
+            doColumnProjectionOptimization(context, scan, table, statement);
+        }
+    }
+    
     public BaseResultIterators(QueryPlan plan, Integer perScanLimit, ParallelScanGrouper scanGrouper) throws SQLException {
         super(plan.getContext(), plan.getTableRef(), plan.getGroupBy(), plan.getOrderBy(), plan.getStatement().getHint(), plan.getLimit());
         this.plan = plan;
@@ -141,52 +204,12 @@ public abstract class BaseResultIterators extends ExplainTable implements Result
         StatementContext context = plan.getContext();
         TableRef tableRef = plan.getTableRef();
         PTable table = tableRef.getTable();
-        FilterableStatement statement = plan.getStatement();
-        RowProjector projector = plan.getProjector();
         physicalTableName = table.getPhysicalName().getBytes();
         tableStats = useStats() ? new MetaDataClient(context.getConnection()).getTableStats(table) : PTableStats.EMPTY_STATS;
-        Scan scan = context.getScan();
         // Used to tie all the scans together during logging
         scanId = UUID.randomUUID().toString();
-        Map<byte [], NavigableSet<byte []>> familyMap = scan.getFamilyMap();
-        boolean keyOnlyFilter = familyMap.isEmpty() && context.getWhereCoditionColumns().isEmpty();
-        if (projector.isProjectEmptyKeyValue()) {
-            // If nothing projected into scan and we only have one column family, just allow everything
-            // to be projected and use a FirstKeyOnlyFilter to skip from row to row. This turns out to
-            // be quite a bit faster.
-            // Where condition columns also will get added into familyMap
-            // When where conditions are present, we can not add FirstKeyOnlyFilter at beginning.
-            if (familyMap.isEmpty() && context.getWhereCoditionColumns().isEmpty()
-                    && table.getColumnFamilies().size() == 1) {
-                // Project the one column family. We must project a column family since it's possible
-                // that there are other non declared column families that we need to ignore.
-                scan.addFamily(table.getColumnFamilies().get(0).getName().getBytes());
-            } else {
-                byte[] ecf = SchemaUtil.getEmptyColumnFamily(table);
-                // Project empty key value unless the column family containing it has
-                // been projected in its entirety.
-                if (!familyMap.containsKey(ecf) || familyMap.get(ecf) != null) {
-                    scan.addColumn(ecf, QueryConstants.EMPTY_COLUMN_BYTES);
-                }
-            }
-        } else if (table.getViewType() == ViewType.MAPPED) {
-            // Since we don't have the empty key value in MAPPED tables, we must select all CFs in HRS. But only the
-            // selected column values are returned back to client
-            for (PColumnFamily family : table.getColumnFamilies()) {
-                scan.addFamily(family.getName().getBytes());
-            }
-        }
-        // Add FirstKeyOnlyFilter if there are no references to key value columns
-        if (keyOnlyFilter) {
-            ScanUtil.andFilterAtBeginning(scan, new FirstKeyOnlyFilter());
-        }
         
-        // TODO adding all CFs here is not correct. It should be done only after ColumnProjectionOptimization.
-        if (perScanLimit != null) {
-            ScanUtil.andFilterAtEnd(scan, new PageFilter(perScanLimit));
-        }
-
-        doColumnProjectionOptimization(context, scan, table, statement);
+        initializeScan(plan, perScanLimit);
         
         this.scans = getParallelScans();
         List<KeyRange> splitRanges = Lists.newArrayListWithExpectedSize(scans.size() * ESTIMATED_GUIDEPOSTS_PER_REGION);
@@ -200,7 +223,7 @@ public abstract class BaseResultIterators extends ExplainTable implements Result
         this.allFutures = Lists.newArrayListWithExpectedSize(1);
     }
 
-    private void doColumnProjectionOptimization(StatementContext context, Scan scan, PTable table, FilterableStatement statement) {
+    private static void doColumnProjectionOptimization(StatementContext context, Scan scan, PTable table, FilterableStatement statement) {
         Map<byte[], NavigableSet<byte[]>> familyMap = scan.getFamilyMap();
         if (familyMap != null && !familyMap.isEmpty()) {
             // columnsTracker contain cf -> qualifiers which should get returned.

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/iterate/OrderedResultIterator.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/iterate/OrderedResultIterator.java b/phoenix-core/src/main/java/org/apache/phoenix/iterate/OrderedResultIterator.java
index 7b7d4dc..d999ecb 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/iterate/OrderedResultIterator.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/iterate/OrderedResultIterator.java
@@ -27,15 +27,17 @@ import java.util.List;
 
 import org.apache.hadoop.hbase.client.Result;
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.phoenix.execute.DescVarLengthFastByteComparisons;
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.expression.OrderByExpression;
+import org.apache.phoenix.schema.SortOrder;
+import org.apache.phoenix.schema.tuple.Tuple;
+import org.apache.phoenix.util.SizedUtil;
 
 import com.google.common.base.Function;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Ordering;
-import org.apache.phoenix.expression.Expression;
-import org.apache.phoenix.expression.OrderByExpression;
-import org.apache.phoenix.schema.tuple.Tuple;
-import org.apache.phoenix.util.SizedUtil;
 
 /**
  * Result scanner that sorts aggregated rows by columns specified in the ORDER BY clause.
@@ -155,7 +157,12 @@ public class OrderedResultIterator implements PeekingResultIterator {
         Ordering<ResultEntry> ordering = null;
         int pos = 0;
         for (OrderByExpression col : orderByExpressions) {
-            Ordering<ImmutableBytesWritable> o = Ordering.from(new ImmutableBytesWritable.Comparator());
+            Expression e = col.getExpression();
+            Comparator<ImmutableBytesWritable> comparator = 
+                    e.getSortOrder() == SortOrder.DESC && !e.getDataType().isFixedWidth() 
+                    ? buildDescVarLengthComparator() 
+                    : new ImmutableBytesWritable.Comparator();
+            Ordering<ImmutableBytesWritable> o = Ordering.from(comparator);
             if(!col.isAscending()) o = o.reverse();
             o = col.isNullsLast() ? o.nullsLast() : o.nullsFirst();
             Ordering<ResultEntry> entryOrdering = o.onResultOf(new NthKey(pos++));
@@ -164,6 +171,23 @@ public class OrderedResultIterator implements PeekingResultIterator {
         return ordering;
     }
 
+    /*
+     * Same as regular comparator, but if all the bytes match and the length is
+     * different, returns the longer length as bigger.
+     */
+    private static Comparator<ImmutableBytesWritable> buildDescVarLengthComparator() {
+        return new Comparator<ImmutableBytesWritable>() {
+
+            @Override
+            public int compare(ImmutableBytesWritable o1, ImmutableBytesWritable o2) {
+                return DescVarLengthFastByteComparisons.compareTo(
+                        o1.get(), o1.getOffset(), o1.getLength(),
+                        o2.get(), o2.getOffset(), o2.getLength());
+            }
+            
+        };
+    }
+    
     @Override
     public Tuple next() throws SQLException {
         return getResultIterator().next();
@@ -252,13 +276,13 @@ public class OrderedResultIterator implements PeekingResultIterator {
         planSteps.add("CLIENT" + (limit == null ? "" : " TOP " + limit + " ROW"  + (limit == 1 ? "" : "S"))  + " SORTED BY " + orderByExpressions.toString());
     }
 
-	@Override
-	public String toString() {
-		return "OrderedResultIterator [thresholdBytes=" + thresholdBytes
-				+ ", limit=" + limit + ", delegate=" + delegate
-				+ ", orderByExpressions=" + orderByExpressions
-				+ ", estimatedByteSize=" + estimatedByteSize
-				+ ", resultIterator=" + resultIterator + ", byteSize="
-				+ byteSize + "]";
-	}
+    @Override
+    public String toString() {
+        return "OrderedResultIterator [thresholdBytes=" + thresholdBytes
+                + ", limit=" + limit + ", delegate=" + delegate
+                + ", orderByExpressions=" + orderByExpressions
+                + ", estimatedByteSize=" + estimatedByteSize
+                + ", resultIterator=" + resultIterator + ", byteSize="
+                + byteSize + "]";
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java
index 5805999..0cd76fe 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java
@@ -140,6 +140,9 @@ public class PhoenixConnection implements Connection, org.apache.phoenix.jdbc.Jd
     private Consistency consistency = Consistency.STRONG;
     private Map<String, String> customTracingAnnotations = emptyMap();
     private final boolean isRequestLevelMetricsEnabled;
+    private final boolean isDescVarLengthRowKeyUpgrade;
+    
+    
     static {
         Tracing.addTraceMetricsSource();
     }
@@ -150,28 +153,35 @@ public class PhoenixConnection implements Connection, org.apache.phoenix.jdbc.Jd
         return props;
     }
 
-    public PhoenixConnection(PhoenixConnection connection) throws SQLException {
-        this(connection.getQueryServices(), connection.getURL(), connection.getClientInfo(), connection.getMetaDataCache());
+    public PhoenixConnection(PhoenixConnection connection, boolean isDescRowKeyOrderUpgrade) throws SQLException {
+        this(connection.getQueryServices(), connection.getURL(), connection.getClientInfo(), connection.getMetaDataCache(), isDescRowKeyOrderUpgrade);
         this.isAutoCommit = connection.isAutoCommit;
         this.sampler = connection.sampler;
         this.statementExecutionCounter = connection.statementExecutionCounter;
     }
+
+    public PhoenixConnection(PhoenixConnection connection) throws SQLException {
+        this(connection, connection.isDescVarLengthRowKeyUpgrade);
+    }
     
     public PhoenixConnection(PhoenixConnection connection, long scn) throws SQLException {
         this(connection.getQueryServices(), connection, scn);
-        this.sampler = connection.sampler;
-        this.statementExecutionCounter = connection.statementExecutionCounter;
     }
     
     public PhoenixConnection(ConnectionQueryServices services, PhoenixConnection connection, long scn) throws SQLException {
-        this(services, connection.getURL(), newPropsWithSCN(scn,connection.getClientInfo()), connection.getMetaDataCache());
+        this(services, connection.getURL(), newPropsWithSCN(scn,connection.getClientInfo()), connection.getMetaDataCache(), connection.isDescVarLengthRowKeyUpgrade());
         this.isAutoCommit = connection.isAutoCommit;
         this.sampler = connection.sampler;
         this.statementExecutionCounter = connection.statementExecutionCounter;
     }
     
     public PhoenixConnection(ConnectionQueryServices services, String url, Properties info, PMetaData metaData) throws SQLException {
+        this(services, url, info, metaData, false);
+    }
+
+    public PhoenixConnection(ConnectionQueryServices services, String url, Properties info, PMetaData metaData, boolean isDescVarLengthRowKeyUpgrade) throws SQLException {
         this.url = url;
+        this.isDescVarLengthRowKeyUpgrade = isDescVarLengthRowKeyUpgrade;
         // Copy so client cannot change
         this.info = info == null ? new Properties() : PropertiesUtil.deepCopy(info);
         final PName tenantId = JDBCUtil.getTenantId(url, info);
@@ -887,4 +897,13 @@ public class PhoenixConnection implements Connection, org.apache.phoenix.jdbc.Jd
             mutationState.getReadMetricQueue().clearMetrics();
         }
     }
+
+    /**
+     * Returns true if this connection is being used to upgrade the
+     * data due to PHOENIX-2067 and false otherwise.
+     * @return
+     */
+    public boolean isDescVarLengthRowKeyUpgrade() {
+        return isDescVarLengthRowKeyUpgrade;
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
index 9cd32e8..feb5989 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
@@ -162,6 +162,7 @@ import org.apache.phoenix.util.UpgradeUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Joiner;
 import com.google.common.base.Stopwatch;
 import com.google.common.base.Throwables;
 import com.google.common.cache.Cache;
@@ -1831,7 +1832,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
      * This closes the passed connection.
      */
     private PhoenixConnection addColumn(PhoenixConnection oldMetaConnection, String tableName, long timestamp, String columns, boolean addIfNotExists) throws SQLException {
-        Properties props = new Properties(oldMetaConnection.getClientInfo());
+        Properties props = PropertiesUtil.deepCopy(oldMetaConnection.getClientInfo());
         props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(timestamp));
         // Cannot go through DriverManager or you end up in an infinite loop because it'll call init again
         PhoenixConnection metaConnection = new PhoenixConnection(this, oldMetaConnection.getURL(), props, oldMetaConnection.getMetaDataCache());
@@ -1970,6 +1971,20 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
                                          */
                                         logger.debug("No need to run 4.5 upgrade");
                                     }
+                                    Properties props = PropertiesUtil.deepCopy(metaConnection.getClientInfo());
+                                    props.remove(PhoenixRuntime.CURRENT_SCN_ATTRIB);
+                                    props.remove(PhoenixRuntime.TENANT_ID_ATTRIB);
+                                    PhoenixConnection conn = new PhoenixConnection(ConnectionQueryServicesImpl.this, metaConnection.getURL(), props, metaConnection.getMetaDataCache());
+                                    try {
+                                        Set<String> tablesNeedingUpgrade = UpgradeUtil.getPhysicalTablesWithDescVarLengthRowKey(conn);
+                                        if (!tablesNeedingUpgrade.isEmpty()) {
+                                            logger.warn("The following tables require upgrade due to a bug causing the row key to be incorrect for descending columns (PHOENIX-2067):\n" + Joiner.on(' ').join(tablesNeedingUpgrade));
+                                        }
+                                    } catch (Exception ex) {
+                                        logger.error("Unable to determine tables requiring upgrade due to PHOENIX-2067", ex);
+                                    } finally {
+                                        conn.close();
+                                    }
                                 }
                             }
                             int nSaltBuckets = ConnectionQueryServicesImpl.this.props.getInt(QueryServices.SEQUENCE_SALT_BUCKETS_ATTRIB,

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/query/KeyRange.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/KeyRange.java b/phoenix-core/src/main/java/org/apache/phoenix/query/KeyRange.java
index afcc741..bca55e8 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/KeyRange.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/KeyRange.java
@@ -17,8 +17,6 @@
  */
 package org.apache.phoenix.query;
 
-import static org.apache.phoenix.query.QueryConstants.SEPARATOR_BYTE_ARRAY;
-
 import java.io.DataInput;
 import java.io.DataOutput;
 import java.io.IOException;
@@ -394,18 +392,6 @@ public class KeyRange implements Writable {
         return lowerRange == KeyRange.EMPTY_RANGE.getLowerRange() && upperRange == KeyRange.EMPTY_RANGE.getUpperRange();
     }
 
-    public KeyRange appendSeparator() {
-        byte[] lowerBound = getLowerRange();
-        byte[] upperBound = getUpperRange();
-        if (lowerBound != UNBOUND) {
-            lowerBound = ByteUtil.concat(lowerBound, SEPARATOR_BYTE_ARRAY);
-        }
-        if (upperBound != UNBOUND) {
-            upperBound = ByteUtil.concat(upperBound, SEPARATOR_BYTE_ARRAY);
-        }
-        return getKeyRange(lowerBound, lowerInclusive, upperBound, upperInclusive);
-    }
-
     /**
      * @return list of at least size 1
      */

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java
index d095049..92479b3 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java
@@ -114,6 +114,7 @@ import org.apache.phoenix.hbase.index.util.ImmutableBytesPtr;
 import org.apache.phoenix.schema.MetaDataSplitPolicy;
 import org.apache.phoenix.schema.PName;
 import org.apache.phoenix.schema.PNameFactory;
+import org.apache.phoenix.schema.SortOrder;
 import org.apache.phoenix.util.ByteUtil;
 
 
@@ -163,6 +164,8 @@ public interface QueryConstants {
      */
     public static final byte SEPARATOR_BYTE = (byte) 0;
     public static final byte[] SEPARATOR_BYTE_ARRAY = new byte[] {SEPARATOR_BYTE};
+    public static final byte DESC_SEPARATOR_BYTE = SortOrder.invert(SEPARATOR_BYTE);
+    public static final byte[] DESC_SEPARATOR_BYTE_ARRAY = new byte[] {DESC_SEPARATOR_BYTE};
 
     public static final String DEFAULT_COPROCESS_PATH = "phoenix.jar";
     public final static int MILLIS_IN_DAY = 1000 * 60 * 60 * 24;

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/DelegateTable.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/DelegateTable.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/DelegateTable.java
index 2a43679..0251da1 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/DelegateTable.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/DelegateTable.java
@@ -241,4 +241,9 @@ public class DelegateTable implements PTable {
     public int getBaseColumnCount() {
         return delegate.getBaseColumnCount();
     }
+
+    @Override
+    public boolean rowKeyOrderOptimizable() {
+        return delegate.rowKeyOrderOptimizable();
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
index 9ad52a5..b1fcf30 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
@@ -189,6 +189,7 @@ import org.apache.phoenix.util.PhoenixRuntime;
 import org.apache.phoenix.util.ReadOnlyProps;
 import org.apache.phoenix.util.SchemaUtil;
 import org.apache.phoenix.util.StringUtil;
+import org.apache.phoenix.util.UpgradeUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -702,8 +703,13 @@ public class MetaDataClient {
                     sortOrder = pkSortOrder.getSecond();
                 }
             }
-
             String columnName = columnDefName.getColumnName();
+            if (isPK && sortOrder == SortOrder.DESC && def.getDataType() == PVarbinary.INSTANCE) {
+                throw new SQLExceptionInfo.Builder(SQLExceptionCode.DESC_VARBINARY_NOT_SUPPORTED)
+                    .setColumnName(columnName)
+                    .build().buildException();
+            }
+
             PName familyName = null;
             if (def.isPK() && !pkConstraint.getColumnNames().isEmpty() ) {
                 throw new SQLExceptionInfo.Builder(SQLExceptionCode.PRIMARY_KEY_ALREADY_EXISTS)
@@ -1432,6 +1438,8 @@ public class MetaDataClient {
             String parentTableName = null;
             PName tenantId = connection.getTenantId();
             String tenantIdStr = tenantId == null ? null : connection.getTenantId().getString();
+            Long scn = connection.getSCN();
+            long clientTimeStamp = scn == null ? HConstants.LATEST_TIMESTAMP : scn;
             boolean multiTenant = false;
             boolean storeNulls = false;
             Integer saltBucketNum = null;
@@ -1439,6 +1447,7 @@ public class MetaDataClient {
             boolean isImmutableRows = false;
             List<PName> physicalNames = Collections.emptyList();
             boolean addSaltColumn = false;
+            boolean rowKeyOrderOptimizable = true;
             if (parent != null && tableType == PTableType.INDEX) {
                 // Index on view
                 // TODO: Can we support a multi-tenant index directly on a multi-tenant
@@ -1464,7 +1473,7 @@ public class MetaDataClient {
                 parentTableName = parent.getTableName().getString();
                 // Pass through data table sequence number so we can check it hasn't changed
                 PreparedStatement incrementStatement = connection.prepareStatement(INCREMENT_SEQ_NUM);
-                incrementStatement.setString(1, connection.getTenantId() == null ? null : connection.getTenantId().getString());
+                incrementStatement.setString(1, tenantIdStr);
                 incrementStatement.setString(2, schemaName);
                 incrementStatement.setString(3, parentTableName);
                 incrementStatement.setLong(4, parent.getSequenceNumber());
@@ -1476,7 +1485,7 @@ public class MetaDataClient {
 
                 // Add row linking from data table row to index table row
                 PreparedStatement linkStatement = connection.prepareStatement(CREATE_LINK);
-                linkStatement.setString(1, connection.getTenantId() == null ? null : connection.getTenantId().getString());
+                linkStatement.setString(1, tenantIdStr);
                 linkStatement.setString(2, schemaName);
                 linkStatement.setString(3, parentTableName);
                 linkStatement.setString(4, tableName);
@@ -1589,6 +1598,12 @@ public class MetaDataClient {
                 } else {
                     // Propagate property values to VIEW.
                     // TODO: formalize the known set of these properties
+                    // Manually transfer the ROW_KEY_ORDER_OPTIMIZABLE_BYTES from parent as we don't
+                    // want to add this hacky flag to the schema (see PHOENIX-2067).
+                    rowKeyOrderOptimizable = parent.rowKeyOrderOptimizable();
+                    if (rowKeyOrderOptimizable) {
+                        UpgradeUtil.addRowKeyOrderOptimizableCell(tableMetaData, SchemaUtil.getTableKey(tenantIdStr, schemaName, tableName), clientTimeStamp);
+                    }
                     multiTenant = parent.isMultiTenant();
                     saltBucketNum = parent.getBucketNum();
                     isImmutableRows = parent.isImmutableRows();
@@ -1606,7 +1621,7 @@ public class MetaDataClient {
                     // FIXME: not currently used, but see PHOENIX-1367
                     // as fixing that will require it's usage.
                     PreparedStatement linkStatement = connection.prepareStatement(CREATE_VIEW_LINK);
-                    linkStatement.setString(1, connection.getTenantId() == null ? null : connection.getTenantId().getString());
+                    linkStatement.setString(1, tenantIdStr);
                     linkStatement.setString(2, schemaName);
                     linkStatement.setString(3, tableName);
                     linkStatement.setString(4, parent.getName().getString());
@@ -1629,7 +1644,7 @@ public class MetaDataClient {
                     // Add row linking from data table row to physical table row
                     PreparedStatement linkStatement = connection.prepareStatement(CREATE_LINK);
                     for (PName physicalName : physicalNames) {
-                        linkStatement.setString(1, connection.getTenantId() == null ? null : connection.getTenantId().getString());
+                        linkStatement.setString(1, tenantIdStr);
                         linkStatement.setString(2, schemaName);
                         linkStatement.setString(3, tableName);
                         linkStatement.setString(4, physicalName.getString());
@@ -1788,7 +1803,7 @@ public class MetaDataClient {
                         Collections.<PTable>emptyList(), isImmutableRows,
                         Collections.<PName>emptyList(), defaultFamilyName == null ? null :
                                 PNameFactory.newName(defaultFamilyName), null,
-                        Boolean.TRUE.equals(disableWAL), false, false, null, indexId, indexType);
+                        Boolean.TRUE.equals(disableWAL), false, false, null, indexId, indexType, true);
                 connection.addTable(table);
             } else if (tableType == PTableType.INDEX && indexId == null) {
                 if (tableProps.get(HTableDescriptor.MAX_FILESIZE) == null) {
@@ -1942,7 +1957,7 @@ public class MetaDataClient {
                         PTable.INITIAL_SEQ_NUM, pkName == null ? null : PNameFactory.newName(pkName), saltBucketNum, columns,
                         dataTableName == null ? null : newSchemaName, dataTableName == null ? null : PNameFactory.newName(dataTableName), Collections.<PTable>emptyList(), isImmutableRows,
                         physicalNames, defaultFamilyName == null ? null : PNameFactory.newName(defaultFamilyName), viewStatement, Boolean.TRUE.equals(disableWAL), multiTenant, storeNulls, viewType,
-                        indexId, indexType);
+                        indexId, indexType, rowKeyOrderOptimizable);
                 result = new MetaDataMutationResult(code, result.getMutationTime(), table, true);
                 addTableToCache(result);
                 return table;
@@ -2889,6 +2904,8 @@ public class MetaDataClient {
             if (code == MutationCode.TABLE_ALREADY_EXISTS) {
                 if (result.getTable() != null) { // To accommodate connection-less update of index state
                     addTableToCache(result);
+                    // Set so that we get the table below with the potentially modified rowKeyOrderOptimizable flag set
+                    indexRef.setTable(result.getTable());
                 }
             }
             if (newIndexState == PIndexState.BUILDING) {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/PTable.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/PTable.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/PTable.java
index b983074..8da2206 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/PTable.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/PTable.java
@@ -323,4 +323,13 @@ public interface PTable extends PMetaDataEntity {
     IndexType getIndexType();
     PTableStats getTableStats();
     int getBaseColumnCount();
+
+    /**
+     * Determines whether or not we may optimize out an ORDER BY or do a GROUP BY
+     * in-place when the optimizer tells us it's possible. This is due to PHOENIX-2067
+     * and only applicable for tables using DESC primary key column(s) which have
+     * not been upgraded.
+     * @return true if optimizations row key order optimizations are possible
+     */
+    boolean rowKeyOrderOptimizable();
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableImpl.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableImpl.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableImpl.java
index a947bfc..1756c2f 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableImpl.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableImpl.java
@@ -19,7 +19,6 @@ package org.apache.phoenix.schema;
 
 import static org.apache.phoenix.hbase.index.util.KeyValueBuilder.addQuietly;
 import static org.apache.phoenix.hbase.index.util.KeyValueBuilder.deleteQuietly;
-import static org.apache.phoenix.query.QueryConstants.SEPARATOR_BYTE;
 import static org.apache.phoenix.schema.SaltingUtil.SALTING_COLUMN;
 
 import java.io.IOException;
@@ -126,6 +125,8 @@ public class PTableImpl implements PTable {
     private IndexType indexType;
     private PTableStats tableStats = PTableStats.EMPTY_STATS;
     private int baseColumnCount;
+    private boolean hasDescVarLengthColumns;
+    private boolean rowKeyOrderOptimizable;
 
     public PTableImpl() {
         this.indexes = Collections.emptyList();
@@ -195,7 +196,8 @@ public class PTableImpl implements PTable {
                 table.getTenantId(), table.getSchemaName(), table.getTableName(), table.getType(), table.getIndexState(), timeStamp,
                 table.getSequenceNumber(), table.getPKName(), table.getBucketNum(), getColumnsToClone(table), parentSchemaName, table.getParentTableName(),
                 indexes, table.isImmutableRows(), table.getPhysicalNames(), table.getDefaultFamilyName(), viewStatement,
-                table.isWALDisabled(), table.isMultiTenant(), table.getStoreNulls(), table.getViewType(), table.getViewIndexId(), table.getIndexType(), table.getTableStats(), table.getBaseColumnCount());
+                table.isWALDisabled(), table.isMultiTenant(), table.getStoreNulls(), table.getViewType(), table.getViewIndexId(), table.getIndexType(),
+                table.getTableStats(), table.getBaseColumnCount(), table.rowKeyOrderOptimizable());
     }
 
     public static PTableImpl makePTable(PTable table, List<PColumn> columns) throws SQLException {
@@ -203,7 +205,8 @@ public class PTableImpl implements PTable {
                 table.getTenantId(), table.getSchemaName(), table.getTableName(), table.getType(), table.getIndexState(), table.getTimeStamp(),
                 table.getSequenceNumber(), table.getPKName(), table.getBucketNum(), columns, table.getParentSchemaName(), table.getParentTableName(),
                 table.getIndexes(), table.isImmutableRows(), table.getPhysicalNames(), table.getDefaultFamilyName(), table.getViewStatement(),
-                table.isWALDisabled(), table.isMultiTenant(), table.getStoreNulls(), table.getViewType(), table.getViewIndexId(), table.getIndexType(), table.getTableStats(), table.getBaseColumnCount());
+                table.isWALDisabled(), table.isMultiTenant(), table.getStoreNulls(), table.getViewType(), table.getViewIndexId(), table.getIndexType(),
+                table.getTableStats(), table.getBaseColumnCount(), table.rowKeyOrderOptimizable());
     }
 
     public static PTableImpl makePTable(PTable table, long timeStamp, long sequenceNumber, List<PColumn> columns) throws SQLException {
@@ -211,7 +214,8 @@ public class PTableImpl implements PTable {
                 table.getTenantId(), table.getSchemaName(), table.getTableName(), table.getType(), table.getIndexState(), timeStamp,
                 sequenceNumber, table.getPKName(), table.getBucketNum(), columns, table.getParentSchemaName(), table.getParentTableName(), table.getIndexes(),
                 table.isImmutableRows(), table.getPhysicalNames(), table.getDefaultFamilyName(), table.getViewStatement(), table.isWALDisabled(),
-                table.isMultiTenant(), table.getStoreNulls(), table.getViewType(), table.getViewIndexId(), table.getIndexType(), table.getTableStats(), table.getBaseColumnCount());
+                table.isMultiTenant(), table.getStoreNulls(), table.getViewType(), table.getViewIndexId(), table.getIndexType(), table.getTableStats(),
+                table.getBaseColumnCount(), table.rowKeyOrderOptimizable());
     }
 
     public static PTableImpl makePTable(PTable table, long timeStamp, long sequenceNumber, List<PColumn> columns, boolean isImmutableRows) throws SQLException {
@@ -219,7 +223,8 @@ public class PTableImpl implements PTable {
                 table.getTenantId(), table.getSchemaName(), table.getTableName(), table.getType(), table.getIndexState(), timeStamp,
                 sequenceNumber, table.getPKName(), table.getBucketNum(), columns, table.getParentSchemaName(), table.getParentTableName(),
                 table.getIndexes(), isImmutableRows, table.getPhysicalNames(), table.getDefaultFamilyName(), table.getViewStatement(),
-                table.isWALDisabled(), table.isMultiTenant(), table.getStoreNulls(), table.getViewType(), table.getViewIndexId(), table.getIndexType(), table.getTableStats(), table.getBaseColumnCount());
+                table.isWALDisabled(), table.isMultiTenant(), table.getStoreNulls(), table.getViewType(), table.getViewIndexId(),
+                table.getIndexType(), table.getTableStats(), table.getBaseColumnCount(), table.rowKeyOrderOptimizable());
     }
     
     public static PTableImpl makePTable(PTable table, long timeStamp, long sequenceNumber, List<PColumn> columns, boolean isImmutableRows, boolean isWalDisabled, boolean isMultitenant, boolean storeNulls) throws SQLException {
@@ -227,7 +232,8 @@ public class PTableImpl implements PTable {
                 table.getTenantId(), table.getSchemaName(), table.getTableName(), table.getType(), table.getIndexState(), timeStamp,
                 sequenceNumber, table.getPKName(), table.getBucketNum(), columns, table.getParentSchemaName(), table.getParentTableName(),
                 table.getIndexes(), isImmutableRows, table.getPhysicalNames(), table.getDefaultFamilyName(), table.getViewStatement(),
-                isWalDisabled, isMultitenant, storeNulls, table.getViewType(), table.getViewIndexId(), table.getIndexType(), table.getTableStats(), table.getBaseColumnCount());
+                isWalDisabled, isMultitenant, storeNulls, table.getViewType(), table.getViewIndexId(), table.getIndexType(), table.getTableStats(),
+                table.getBaseColumnCount(), table.rowKeyOrderOptimizable());
     }
     
     public static PTableImpl makePTable(PTable table, PIndexState state) throws SQLException {
@@ -236,7 +242,18 @@ public class PTableImpl implements PTable {
                 table.getSequenceNumber(), table.getPKName(), table.getBucketNum(), getColumnsToClone(table),
                 table.getParentSchemaName(), table.getParentTableName(), table.getIndexes(),
                 table.isImmutableRows(), table.getPhysicalNames(), table.getDefaultFamilyName(), table.getViewStatement(),
-                table.isWALDisabled(), table.isMultiTenant(), table.getStoreNulls(), table.getViewType(), table.getViewIndexId(), table.getIndexType(), table.getTableStats(), table.getBaseColumnCount());
+                table.isWALDisabled(), table.isMultiTenant(), table.getStoreNulls(), table.getViewType(), table.getViewIndexId(), table.getIndexType(),
+                table.getTableStats(), table.getBaseColumnCount(), table.rowKeyOrderOptimizable());
+    }
+
+    public static PTableImpl makePTable(PTable table, boolean rowKeyOrderOptimizable) throws SQLException {
+        return new PTableImpl(
+                table.getTenantId(), table.getSchemaName(), table.getTableName(), table.getType(), table.getIndexState(), table.getTimeStamp(),
+                table.getSequenceNumber(), table.getPKName(), table.getBucketNum(), getColumnsToClone(table),
+                table.getParentSchemaName(), table.getParentTableName(), table.getIndexes(),
+                table.isImmutableRows(), table.getPhysicalNames(), table.getDefaultFamilyName(), table.getViewStatement(),
+                table.isWALDisabled(), table.isMultiTenant(), table.getStoreNulls(), table.getViewType(), table.getViewIndexId(), table.getIndexType(), table.getTableStats(),
+                table.getBaseColumnCount(), rowKeyOrderOptimizable);
     }
 
     public static PTableImpl makePTable(PTable table, PTableStats stats) throws SQLException {
@@ -245,28 +262,29 @@ public class PTableImpl implements PTable {
                 table.getSequenceNumber(), table.getPKName(), table.getBucketNum(), getColumnsToClone(table),
                 table.getParentSchemaName(), table.getParentTableName(), table.getIndexes(),
                 table.isImmutableRows(), table.getPhysicalNames(), table.getDefaultFamilyName(), table.getViewStatement(),
-                table.isWALDisabled(), table.isMultiTenant(), table.getStoreNulls(), table.getViewType(), table.getViewIndexId(), table.getIndexType(), stats, table.getBaseColumnCount());
+                table.isWALDisabled(), table.isMultiTenant(), table.getStoreNulls(), table.getViewType(), table.getViewIndexId(), table.getIndexType(), stats,
+                table.getBaseColumnCount(), table.rowKeyOrderOptimizable());
     }
 
     public static PTableImpl makePTable(PName tenantId, PName schemaName, PName tableName, PTableType type, PIndexState state, long timeStamp, long sequenceNumber,
             PName pkName, Integer bucketNum, List<PColumn> columns, PName dataSchemaName, PName dataTableName, List<PTable> indexes,
             boolean isImmutableRows, List<PName> physicalNames, PName defaultFamilyName, String viewExpression, boolean disableWAL, boolean multiTenant,
-            boolean storeNulls, ViewType viewType, Short viewIndexId, IndexType indexType) throws SQLException {
+            boolean storeNulls, ViewType viewType, Short viewIndexId, IndexType indexType, boolean rowKeyOrderOptimizable) throws SQLException {
         return new PTableImpl(tenantId, schemaName, tableName, type, state, timeStamp, sequenceNumber, pkName, bucketNum, columns, dataSchemaName,
                 dataTableName, indexes, isImmutableRows, physicalNames, defaultFamilyName,
                 viewExpression, disableWAL, multiTenant, storeNulls, viewType, viewIndexId,
-                indexType, PTableStats.EMPTY_STATS, QueryConstants.BASE_TABLE_BASE_COLUMN_COUNT);
+                indexType, PTableStats.EMPTY_STATS, QueryConstants.BASE_TABLE_BASE_COLUMN_COUNT, rowKeyOrderOptimizable);
     }
 
     public static PTableImpl makePTable(PName tenantId, PName schemaName, PName tableName, PTableType type,
             PIndexState state, long timeStamp, long sequenceNumber, PName pkName, Integer bucketNum,
             List<PColumn> columns, PName dataSchemaName, PName dataTableName, List<PTable> indexes,
             boolean isImmutableRows, List<PName> physicalNames, PName defaultFamilyName, String viewExpression,
-            boolean disableWAL, boolean multiTenant, boolean storeNulls, ViewType viewType, Short viewIndexId, IndexType indexType, @NotNull PTableStats stats, int baseColumnCount)
+            boolean disableWAL, boolean multiTenant, boolean storeNulls, ViewType viewType, Short viewIndexId, IndexType indexType, @NotNull PTableStats stats, int baseColumnCount, boolean rowKeyOrderOptimizable)
             throws SQLException {
         return new PTableImpl(tenantId, schemaName, tableName, type, state, timeStamp, sequenceNumber, pkName,
                 bucketNum, columns, dataSchemaName, dataTableName, indexes, isImmutableRows, physicalNames,
-                defaultFamilyName, viewExpression, disableWAL, multiTenant, storeNulls, viewType, viewIndexId, indexType, stats, baseColumnCount);
+                defaultFamilyName, viewExpression, disableWAL, multiTenant, storeNulls, viewType, viewIndexId, indexType, stats, baseColumnCount, rowKeyOrderOptimizable);
     }
 
     private PTableImpl(PName tenantId, PName schemaName, PName tableName, PTableType type, PIndexState state,
@@ -274,10 +292,10 @@ public class PTableImpl implements PTable {
             PName parentSchemaName, PName parentTableName, List<PTable> indexes, boolean isImmutableRows,
             List<PName> physicalNames, PName defaultFamilyName, String viewExpression, boolean disableWAL, boolean multiTenant,
             boolean storeNulls, ViewType viewType, Short viewIndexId, IndexType indexType,
-            PTableStats stats, int baseColumnCount) throws SQLException {
+            PTableStats stats, int baseColumnCount, boolean rowKeyOrderOptimizable) throws SQLException {
         init(tenantId, schemaName, tableName, type, state, timeStamp, sequenceNumber, pkName, bucketNum, columns,
                 stats, schemaName, parentTableName, indexes, isImmutableRows, physicalNames, defaultFamilyName,
-                viewExpression, disableWAL, multiTenant, storeNulls, viewType, viewIndexId, indexType, baseColumnCount);
+                viewExpression, disableWAL, multiTenant, storeNulls, viewType, viewIndexId, indexType, baseColumnCount, rowKeyOrderOptimizable);
     }
 
     @Override
@@ -305,7 +323,7 @@ public class PTableImpl implements PTable {
             PName pkName, Integer bucketNum, List<PColumn> columns, PTableStats stats, PName parentSchemaName, PName parentTableName,
             List<PTable> indexes, boolean isImmutableRows, List<PName> physicalNames, PName defaultFamilyName, String viewExpression, boolean disableWAL,
             boolean multiTenant, boolean storeNulls, ViewType viewType, Short viewIndexId,
-            IndexType indexType , int baseColumnCount) throws SQLException {
+            IndexType indexType , int baseColumnCount, boolean rowKeyOrderOptimizable) throws SQLException {
         Preconditions.checkNotNull(schemaName);
         Preconditions.checkArgument(tenantId==null || tenantId.getBytes().length > 0); // tenantId should be null or not empty
         int estimatedSize = SizedUtil.OBJECT_SIZE * 2 + 23 * SizedUtil.POINTER_SIZE + 4 * SizedUtil.INT_SIZE + 2 * SizedUtil.LONG_SIZE + 2 * SizedUtil.INT_OBJECT_SIZE +
@@ -335,6 +353,7 @@ public class PTableImpl implements PTable {
         this.viewIndexId = viewIndexId;
         this.indexType = indexType;
         this.tableStats = stats;
+        this.rowKeyOrderOptimizable = rowKeyOrderOptimizable;
         List<PColumn> pkColumns;
         PColumn[] allColumns;
         
@@ -386,7 +405,8 @@ public class PTableImpl implements PTable {
         for (PColumn column : allColumns) {
             PName familyName = column.getFamilyName();
             if (familyName == null) {
-            	 pkColumns.add(column);
+                hasDescVarLengthColumns |= (column.getSortOrder() == SortOrder.DESC && !column.getDataType().isFixedWidth());
+            	pkColumns.add(column);
             }
             if (familyName == null) {
                 estimatedSize += column.getEstimatedSize(); // PK columns
@@ -401,6 +421,7 @@ public class PTableImpl implements PTable {
             }
         }
         this.pkColumns = ImmutableList.copyOf(pkColumns);
+        builder.rowKeyOrderOptimizable(this.rowKeyOrderOptimizable()); // after hasDescVarLengthColumns is calculated
         this.rowKeySchema = builder.build();
         estimatedSize += rowKeySchema.getEstimatedSize();
         Iterator<Map.Entry<PName,List<PColumn>>> iterator = familyMap.entrySet().iterator();
@@ -500,18 +521,22 @@ public class PTableImpl implements PTable {
             List<PColumn> columns = getPKColumns();
             int nColumns = columns.size();
             PDataType type = null;
+            SortOrder sortOrder = null;
+            boolean wasNull = false;
             while (i < nValues && i < nColumns) {
                 // Separate variable length column values in key with zero byte
                 if (type != null && !type.isFixedWidth()) {
-                    os.write(SEPARATOR_BYTE);
+                    os.write(SchemaUtil.getSeparatorByte(rowKeyOrderOptimizable(), wasNull, sortOrder));
                 }
                 PColumn column = columns.get(i);
+                sortOrder = column.getSortOrder();
                 type = column.getDataType();
                 // This will throw if the value is null and the type doesn't allow null
                 byte[] byteValue = values[i++];
                 if (byteValue == null) {
                     byteValue = ByteUtil.EMPTY_BYTE_ARRAY;
                 }
+                wasNull = byteValue.length == 0;
                 // An empty byte array return value means null. Do this,
                 // since a type may have muliple representations of null.
                 // For example, VARCHAR treats both null and an empty string
@@ -529,11 +554,14 @@ public class PTableImpl implements PTable {
                 }
                 os.write(byteValue, 0, byteValue.length);
             }
+            // Need trailing byte for DESC columns
+            if (type != null && !type.isFixedWidth() && SchemaUtil.getSeparatorByte(rowKeyOrderOptimizable(), wasNull, sortOrder) == QueryConstants.DESC_SEPARATOR_BYTE) {
+                os.write(QueryConstants.DESC_SEPARATOR_BYTE);
+            }
             // If some non null pk values aren't set, then throw
             if (i < nColumns) {
                 PColumn column = columns.get(i);
-                type = column.getDataType();
-                if (type.isFixedWidth() || !column.isNullable()) {
+                if (column.getDataType().isFixedWidth() || !column.isNullable()) {
                     throw new ConstraintViolationException(name.getString() + "." + column.getName().getString() + " may not be null");
                 }
             }
@@ -977,12 +1005,16 @@ public class PTableImpl implements PTable {
           baseColumnCount = table.getBaseColumnCount();
       }
 
+      boolean rowKeyOrderOptimizable = false;
+      if (table.hasRowKeyOrderOptimizable()) {
+          rowKeyOrderOptimizable = table.getRowKeyOrderOptimizable();
+      }
       try {
         PTableImpl result = new PTableImpl();
         result.init(tenantId, schemaName, tableName, tableType, indexState, timeStamp, sequenceNumber, pkName,
           (bucketNum == NO_SALTING) ? null : bucketNum, columns, stats, schemaName,dataTableName, indexes,
               isImmutableRows, physicalNames, defaultFamilyName, viewStatement, disableWAL,
-                multiTenant, storeNulls, viewType, viewIndexId, indexType, baseColumnCount);
+                multiTenant, storeNulls, viewType, viewIndexId, indexType, baseColumnCount, rowKeyOrderOptimizable);
         return result;
       } catch (SQLException e) {
         throw new RuntimeException(e); // Impossible
@@ -1072,6 +1104,7 @@ public class PTableImpl implements PTable {
         }
       }
       builder.setBaseColumnCount(table.getBaseColumnCount());
+      builder.setRowKeyOrderOptimizable(table.rowKeyOrderOptimizable());
 
       return builder.build();
     }
@@ -1095,4 +1128,9 @@ public class PTableImpl implements PTable {
     public int getBaseColumnCount() {
         return baseColumnCount;
     }
+
+    @Override
+    public boolean rowKeyOrderOptimizable() {
+        return rowKeyOrderOptimizable || !hasDescVarLengthColumns;
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/RowKeySchema.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/RowKeySchema.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/RowKeySchema.java
index e5aa571..9d86dd6 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/RowKeySchema.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/RowKeySchema.java
@@ -17,13 +17,12 @@
  */
 package org.apache.phoenix.schema;
 
-import static org.apache.phoenix.query.QueryConstants.SEPARATOR_BYTE;
-
 import java.util.Collections;
 import java.util.List;
 
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.phoenix.query.QueryConstants;
+import org.apache.phoenix.util.SchemaUtil;
 
 
 /**
@@ -37,17 +36,19 @@ import org.apache.phoenix.query.QueryConstants;
  * @since 0.1
  */
 public class RowKeySchema extends ValueSchema {
-    public static final RowKeySchema EMPTY_SCHEMA = new RowKeySchema(0,Collections.<Field>emptyList())
+    public static final RowKeySchema EMPTY_SCHEMA = new RowKeySchema(0,Collections.<Field>emptyList(), true)
     ;
     
     public RowKeySchema() {
     }
     
-    protected RowKeySchema(int minNullable, List<Field> fields) {
-        super(minNullable, fields);
+    protected RowKeySchema(int minNullable, List<Field> fields, boolean rowKeyOrderOptimizable) {
+        super(minNullable, fields, rowKeyOrderOptimizable);
     }
 
     public static class RowKeySchemaBuilder extends ValueSchemaBuilder {
+        private boolean rowKeyOrderOptimizable = false;
+        
         public RowKeySchemaBuilder(int maxFields) {
             super(maxFields);
             setMaxFields(maxFields);
@@ -59,13 +60,22 @@ public class RowKeySchema extends ValueSchema {
             return this;
         }
 
+        public RowKeySchemaBuilder rowKeyOrderOptimizable(boolean rowKeyOrderOptimizable) {
+            this.rowKeyOrderOptimizable = rowKeyOrderOptimizable;
+            return this;
+        }
+
         @Override
         public RowKeySchema build() {
             List<Field> condensedFields = buildFields();
-            return new RowKeySchema(this.minNullable, condensedFields);
+            return new RowKeySchema(this.minNullable, condensedFields, rowKeyOrderOptimizable);
         }
     }
 
+    public boolean rowKeyOrderOptimizable() {
+        return rowKeyOrderOptimizable;
+    }
+
     public int getMaxFields() {
         return this.getMinNullable();
     }
@@ -148,13 +158,19 @@ public class RowKeySchema extends ValueSchema {
         if (field.getDataType().isFixedWidth()) {
             ptr.set(ptr.get(),ptr.getOffset(), field.getByteSize());
         } else {
-            if (position+1 == getFieldCount() ) { // Last field has no terminator
-                ptr.set(ptr.get(), ptr.getOffset(), maxOffset - ptr.getOffset());
+            if (position+1 == getFieldCount() ) {
+                // Last field has no terminator unless it's descending sort order
+                int len = maxOffset - ptr.getOffset();
+                ptr.set(ptr.get(), ptr.getOffset(), maxOffset - ptr.getOffset() - (SchemaUtil.getSeparatorByte(rowKeyOrderOptimizable, len == 0, field) == QueryConstants.DESC_SEPARATOR_BYTE ? 1 : 0));
             } else {
                 byte[] buf = ptr.get();
                 int offset = ptr.getOffset();
-                while (offset < maxOffset && buf[offset] != SEPARATOR_BYTE) {
-                    offset++;
+                // First byte 
+                if (offset < maxOffset && buf[offset] != QueryConstants.SEPARATOR_BYTE) {
+                    byte sepByte = SchemaUtil.getSeparatorByte(rowKeyOrderOptimizable, false, field);
+                    do {
+                        offset++;
+                    } while (offset < maxOffset && buf[offset] != sepByte);
                 }
                 ptr.set(buf, ptr.getOffset(), offset - ptr.getOffset());
             }
@@ -204,8 +220,12 @@ public class RowKeySchema extends ValueSchema {
         if (!field.getDataType().isFixedWidth()) {
             byte[] buf = ptr.get();
             int offset = ptr.getOffset()-1-offsetAdjustment;
-            while (offset > minOffset /* sanity check*/ && buf[offset] != QueryConstants.SEPARATOR_BYTE) {
-                offset--;
+            // Separator always zero byte if zero length
+            if (offset > minOffset && buf[offset] != QueryConstants.SEPARATOR_BYTE) {
+                byte sepByte = SchemaUtil.getSeparatorByte(rowKeyOrderOptimizable, false, field);
+                do {
+                    offset--;
+                } while (offset > minOffset && buf[offset] != sepByte);
             }
             if (offset == minOffset) { // shouldn't happen
                 ptr.set(buf, minOffset, ptr.getOffset()-minOffset-1);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/RowKeyValueAccessor.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/RowKeyValueAccessor.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/RowKeyValueAccessor.java
index 03b2e6a..2a59e01 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/RowKeyValueAccessor.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/RowKeyValueAccessor.java
@@ -17,8 +17,6 @@
  */
 package org.apache.phoenix.schema;
 
-import static org.apache.phoenix.query.QueryConstants.SEPARATOR_BYTE;
-
 import java.io.DataInput;
 import java.io.DataOutput;
 import java.io.IOException;
@@ -28,6 +26,7 @@ import java.util.List;
 
 import org.apache.hadoop.io.Writable;
 import org.apache.hadoop.io.WritableUtils;
+import org.apache.phoenix.query.QueryConstants;
 import org.apache.phoenix.util.ByteUtil;
 
 
@@ -150,6 +149,9 @@ public class RowKeyValueAccessor implements Writable   {
         ByteUtil.serializeVIntArray(output, offsets, length);
     }
     
+    private static boolean isSeparatorByte(byte b) {
+        return b == QueryConstants.SEPARATOR_BYTE || b == QueryConstants.DESC_SEPARATOR_BYTE;
+    }
     /**
      * Calculate the byte offset in the row key to the start of the PK column value
      * @param keyBuffer the byte array of the row key
@@ -164,7 +166,7 @@ public class RowKeyValueAccessor implements Writable   {
             } else { // Else, a negative offset is the number of variable length values to skip
                 while (offset++ < 0) {
                     // FIXME: keyOffset < keyBuffer.length required because HBase passes bogus keys to filter to position scan (HBASE-6562)
-                    while (keyOffset < keyBuffer.length && keyBuffer[keyOffset++] != SEPARATOR_BYTE) {
+                    while (keyOffset < keyBuffer.length && !isSeparatorByte(keyBuffer[keyOffset++])) {
                     }
                 }
             }
@@ -181,11 +183,11 @@ public class RowKeyValueAccessor implements Writable   {
      */
     public int getLength(byte[] keyBuffer, int keyOffset, int maxOffset) {
         if (!hasSeparator) {
-            return maxOffset - keyOffset;
+            return maxOffset - keyOffset - (keyBuffer[maxOffset-1] == QueryConstants.DESC_SEPARATOR_BYTE ? 1 : 0);
         }
         int offset = keyOffset;
         // FIXME: offset < maxOffset required because HBase passes bogus keys to filter to position scan (HBASE-6562)
-        while (offset < maxOffset && keyBuffer[offset] != SEPARATOR_BYTE) {
+        while (offset < maxOffset && !isSeparatorByte(keyBuffer[offset])) {
             offset++;
         }
         return offset - keyOffset;

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/ValueSchema.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/ValueSchema.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/ValueSchema.java
index 7660ffe..a4b40f3 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/ValueSchema.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/ValueSchema.java
@@ -49,12 +49,20 @@ public abstract class ValueSchema implements Writable {
     private boolean isFixedLength;
     private boolean isMaxLength;
     private int minNullable;
+    // Only applicable for RowKeySchema (and only due to PHOENIX-2067), but
+    // added here as this is where serialization is done (and we need to
+    // maintain the same serialization shape for b/w compat).
+    protected boolean rowKeyOrderOptimizable;
     
     public ValueSchema() {
     }
     
     protected ValueSchema(int minNullable, List<Field> fields) {
-        init(minNullable, fields);
+        this(minNullable, fields, true);
+    }
+    
+    protected ValueSchema(int minNullable, List<Field> fields, boolean rowKeyOrderOptimizable) {
+        init(minNullable, fields, rowKeyOrderOptimizable);
     }
     
     @Override
@@ -68,7 +76,8 @@ public abstract class ValueSchema implements Writable {
                 SizedUtil.ARRAY_SIZE + count * Field.ESTIMATED_SIZE + SizedUtil.sizeOfArrayList(count);
     }
 
-    private void init(int minNullable, List<Field> fields) {
+    private void init(int minNullable, List<Field> fields, boolean rowKeyOrderOptimizable) {
+        this.rowKeyOrderOptimizable = rowKeyOrderOptimizable;
         this.minNullable = minNullable;
         this.fields = ImmutableList.copyOf(fields);
         int estimatedLength = 0;
@@ -324,14 +333,6 @@ public abstract class ValueSchema implements Writable {
         return size;
     }
     
-    public void serialize(DataOutput output) throws IOException {
-        WritableUtils.writeVInt(output, minNullable);
-        WritableUtils.writeVInt(output, fields.size());
-        for (int i = 0; i < fields.size(); i++) {
-            fields.get(i).write(output);
-        }
-    }
-    
     public Field getField(int position) {
         return fields.get(fieldIndexByPosition[position]);
     }
@@ -366,19 +367,24 @@ public abstract class ValueSchema implements Writable {
     public void readFields(DataInput in) throws IOException {
         int minNullable = WritableUtils.readVInt(in);
         int nFields = WritableUtils.readVInt(in);
+        boolean rowKeyOrderOptimizable = false;
+        if (nFields < 0) {
+            rowKeyOrderOptimizable = true;
+            nFields *= -1;
+        }
         List<Field> fields = Lists.newArrayListWithExpectedSize(nFields);
         for (int i = 0; i < nFields; i++) {
             Field field = new Field();
             field.readFields(in);
             fields.add(field);
         }
-        init(minNullable, fields);
+        init(minNullable, fields, rowKeyOrderOptimizable);
     }
          
     @Override
     public void write(DataOutput out) throws IOException {
         WritableUtils.writeVInt(out, minNullable);
-        WritableUtils.writeVInt(out, fields.size());
+        WritableUtils.writeVInt(out, fields.size() * (rowKeyOrderOptimizable ? -1 : 1));
         for (int i = 0; i < fields.size(); i++) {
             fields.get(i).write(out);
         }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/stats/StatisticsUtil.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/stats/StatisticsUtil.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/stats/StatisticsUtil.java
index ebb7d1f..e692470 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/stats/StatisticsUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/stats/StatisticsUtil.java
@@ -60,10 +60,10 @@ public class StatisticsUtil {
         int offset = 0;
         System.arraycopy(table, 0, rowKey, offset, table.length);
         offset += table.length;
-        rowKey[offset++] = QueryConstants.SEPARATOR_BYTE;
+        rowKey[offset++] = QueryConstants.SEPARATOR_BYTE; // assumes stats table columns not DESC
         System.arraycopy(fam.get(), fam.getOffset(), rowKey, offset, fam.getLength());
         offset += fam.getLength();
-        rowKey[offset++] = QueryConstants.SEPARATOR_BYTE;
+        rowKey[offset++] = QueryConstants.SEPARATOR_BYTE; // assumes stats table columns not DESC
         System.arraycopy(region, 0, rowKey, offset, region.length);
         return rowKey;
     }


[29/50] [abbrv] phoenix git commit: PHOENIX-2067 Sort order incorrect for variable length DESC columns

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedTimestampArray.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedTimestampArray.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedTimestampArray.java
index 1159b5c..3407310 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedTimestampArray.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedTimestampArray.java
@@ -17,94 +17,80 @@
  */
 package org.apache.phoenix.schema.types;
 
-import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
-import org.apache.phoenix.schema.SortOrder;
+import java.sql.Timestamp;
 
-import java.sql.*;
+import org.apache.phoenix.schema.SortOrder;
 
 public class PUnsignedTimestampArray extends PArrayDataType<Timestamp[]> {
 
-  public static final PUnsignedTimestampArray INSTANCE = new PUnsignedTimestampArray();
-
-  private PUnsignedTimestampArray() {
-    super("UNSIGNED_TIMESTAMP ARRAY",
-        PDataType.ARRAY_TYPE_BASE + PUnsignedTimestamp.INSTANCE.getSqlType(), PhoenixArray.class,
-        null, 37);
-  }
-
-  @Override
-  public boolean isArrayType() {
-    return true;
-  }
-
-  @Override
-  public boolean isFixedWidth() {
-    return false;
-  }
+    public static final PUnsignedTimestampArray INSTANCE = new PUnsignedTimestampArray();
 
-  @Override
-  public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
-    return compareTo(lhs, rhs);
-  }
+    private PUnsignedTimestampArray() {
+        super("UNSIGNED_TIMESTAMP ARRAY",
+                PDataType.ARRAY_TYPE_BASE + PUnsignedTimestamp.INSTANCE.getSqlType(), PhoenixArray.class,
+                null, 37);
+    }
 
-  @Override
-  public Integer getByteSize() {
-    return null;
-  }
+    @Override
+    public boolean isArrayType() {
+        return true;
+    }
 
-  @Override
-  public byte[] toBytes(Object object) {
-    return toBytes(object, SortOrder.ASC);
-  }
+    @Override
+    public boolean isFixedWidth() {
+        return false;
+    }
 
-  @Override
-  public byte[] toBytes(Object object, SortOrder sortOrder) {
-    return toBytes(object, PUnsignedTimestamp.INSTANCE, sortOrder);
-  }
+    @Override
+    public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
+        return compareTo(lhs, rhs);
+    }
 
-  @Override
-  public Object toObject(byte[] bytes, int offset, int length,
-      PDataType actualType, SortOrder sortOrder, Integer maxLength,
-      Integer scale) {
-    return toObject(bytes, offset, length, PUnsignedTimestamp.INSTANCE, sortOrder,
-        maxLength, scale, PUnsignedTimestamp.INSTANCE);
-  }
+    @Override
+    public Integer getByteSize() {
+        return null;
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType) {
-    return isCoercibleTo(targetType, this);
-  }
+    @Override
+    public byte[] toBytes(Object object) {
+        return toBytes(object, SortOrder.ASC);
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType, Object value) {
-    if (value == null) {
-      return true;
+    @Override
+    public byte[] toBytes(Object object, SortOrder sortOrder) {
+        return toBytes(object, PUnsignedTimestamp.INSTANCE, sortOrder);
     }
-    PhoenixArray pArr = (PhoenixArray) value;
-    Object[] timeStampArr = (Object[]) pArr.array;
-    for (Object i : timeStampArr) {
-      if (!super.isCoercibleTo(PUnsignedTimestamp.INSTANCE, i)) {
-        return false;
-      }
+
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length,
+            PDataType actualType, SortOrder sortOrder, Integer maxLength,
+            Integer scale) {
+        return toObject(bytes, offset, length, PUnsignedTimestamp.INSTANCE, sortOrder,
+                maxLength, scale, PUnsignedTimestamp.INSTANCE);
     }
-    return true;
-  }
 
-  @Override
-  public void coerceBytes(ImmutableBytesWritable ptr, Object object, PDataType actualType,
-      Integer maxLength, Integer scale, SortOrder actualModifer, Integer desiredMaxLength,
-      Integer desiredScale, SortOrder desiredModifier) {
-    coerceBytes(ptr, object, actualType, maxLength, scale, desiredMaxLength, desiredScale,
-        this, actualModifer, desiredModifier);
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType) {
+        return isCoercibleTo(targetType, this);
+    }
 
-  @Override
-  public int getResultSetSqlType() {
-    return Types.ARRAY;
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType, Object value) {
+        if (value == null) {
+            return true;
+        }
+        PhoenixArray pArr = (PhoenixArray) value;
+        Object[] timeStampArr = (Object[]) pArr.array;
+        for (Object i : timeStampArr) {
+            if (!super.isCoercibleTo(PUnsignedTimestamp.INSTANCE, i)) {
+                return false;
+            }
+        }
+        return true;
+    }
 
-  @Override
-  public Object getSampleValue(Integer maxLength, Integer arrayLength) {
-    return getSampleValue(PUnsignedTimestamp.INSTANCE, arrayLength, maxLength);
-  }
+    @Override
+    public Object getSampleValue(Integer maxLength, Integer arrayLength) {
+        return getSampleValue(PUnsignedTimestamp.INSTANCE, arrayLength, maxLength);
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedTinyintArray.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedTinyintArray.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedTinyintArray.java
index a73f284..6843340 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedTinyintArray.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedTinyintArray.java
@@ -17,94 +17,78 @@
  */
 package org.apache.phoenix.schema.types;
 
-import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.phoenix.schema.SortOrder;
 
-import java.sql.Types;
-
 public class PUnsignedTinyintArray extends PArrayDataType<byte[]> {
 
-  public static final PUnsignedTinyintArray INSTANCE = new PUnsignedTinyintArray();
-
-  private PUnsignedTinyintArray() {
-    super("UNSIGNED_TINYINT ARRAY",
-        PDataType.ARRAY_TYPE_BASE + PUnsignedTinyint.INSTANCE.getSqlType(), PhoenixArray.class,
-        null, 45);
-  }
-
-  @Override
-  public boolean isArrayType() {
-    return true;
-  }
-
-  @Override
-  public boolean isFixedWidth() {
-    return false;
-  }
+    public static final PUnsignedTinyintArray INSTANCE = new PUnsignedTinyintArray();
 
-  @Override
-  public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
-    return compareTo(lhs, rhs);
-  }
+    private PUnsignedTinyintArray() {
+        super("UNSIGNED_TINYINT ARRAY",
+                PDataType.ARRAY_TYPE_BASE + PUnsignedTinyint.INSTANCE.getSqlType(), PhoenixArray.class,
+                null, 45);
+    }
 
-  @Override
-  public Integer getByteSize() {
-    return null;
-  }
+    @Override
+    public boolean isArrayType() {
+        return true;
+    }
 
-  @Override
-  public byte[] toBytes(Object object) {
-    return toBytes(object, SortOrder.ASC);
-  }
+    @Override
+    public boolean isFixedWidth() {
+        return false;
+    }
 
-  @Override
-  public byte[] toBytes(Object object, SortOrder sortOrder) {
-    return toBytes(object, PUnsignedTinyint.INSTANCE, sortOrder);
-  }
+    @Override
+    public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
+        return compareTo(lhs, rhs);
+    }
 
-  @Override
-  public Object toObject(byte[] bytes, int offset, int length,
-      PDataType actualType, SortOrder sortOrder, Integer maxLength,
-      Integer scale) {
-    return toObject(bytes, offset, length, PUnsignedTinyint.INSTANCE, sortOrder, maxLength,
-        scale, PUnsignedTinyint.INSTANCE);
-  }
+    @Override
+    public Integer getByteSize() {
+        return null;
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType) {
-    return isCoercibleTo(targetType, this);
-  }
+    @Override
+    public byte[] toBytes(Object object) {
+        return toBytes(object, SortOrder.ASC);
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType, Object value) {
-    if (value == null) {
-      return true;
+    @Override
+    public byte[] toBytes(Object object, SortOrder sortOrder) {
+        return toBytes(object, PUnsignedTinyint.INSTANCE, sortOrder);
     }
-    PhoenixArray pArr = (PhoenixArray) value;
-    Object[] byteArr = (Object[]) pArr.array;
-    for (Object i : byteArr) {
-      if (!super.isCoercibleTo(PUnsignedTinyint.INSTANCE, i)) {
-        return false;
-      }
+
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length,
+            PDataType actualType, SortOrder sortOrder, Integer maxLength,
+            Integer scale) {
+        return toObject(bytes, offset, length, PUnsignedTinyint.INSTANCE, sortOrder, maxLength,
+                scale, PUnsignedTinyint.INSTANCE);
     }
-    return true;
-  }
 
-  @Override
-  public int getResultSetSqlType() {
-    return Types.ARRAY;
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType) {
+        return isCoercibleTo(targetType, this);
+    }
 
-  @Override
-  public void coerceBytes(ImmutableBytesWritable ptr, Object object, PDataType actualType,
-      Integer maxLength, Integer scale, SortOrder actualModifer, Integer desiredMaxLength,
-      Integer desiredScale, SortOrder desiredModifier) {
-    coerceBytes(ptr, object, actualType, maxLength, scale, desiredMaxLength, desiredScale,
-        this, actualModifer, desiredModifier);
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType, Object value) {
+        if (value == null) {
+            return true;
+        }
+        PhoenixArray pArr = (PhoenixArray) value;
+        Object[] byteArr = (Object[]) pArr.array;
+        for (Object i : byteArr) {
+            if (!super.isCoercibleTo(PUnsignedTinyint.INSTANCE, i)) {
+                return false;
+            }
+        }
+        return true;
+    }
 
-  @Override
-  public Object getSampleValue(Integer maxLength, Integer arrayLength) {
-    return getSampleValue(PUnsignedTinyint.INSTANCE, arrayLength, maxLength);
-  }
+    @Override
+    public Object getSampleValue(Integer maxLength, Integer arrayLength) {
+        return getSampleValue(PUnsignedTinyint.INSTANCE, arrayLength, maxLength);
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PVarbinaryArray.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PVarbinaryArray.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PVarbinaryArray.java
index 10448ac..01b8667 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PVarbinaryArray.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PVarbinaryArray.java
@@ -17,93 +17,77 @@
  */
 package org.apache.phoenix.schema.types;
 
-import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.phoenix.schema.SortOrder;
 
-import java.sql.Types;
-
 public class PVarbinaryArray extends PArrayDataType<byte[][]> {
 
-  public static final PVarbinaryArray INSTANCE = new PVarbinaryArray();
-
-  private PVarbinaryArray() {
-    super("VARBINARY ARRAY", PDataType.ARRAY_TYPE_BASE + PVarbinary.INSTANCE.getSqlType(),
-        PhoenixArray.class, null, 27);
-  }
-
-  @Override
-  public boolean isArrayType() {
-    return true;
-  }
-
-  @Override
-  public boolean isFixedWidth() {
-    return false;
-  }
+    public static final PVarbinaryArray INSTANCE = new PVarbinaryArray();
 
-  @Override
-  public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
-    return compareTo(lhs, rhs);
-  }
+    private PVarbinaryArray() {
+        super("VARBINARY ARRAY", PDataType.ARRAY_TYPE_BASE + PVarbinary.INSTANCE.getSqlType(),
+                PhoenixArray.class, null, 27);
+    }
 
-  @Override
-  public Integer getByteSize() {
-    return null;
-  }
+    @Override
+    public boolean isArrayType() {
+        return true;
+    }
 
-  @Override
-  public byte[] toBytes(Object object) {
-    return toBytes(object, SortOrder.ASC);
-  }
+    @Override
+    public boolean isFixedWidth() {
+        return false;
+    }
 
-  @Override
-  public byte[] toBytes(Object object, SortOrder sortOrder) {
-    return toBytes(object, PVarbinary.INSTANCE, sortOrder);
-  }
+    @Override
+    public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
+        return compareTo(lhs, rhs);
+    }
 
-  @Override
-  public Object toObject(byte[] bytes, int offset, int length,
-      PDataType actualType, SortOrder sortOrder, Integer maxLength,
-      Integer scale) {
-    return toObject(bytes, offset, length, PVarbinary.INSTANCE, sortOrder, maxLength, scale,
-        PVarbinary.INSTANCE);
-  }
+    @Override
+    public Integer getByteSize() {
+        return null;
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType) {
-    return isCoercibleTo(targetType, this);
-  }
+    @Override
+    public byte[] toBytes(Object object) {
+        return toBytes(object, SortOrder.ASC);
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType, Object value) {
-    if (value == null) {
-      return true;
+    @Override
+    public byte[] toBytes(Object object, SortOrder sortOrder) {
+        return toBytes(object, PVarbinary.INSTANCE, sortOrder);
     }
-    PhoenixArray pArr = (PhoenixArray) value;
-    Object[] charArr = (Object[]) pArr.array;
-    for (Object i : charArr) {
-      if (!super.isCoercibleTo(PVarbinary.INSTANCE, i)) {
-        return false;
-      }
+
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length,
+            PDataType actualType, SortOrder sortOrder, Integer maxLength,
+            Integer scale) {
+        return toObject(bytes, offset, length, PVarbinary.INSTANCE, sortOrder, maxLength, scale,
+                PVarbinary.INSTANCE);
     }
-    return true;
-  }
 
-  @Override
-  public void coerceBytes(ImmutableBytesWritable ptr, Object object, PDataType actualType,
-      Integer maxLength, Integer scale, SortOrder actualModifer, Integer desiredMaxLength,
-      Integer desiredScale, SortOrder desiredModifier) {
-    coerceBytes(ptr, object, actualType, maxLength, scale, desiredMaxLength, desiredScale,
-        this, actualModifer, desiredModifier);
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType) {
+        return isCoercibleTo(targetType, this);
+    }
 
-  @Override
-  public int getResultSetSqlType() {
-    return Types.ARRAY;
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType, Object value) {
+        if (value == null) {
+            return true;
+        }
+        PhoenixArray pArr = (PhoenixArray) value;
+        Object[] charArr = (Object[]) pArr.array;
+        for (Object i : charArr) {
+            if (!super.isCoercibleTo(PVarbinary.INSTANCE, i)) {
+                return false;
+            }
+        }
+        return true;
+    }
 
-  @Override
-  public Object getSampleValue(Integer maxLength, Integer arrayLength) {
-    return getSampleValue(PVarbinary.INSTANCE, arrayLength, maxLength);
-  }
+    @Override
+    public Object getSampleValue(Integer maxLength, Integer arrayLength) {
+        return getSampleValue(PVarbinary.INSTANCE, arrayLength, maxLength);
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PVarcharArray.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PVarcharArray.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PVarcharArray.java
index 72561b3..6edaf80 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PVarcharArray.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PVarcharArray.java
@@ -17,93 +17,77 @@
  */
 package org.apache.phoenix.schema.types;
 
-import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.phoenix.schema.SortOrder;
 
-import java.sql.Types;
-
 public class PVarcharArray extends PArrayDataType<String> {
 
-  public static final PVarcharArray INSTANCE = new PVarcharArray();
-
-  private PVarcharArray() {
-    super("VARCHAR ARRAY", PDataType.ARRAY_TYPE_BASE + PVarchar.INSTANCE.getSqlType(),
-        PhoenixArray.class, null, 26);
-  }
-
-  @Override
-  public boolean isArrayType() {
-    return true;
-  }
-
-  @Override
-  public boolean isFixedWidth() {
-    return false;
-  }
+    public static final PVarcharArray INSTANCE = new PVarcharArray();
 
-  @Override
-  public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
-    return compareTo(lhs, rhs);
-  }
+    private PVarcharArray() {
+        super("VARCHAR ARRAY", PDataType.ARRAY_TYPE_BASE + PVarchar.INSTANCE.getSqlType(),
+                PhoenixArray.class, null, 26);
+    }
 
-  @Override
-  public Integer getByteSize() {
-    return null;
-  }
+    @Override
+    public boolean isArrayType() {
+        return true;
+    }
 
-  @Override
-  public byte[] toBytes(Object object) {
-    return toBytes(object, SortOrder.ASC);
-  }
+    @Override
+    public boolean isFixedWidth() {
+        return false;
+    }
 
-  @Override
-  public byte[] toBytes(Object object, SortOrder sortOrder) {
-    return toBytes(object, PVarchar.INSTANCE, sortOrder);
-  }
+    @Override
+    public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
+        return compareTo(lhs, rhs);
+    }
 
-  @Override
-  public Object toObject(byte[] bytes, int offset, int length,
-      PDataType actualType, SortOrder sortOrder, Integer maxLength,
-      Integer scale) {
-    return toObject(bytes, offset, length, PVarchar.INSTANCE, sortOrder, maxLength, scale,
-        PVarchar.INSTANCE);
-  }
+    @Override
+    public Integer getByteSize() {
+        return null;
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType) {
-    return isCoercibleTo(targetType, this);
-  }
+    @Override
+    public byte[] toBytes(Object object) {
+        return toBytes(object, SortOrder.ASC);
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType, Object value) {
-    if (value == null) {
-      return true;
+    @Override
+    public byte[] toBytes(Object object, SortOrder sortOrder) {
+        return toBytes(object, PVarchar.INSTANCE, sortOrder);
     }
-    PhoenixArray pArr = (PhoenixArray) value;
-    Object[] charArr = (Object[]) pArr.array;
-    for (Object i : charArr) {
-      if (!super.isCoercibleTo(PVarchar.INSTANCE, i)) {
-        return false;
-      }
+
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length,
+            PDataType actualType, SortOrder sortOrder, Integer maxLength,
+            Integer scale) {
+        return toObject(bytes, offset, length, PVarchar.INSTANCE, sortOrder, maxLength, scale,
+                PVarchar.INSTANCE);
     }
-    return true;
-  }
 
-  @Override
-  public void coerceBytes(ImmutableBytesWritable ptr, Object object, PDataType actualType,
-      Integer maxLength, Integer scale, SortOrder actualModifer, Integer desiredMaxLength,
-      Integer desiredScale, SortOrder desiredModifier) {
-    coerceBytes(ptr, object, actualType, maxLength, scale, desiredMaxLength, desiredScale,
-        this, actualModifer, desiredModifier);
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType) {
+        return isCoercibleTo(targetType, this);
+    }
 
-  @Override
-  public int getResultSetSqlType() {
-    return Types.ARRAY;
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType, Object value) {
+        if (value == null) {
+            return true;
+        }
+        PhoenixArray pArr = (PhoenixArray) value;
+        Object[] charArr = (Object[]) pArr.array;
+        for (Object i : charArr) {
+            if (!super.isCoercibleTo(PVarchar.INSTANCE, i)) {
+                return false;
+            }
+        }
+        return true;
+    }
 
-  @Override
-  public Object getSampleValue(Integer maxLength, Integer arrayLength) {
-    return getSampleValue(PVarchar.INSTANCE, arrayLength, maxLength);
-  }
+    @Override
+    public Object getSampleValue(Integer maxLength, Integer arrayLength) {
+        return getSampleValue(PVarchar.INSTANCE, arrayLength, maxLength);
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/util/ByteUtil.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/ByteUtil.java b/phoenix-core/src/main/java/org/apache/phoenix/util/ByteUtil.java
index 1e3516d..64d064a 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/util/ByteUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/util/ByteUtil.java
@@ -32,8 +32,8 @@ import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.hadoop.io.WritableUtils;
 import org.apache.phoenix.hbase.index.util.ImmutableBytesPtr;
 import org.apache.phoenix.query.KeyRange;
-import org.apache.phoenix.schema.types.PDataType;
 import org.apache.phoenix.schema.SortOrder;
+import org.apache.phoenix.schema.types.PDataType;
 
 import com.google.common.base.Preconditions;
 
@@ -285,14 +285,16 @@ public class ByteUtil {
             totalLength += writable.getLength();
         }
         byte[] result = new byte[totalLength];
-        int offset = 0;
+        int totalOffset = 0;
         for (ImmutableBytesWritable array : writables) {
             byte[] bytes = array.get();
+            int offset = array.getOffset();
             if (sortOrder == SortOrder.DESC) {
-                bytes = SortOrder.invert(bytes, array.getOffset(), new byte[array.getLength()], 0, array.getLength());
+                bytes = SortOrder.invert(bytes, offset, new byte[array.getLength()], 0, array.getLength());
+                offset = 0;
             }
-            System.arraycopy(bytes, array.getOffset(), result, offset, array.getLength());
-            offset += array.getLength();
+            System.arraycopy(bytes, offset, result, totalOffset, array.getLength());
+            totalOffset += array.getLength();
         }
         return result;
     }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/util/MetaDataUtil.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/MetaDataUtil.java b/phoenix-core/src/main/java/org/apache/phoenix/util/MetaDataUtil.java
index 91fd2cb..fbc15be 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/util/MetaDataUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/util/MetaDataUtil.java
@@ -185,7 +185,7 @@ public class MetaDataUtil {
     }
     
     public static PTableType getTableType(List<Mutation> tableMetaData, KeyValueBuilder builder,
-      ImmutableBytesPtr value) {
+      ImmutableBytesWritable value) {
         if (getMutationValue(getPutOnlyTableHeaderRow(tableMetaData),
             PhoenixDatabaseMetaData.TABLE_TYPE_BYTES, builder, value)) {
             return PTableType.fromSerializedValue(value.get()[value.getOffset()]);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/util/PhoenixRuntime.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/PhoenixRuntime.java b/phoenix-core/src/main/java/org/apache/phoenix/util/PhoenixRuntime.java
index 92bb1d8..61642bc 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/util/PhoenixRuntime.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/util/PhoenixRuntime.java
@@ -67,6 +67,7 @@ import org.apache.phoenix.jdbc.PhoenixResultSet;
 import org.apache.phoenix.monitoring.GlobalClientMetrics;
 import org.apache.phoenix.monitoring.GlobalMetric;
 import org.apache.phoenix.query.QueryConstants;
+import org.apache.phoenix.query.QueryServices;
 import org.apache.phoenix.schema.AmbiguousColumnException;
 import org.apache.phoenix.schema.ColumnNotFoundException;
 import org.apache.phoenix.schema.KeyValueSchema;
@@ -78,12 +79,14 @@ import org.apache.phoenix.schema.PTable;
 import org.apache.phoenix.schema.PTable.IndexType;
 import org.apache.phoenix.schema.PTableKey;
 import org.apache.phoenix.schema.PTableType;
-import org.apache.phoenix.schema.RowKeySchema;
 import org.apache.phoenix.schema.RowKeyValueAccessor;
 import org.apache.phoenix.schema.TableNotFoundException;
 import org.apache.phoenix.schema.ValueBitSet;
 import org.apache.phoenix.schema.types.PDataType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Joiner;
 import com.google.common.base.Splitter;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
@@ -96,6 +99,8 @@ import com.google.common.collect.Lists;
  * @since 0.1
  */
 public class PhoenixRuntime {
+    private static final Logger logger = LoggerFactory.getLogger(PhoenixRuntime.class);
+
     /**
      * Use this connection property to control HBase timestamps
      * by specifying your own long timestamp value at connection time. All
@@ -191,24 +196,39 @@ public class PhoenixRuntime {
             conn = DriverManager.getConnection(jdbcUrl, props)
                     .unwrap(PhoenixConnection.class);
 
-            for (String inputFile : execCmd.getInputFiles()) {
-                if (inputFile.endsWith(SQL_FILE_EXT)) {
-                    PhoenixRuntime.executeStatements(conn,
-                            new FileReader(inputFile), Collections.emptyList());
-                } else if (inputFile.endsWith(CSV_FILE_EXT)) {
-
-                    String tableName = execCmd.getTableName();
-                    if (tableName == null) {
-                        tableName = SchemaUtil.normalizeIdentifier(
-                                inputFile.substring(inputFile.lastIndexOf(File.separatorChar) + 1,
-                                        inputFile.length() - CSV_FILE_EXT.length()));
+            if (execCmd.isUpgrade()) {
+                if (execCmd.getInputFiles().isEmpty()) {
+                    Set<String> tablesNeedingUpgrade = UpgradeUtil.getPhysicalTablesWithDescVarLengthRowKey(conn);
+                    if (tablesNeedingUpgrade.isEmpty()) {
+                        String msg = "No tables are required to be upgraded due to incorrect row key order for descending, variable length columsn (PHOENIX-2067)";
+                        System.out.println(msg);
+                    } else {
+                        String msg = "The following tables require upgrade due to a bug causing the row key to be incorrect for descending columns (PHOENIX-2067):\n" + Joiner.on(' ').join(tablesNeedingUpgrade);
+                        System.out.println("WARNING: " + msg);
+                    }
+                } else {
+                    UpgradeUtil.upgradeDescVarLengthRowKeys(conn, execCmd.getInputFiles());
+                }
+            } else {
+                for (String inputFile : execCmd.getInputFiles()) {
+                    if (inputFile.endsWith(SQL_FILE_EXT)) {
+                        PhoenixRuntime.executeStatements(conn,
+                                new FileReader(inputFile), Collections.emptyList());
+                    } else if (inputFile.endsWith(CSV_FILE_EXT)) {
+    
+                        String tableName = execCmd.getTableName();
+                        if (tableName == null) {
+                            tableName = SchemaUtil.normalizeIdentifier(
+                                    inputFile.substring(inputFile.lastIndexOf(File.separatorChar) + 1,
+                                            inputFile.length() - CSV_FILE_EXT.length()));
+                        }
+                        CSVCommonsLoader csvLoader =
+                                new CSVCommonsLoader(conn, tableName, execCmd.getColumns(),
+                                        execCmd.isStrict(), execCmd.getFieldDelimiter(),
+                                        execCmd.getQuoteCharacter(), execCmd.getEscapeCharacter(),
+                                        execCmd.getArrayElementSeparator());
+                        csvLoader.upsert(inputFile);
                     }
-                    CSVCommonsLoader csvLoader =
-                            new CSVCommonsLoader(conn, tableName, execCmd.getColumns(),
-                                    execCmd.isStrict(), execCmd.getFieldDelimiter(),
-                                    execCmd.getQuoteCharacter(), execCmd.getEscapeCharacter(),
-                                    execCmd.getArrayElementSeparator());
-                    csvLoader.upsert(inputFile);
                 }
             }
         } catch (Throwable t) {
@@ -459,6 +479,7 @@ public class PhoenixRuntime {
         private String arrayElementSeparator;
         private boolean strict;
         private List<String> inputFiles;
+        private boolean isUpgrade;
 
         /**
          * Factory method to build up an {@code ExecutionCommand} based on supplied parameters.
@@ -483,6 +504,11 @@ public class PhoenixRuntime {
                             "character");
             Option arrayValueSeparatorOption = new Option("a", "array-separator", true,
                     "Define the array element separator, defaults to ':'");
+            Option upgradeOption = new Option("u", "upgrade", false, "Upgrades tables specified as arguments " +
+                    "by rewriting them with the correct row key for descending columns. If no arguments are " +
+                    "specified, then tables that need to be upgraded will be displayed. " +
+                    "Note that " + QueryServices.THREAD_TIMEOUT_MS_ATTRIB + " and hbase.regionserver.lease.period " +
+                    "parameters must be set very high to prevent timeouts when upgrading.");
             Options options = new Options();
             options.addOption(tableOption);
             options.addOption(headerOption);
@@ -491,6 +517,7 @@ public class PhoenixRuntime {
             options.addOption(quoteCharacterOption);
             options.addOption(escapeCharacterOption);
             options.addOption(arrayValueSeparatorOption);
+            options.addOption(upgradeOption);
 
             CommandLineParser parser = new PosixParser();
             CommandLine cmdLine = null;
@@ -530,6 +557,10 @@ public class PhoenixRuntime {
             execCmd.arrayElementSeparator = cmdLine.getOptionValue(
                     arrayValueSeparatorOption.getOpt(),
                     CSVCommonsLoader.DEFAULT_ARRAY_ELEMENT_SEPARATOR);
+            
+            if (cmdLine.hasOption(upgradeOption.getOpt())) {
+                execCmd.isUpgrade = true;
+            }
 
 
             List<String> argList = Lists.newArrayList(cmdLine.getArgList());
@@ -539,21 +570,19 @@ public class PhoenixRuntime {
             execCmd.connectionString = argList.remove(0);
             List<String> inputFiles = Lists.newArrayList();
             for (String arg : argList) {
-                if (arg.endsWith(CSV_FILE_EXT) || arg.endsWith(SQL_FILE_EXT)) {
+                if (execCmd.isUpgrade || arg.endsWith(CSV_FILE_EXT) || arg.endsWith(SQL_FILE_EXT)) {
                     inputFiles.add(arg);
                 } else {
                     usageError("Don't know how to interpret argument '" + arg + "'", options);
                 }
             }
 
-            if (inputFiles.isEmpty()) {
+            if (inputFiles.isEmpty() && !execCmd.isUpgrade) {
                 usageError("At least one input file must be supplied", options);
             }
 
             execCmd.inputFiles = inputFiles;
 
-
-
             return execCmd;
         }
 
@@ -620,87 +649,12 @@ public class PhoenixRuntime {
         public boolean isStrict() {
             return strict;
         }
-    }
-    
-    /**
-     * Encode the primary key values from the table as a byte array. The values must
-     * be in the same order as the primary key constraint. If the connection and
-     * table are both tenant-specific, the tenant ID column must not be present in
-     * the values.
-     * @param conn an open connection
-     * @param fullTableName the full table name
-     * @param values the values of the primary key columns ordered in the same order
-     *  as the primary key constraint
-     * @return the encoded byte array
-     * @throws SQLException if the table cannot be found or the incorrect number of
-     *  of values are provided
-     * @see #decodePK(Connection, String, byte[]) to decode the byte[] back to the
-     *  values
-     */
-    @Deprecated
-    public static byte[] encodePK(Connection conn, String fullTableName, Object[] values) throws SQLException {
-        PTable table = getTable(conn, fullTableName);
-        PhoenixConnection pconn = conn.unwrap(PhoenixConnection.class);
-        int offset = (table.getBucketNum() == null ? 0 : 1) + (table.isMultiTenant() && pconn.getTenantId() != null ? 1 : 0);
-        List<PColumn> pkColumns = table.getPKColumns();
-        if (pkColumns.size() - offset != values.length) {
-            throw new SQLException("Expected " + (pkColumns.size() - offset) + " but got " + values.length);
-        }
-        PDataType type = null;
-        TrustedByteArrayOutputStream output = new TrustedByteArrayOutputStream(table.getRowKeySchema().getEstimatedValueLength());
-        try {
-            for (int i = offset; i < pkColumns.size(); i++) {
-                if (type != null && !type.isFixedWidth()) {
-                    output.write(QueryConstants.SEPARATOR_BYTE);
-                }
-                type = pkColumns.get(i).getDataType();
 
-                //for fixed width data types like CHAR and BINARY, we need to pad values to be of max length.
-                Object paddedObj = type.pad(values[i - offset], pkColumns.get(i).getMaxLength());
-                byte[] value = type.toBytes(paddedObj);
-                output.write(value);
-            }
-            return output.toByteArray();
-        } finally {
-            try {
-                output.close();
-            } catch (IOException e) {
-                throw new RuntimeException(e); // Impossible
-            }
+        public boolean isUpgrade() {
+            return isUpgrade;
         }
     }
-
-    /**
-     * Decode a byte array value back into the Object values of the
-     * primary key constraint. If the connection and table are both
-     * tenant-specific, the tenant ID column is not expected to have
-     * been encoded and will not appear in the returned values.
-     * @param conn an open connection
-     * @param name the full table name
-     * @param encodedValue the value that was encoded with {@link #encodePK(Connection, String, Object[])}
-     * @return the Object values encoded in the byte array value
-     * @throws SQLException
-     */
-    @Deprecated
-    public static Object[] decodePK(Connection conn, String name, byte[] value) throws SQLException {
-        PTable table = getTable(conn, name);
-        PhoenixConnection pconn = conn.unwrap(PhoenixConnection.class);
-        int offset = (table.getBucketNum() == null ? 0 : 1) + (table.isMultiTenant() && pconn.getTenantId() != null ? 1 : 0);
-        int nValues = table.getPKColumns().size() - offset;
-        RowKeySchema schema = table.getRowKeySchema();
-        Object[] values = new Object[nValues];
-        ImmutableBytesWritable ptr = new ImmutableBytesWritable();
-        schema.iterator(value, ptr);
-        int i = 0;
-        int fieldIdx = offset;
-        while (i < nValues && schema.next(ptr, fieldIdx, value.length) != null) {
-            values[i] = schema.getField(fieldIdx).getDataType().toObject(ptr);
-            i++;
-            fieldIdx++;
-        }
-        return values;
-    }
-
+    
     /**
      * Returns the opitmized query plan used by phoenix for executing the sql.
      * @param stmt to return the plan for

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/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 3dff972..d63edbb 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
@@ -326,13 +326,15 @@ public class ScanUtil {
         // but the index for the field it represents in the schema
         // should be incremented by 1 + value in the current slotSpan index
         // slotSpan stores the number of columns beyond one that the range spans
+        Field field = null;
         int i = slotStartIndex, fieldIndex = ScanUtil.getRowKeyPosition(slotSpan, slotStartIndex);
         for (i = slotStartIndex; i < slotEndIndex; i++) {
             // Build up the key by appending the bound of each key range
             // from the current position of each slot. 
             KeyRange range = slots.get(i).get(position[i]);
             // Use last slot in a multi-span column to determine if fixed width
-            boolean isFixedWidth = schema.getField(fieldIndex + slotSpan[i]).getDataType().isFixedWidth();
+            field = schema.getField(fieldIndex + slotSpan[i]);
+            boolean isFixedWidth = field.getDataType().isFixedWidth();
             fieldIndex += slotSpan[i] + 1;
             /*
              * If the current slot is unbound then stop if:
@@ -369,9 +371,11 @@ public class ScanUtil {
             // key slots would cause the flag to become true.
             lastInclusiveUpperSingleKey = range.isSingleKey() && inclusiveUpper;
             anyInclusiveUpperRangeKey |= !range.isSingleKey() && inclusiveUpper;
+            // A match for IS NULL or IS NOT NULL should not have a DESC_SEPARATOR_BYTE as nulls sort first
+            byte sepByte = SchemaUtil.getSeparatorByte(schema.rowKeyOrderOptimizable(), bytes.length == 0 || range == KeyRange.IS_NULL_RANGE || range == KeyRange.IS_NOT_NULL_RANGE, field);
             
-            if (!isFixedWidth && ( fieldIndex < schema.getMaxFields() || inclusiveUpper || exclusiveLower)) {
-                key[offset++] = QueryConstants.SEPARATOR_BYTE;
+            if (!isFixedWidth && ( fieldIndex < schema.getMaxFields() || inclusiveUpper || exclusiveLower || sepByte == QueryConstants.DESC_SEPARATOR_BYTE)) {
+                key[offset++] = sepByte;
                 // 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;
@@ -484,10 +488,11 @@ public class ScanUtil {
         while (schema.next(ptr, pos, maxOffset) != null) {
             pos++;
         }
-        if (!schema.getField(pos-1).getDataType().isFixedWidth()) {
+        Field field = schema.getField(pos-1);
+        if (!field.getDataType().isFixedWidth()) {
             byte[] newLowerRange = new byte[key.length + 1];
             System.arraycopy(key, 0, newLowerRange, 0, key.length);
-            newLowerRange[key.length] = QueryConstants.SEPARATOR_BYTE;
+            newLowerRange[key.length] = SchemaUtil.getSeparatorByte(schema.rowKeyOrderOptimizable(), key.length==0, field);
             key = newLowerRange;
         } else {
             key = Arrays.copyOf(key, key.length);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/util/SchemaUtil.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/SchemaUtil.java b/phoenix-core/src/main/java/org/apache/phoenix/util/SchemaUtil.java
index c674140..d01bf39 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/util/SchemaUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/util/SchemaUtil.java
@@ -39,6 +39,7 @@ import org.apache.hadoop.hbase.io.encoding.DataBlockEncoding;
 import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.phoenix.exception.SQLExceptionCode;
 import org.apache.phoenix.exception.SQLExceptionInfo;
+import org.apache.phoenix.expression.Expression;
 import org.apache.phoenix.hbase.index.util.ImmutableBytesPtr;
 import org.apache.phoenix.jdbc.PhoenixConnection;
 import org.apache.phoenix.jdbc.PhoenixDatabaseMetaData;
@@ -721,4 +722,26 @@ public class SchemaUtil {
         fullColumnName = fullColumnName.replaceAll(ESCAPE_CHARACTER, "");
        	return fullColumnName.trim();
     }
+    
+    /**
+     * Return the separator byte to use based on:
+     * @param rowKeyOrderOptimizable whether or not the table may optimize descending row keys. If the
+     *  table has no descending row keys, this will be true. Also, if the table has been upgraded (using
+     *  a new -u option for psql.py), then it'll be true
+     * @param isNullValue whether or not the value is null. We use a null byte still if the value is null
+     * regardless of sort order since nulls will always sort first this way.
+     * @param sortOrder whether the value sorts ascending or descending.
+     * @return the byte to use as the separator
+     */
+    public static byte getSeparatorByte(boolean rowKeyOrderOptimizable, boolean isNullValue, SortOrder sortOrder) {
+        return !rowKeyOrderOptimizable || isNullValue || sortOrder == SortOrder.ASC ? QueryConstants.SEPARATOR_BYTE : QueryConstants.DESC_SEPARATOR_BYTE;
+    }
+    
+    public static byte getSeparatorByte(boolean rowKeyOrderOptimizable, boolean isNullValue, Field f) {
+        return getSeparatorByte(rowKeyOrderOptimizable, isNullValue, f.getSortOrder());
+    }
+    
+    public static byte getSeparatorByte(boolean rowKeyOrderOptimizable, boolean isNullValue, Expression e) {
+        return getSeparatorByte(rowKeyOrderOptimizable, isNullValue, e.getSortOrder());
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/util/TupleUtil.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/TupleUtil.java b/phoenix-core/src/main/java/org/apache/phoenix/util/TupleUtil.java
index 1a1b2c9..f495a7e 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/util/TupleUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/util/TupleUtil.java
@@ -113,10 +113,9 @@ public class TupleUtil {
                 }
                 for (int i = 1; i < expressions.size(); i++) {
                     if (!expression.getDataType().isFixedWidth()) {
-                        output.write(QueryConstants.SEPARATOR_BYTE);
+                        output.write(SchemaUtil.getSeparatorByte(true, value.getLength()==0, expression));
                     }
                     expression = expressions.get(i);
-                    // TODO: should we track trailing null values and omit the separator bytes?
                     if (expression.evaluate(result, value)) {
                         output.write(value.get(), value.getOffset(), value.getLength());
                     } else if (i < expressions.size()-1 && expression.getDataType().isFixedWidth()) {
@@ -125,6 +124,10 @@ public class TupleUtil {
                         throw new DoNotRetryIOException("Non terminating null value found for fixed width expression (" + expression + ") in row: " + result);
                     }
                 }
+                // Write trailing separator if last expression was variable length and descending
+                if (!expression.getDataType().isFixedWidth() && SchemaUtil.getSeparatorByte(true, value.getLength()==0, expression) == QueryConstants.DESC_SEPARATOR_BYTE) {
+                    output.write(QueryConstants.DESC_SEPARATOR_BYTE);
+                }
                 byte[] outputBytes = output.getBuffer();
                 value.set(outputBytes, 0, output.size());
                 return value;

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/util/UpgradeUtil.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/UpgradeUtil.java b/phoenix-core/src/main/java/org/apache/phoenix/util/UpgradeUtil.java
index c829799..e59ea98 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/util/UpgradeUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/util/UpgradeUtil.java
@@ -35,19 +35,25 @@ import static org.apache.phoenix.query.QueryConstants.BASE_TABLE_BASE_COLUMN_COU
 import static org.apache.phoenix.query.QueryConstants.DIVERGED_VIEW_BASE_COLUMN_COUNT;
 
 import java.io.IOException;
+import java.sql.Connection;
+import java.sql.DriverManager;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Properties;
+import java.util.Set;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
 import org.apache.hadoop.hbase.HColumnDescriptor;
+import org.apache.hadoop.hbase.HConstants;
 import org.apache.hadoop.hbase.HTableDescriptor;
 import org.apache.hadoop.hbase.KeyValue;
 import org.apache.hadoop.hbase.TableName;
@@ -60,6 +66,7 @@ import org.apache.hadoop.hbase.client.Result;
 import org.apache.hadoop.hbase.client.ResultScanner;
 import org.apache.hadoop.hbase.client.Scan;
 import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.phoenix.coprocessor.MetaDataEndpointImpl;
 import org.apache.phoenix.coprocessor.MetaDataProtocol;
 import org.apache.phoenix.jdbc.PhoenixConnection;
 import org.apache.phoenix.jdbc.PhoenixDatabaseMetaData;
@@ -67,15 +74,24 @@ import org.apache.phoenix.query.QueryConstants;
 import org.apache.phoenix.schema.PName;
 import org.apache.phoenix.schema.PNameFactory;
 import org.apache.phoenix.schema.PTable;
+import org.apache.phoenix.schema.PTable.IndexType;
 import org.apache.phoenix.schema.PTable.LinkType;
+import org.apache.phoenix.schema.PTableType;
 import org.apache.phoenix.schema.SaltingUtil;
+import org.apache.phoenix.schema.SortOrder;
+import org.apache.phoenix.schema.types.PBoolean;
+import org.apache.phoenix.schema.types.PDecimal;
+import org.apache.phoenix.schema.types.PDecimalArray;
 import org.apache.phoenix.schema.types.PInteger;
 import org.apache.phoenix.schema.types.PLong;
+import org.apache.phoenix.schema.types.PVarchar;
+import org.apache.phoenix.schema.types.PVarcharArray;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Objects;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
 
 public class UpgradeUtil {
     private static final Logger logger = LoggerFactory.getLogger(UpgradeUtil.class);
@@ -808,4 +824,289 @@ public class UpgradeUtil {
         }
     }
 
+    private static String getTableRVC(List<String> tableNames) {
+        StringBuilder query = new StringBuilder("(");
+        for (int i = 0; i < tableNames.size(); i+=3) {
+            String tenantId = tableNames.get(i);
+            String schemaName = tableNames.get(i+1);
+            String tableName = tableNames.get(i+2);
+            query.append('(');
+            query.append(tenantId == null ? "null" : ("'" + tenantId + "'"));
+            query.append(',');
+            query.append(schemaName == null ? "null" : ("'" + schemaName + "'"));
+            query.append(',');
+            query.append("'" + tableName + "'");
+            query.append("),");
+        }
+        // Replace trailing , with ) to end IN expression
+        query.setCharAt(query.length()-1, ')');
+        return query.toString();
+    }
+    
+    private static List<String> addPhysicalTables(PhoenixConnection conn, ResultSet rs, PTableType otherType, Set<String> physicalTables) throws SQLException {
+        List<String> tableNames = Lists.newArrayListWithExpectedSize(1024);
+        while (rs.next()) {
+            tableNames.add(rs.getString(1));
+            tableNames.add(rs.getString(2));
+            tableNames.add(rs.getString(3));
+        }
+        if (tableNames.isEmpty()) {
+            return Collections.emptyList();
+        }
+        
+        List<String> otherTables = Lists.newArrayListWithExpectedSize(tableNames.size());
+        // Find the header rows for tables that have not been upgraded already.
+        // We don't care about views, as the row key cannot be different than the table.
+        // We need this query to find physical tables which won't have a link row.
+        String query = "SELECT TENANT_ID,TABLE_SCHEM,TABLE_NAME,TABLE_TYPE\n" + 
+                "FROM SYSTEM.CATALOG (ROW_KEY_ORDER_OPTIMIZABLE BOOLEAN)\n" + 
+                "WHERE COLUMN_NAME IS NULL\n" + 
+                "AND COLUMN_FAMILY IS NULL\n" + 
+                "AND ROW_KEY_ORDER_OPTIMIZABLE IS NULL\n" +
+                "AND TABLE_TYPE IN ('" + PTableType.TABLE.getSerializedValue() + "','" + otherType.getSerializedValue() + "')\n" +
+                "AND (TENANT_ID, TABLE_SCHEM, TABLE_NAME) IN " + getTableRVC(tableNames);
+        rs = conn.createStatement().executeQuery(query);
+        
+        while (rs.next()) {
+            if (PTableType.TABLE.getSerializedValue().equals(rs.getString(4))) {
+                physicalTables.add(SchemaUtil.getTableName(rs.getString(2), rs.getString(3)));
+            } else {
+                otherTables.add(rs.getString(1));
+                otherTables.add(rs.getString(2));
+                otherTables.add(rs.getString(3));
+            }
+        }
+        return otherTables;
+    }
+    /**
+     * Identify the tables that need to be upgraded due to PHOENIX-2067
+     */
+    public static Set<String> getPhysicalTablesWithDescVarLengthRowKey(PhoenixConnection conn) throws SQLException {
+        // First query finds column rows of tables that need to be upgraded.
+        // We cannot tell if the column is from a table, view, or index however.
+        ResultSet rs = conn.createStatement().executeQuery(
+                "SELECT TENANT_ID,TABLE_SCHEM,TABLE_NAME\n" + 
+                "FROM SYSTEM.CATALOG cat1\n" + 
+                "WHERE COLUMN_NAME IS NOT NULL\n" + 
+                "AND COLUMN_FAMILY IS NULL\n" + 
+                "AND SORT_ORDER = " + SortOrder.DESC.getSystemValue() + "\n" + 
+                "AND DATA_TYPE IN (" + PVarchar.INSTANCE.getSqlType() + "," + PDecimal.INSTANCE.getSqlType() + "," + PVarcharArray.INSTANCE.getSqlType() + "," + PDecimalArray.INSTANCE.getSqlType() + ")\n" +
+                "GROUP BY TENANT_ID,TABLE_SCHEM,TABLE_NAME");
+        Set<String> physicalTables = Sets.newHashSetWithExpectedSize(1024);
+        List<String> remainingTableNames = addPhysicalTables(conn, rs, PTableType.INDEX, physicalTables);
+        if (!remainingTableNames.isEmpty()) {
+            // Find tables/views for index
+            String indexLinkQuery = "SELECT TENANT_ID,TABLE_SCHEM,TABLE_NAME\n" + 
+                    "FROM SYSTEM.CATALOG\n" + 
+                    "WHERE COLUMN_NAME IS NULL\n" + 
+                    "AND (TENANT_ID, TABLE_SCHEM, COLUMN_FAMILY) IN " + getTableRVC(remainingTableNames) + "\n" +
+                    "AND LINK_TYPE = " + LinkType.INDEX_TABLE.getSerializedValue();
+             rs = conn.createStatement().executeQuery(indexLinkQuery);
+             remainingTableNames = addPhysicalTables(conn, rs, PTableType.VIEW, physicalTables);
+             if (!remainingTableNames.isEmpty()) {
+                 // Find physical table name from views, splitting on '.' to get schema name and table name
+                 String physicalLinkQuery = "SELECT null, " + 
+                 " CASE WHEN INSTR(COLUMN_FAMILY,'.') = 0 THEN NULL ELSE SUBSTR(COLUMN_FAMILY,1,INSTR(COLUMN_FAMILY,'.')) END,\n" + 
+                 " CASE WHEN INSTR(COLUMN_FAMILY,'.') = 0 THEN COLUMN_FAMILY ELSE SUBSTR(COLUMN_FAMILY,INSTR(COLUMN_FAMILY,'.')+1) END\n" + 
+                         "FROM SYSTEM.CATALOG\n" + 
+                         "WHERE COLUMN_NAME IS NULL\n" + 
+                         "AND COLUMN_FAMILY IS NOT NULL\n" + 
+                         "AND (TENANT_ID, TABLE_SCHEM, TABLE_NAME) IN " + getTableRVC(remainingTableNames) + "\n" +
+                         "AND LINK_TYPE = " + LinkType.PHYSICAL_TABLE.getSerializedValue();
+                 rs = conn.createStatement().executeQuery(physicalLinkQuery);
+                 // Add any tables (which will all be physical tables) which have not already been upgraded.
+                 addPhysicalTables(conn, rs, PTableType.TABLE, physicalTables);
+             }
+        }
+        return physicalTables;
+    }
+
+    private static void upgradeDescVarLengthRowKeys(PhoenixConnection upgradeConn, PhoenixConnection globalConn, String schemaName, String tableName, boolean isTable) throws SQLException {
+        String escapedTableName = SchemaUtil.getEscapedTableName(schemaName, tableName);
+        String tenantInfo = "";
+        PName tenantId = PName.EMPTY_NAME;
+        if (upgradeConn.getTenantId() != null) {
+            tenantId = upgradeConn.getTenantId();
+            tenantInfo = " for tenant " + tenantId.getString();
+        }
+        String msg = "Starting upgrade of " + escapedTableName + tenantInfo + "...";
+        System.out.println(msg);
+        logger.info(msg);
+        ResultSet rs = upgradeConn.createStatement().executeQuery("SELECT /*+ NO_INDEX */ count(*) FROM " + escapedTableName);
+        rs.next(); // Run query
+        List<String> tableNames = Lists.newArrayListWithExpectedSize(1024);
+        tableNames.add(tenantId == PName.EMPTY_NAME ? null : tenantId.getString());
+        tableNames.add(schemaName);
+        tableNames.add(tableName);
+        // Find views to mark as upgraded
+        if (isTable) {
+            String physicalName = SchemaUtil.getTableName(schemaName, tableName);
+            String query =
+                    "SELECT TENANT_ID,TABLE_SCHEM,TABLE_NAME\n" + 
+                    "FROM SYSTEM.CATALOG\n" + 
+                    "WHERE COLUMN_NAME IS NULL\n" + 
+                    "AND COLUMN_FAMILY = '" + physicalName + "'" +
+                    "AND LINK_TYPE = " + LinkType.PHYSICAL_TABLE.getSerializedValue();
+            rs = globalConn.createStatement().executeQuery(query);
+            while (rs.next()) {
+                tableNames.add(rs.getString(1));
+                tableNames.add(rs.getString(2));
+                tableNames.add(rs.getString(3));
+            }
+        }
+        // Mark the table and views as upgraded now
+        for (int i = 0; i < tableNames.size(); i += 3) {
+            String theTenantId = tableNames.get(i);
+            String theSchemaName = tableNames.get(i+1);
+            String theTableName = tableNames.get(i+2);
+            globalConn.createStatement().execute("UPSERT INTO " + PhoenixDatabaseMetaData.SYSTEM_CATALOG_NAME + 
+                " (" + PhoenixDatabaseMetaData.TENANT_ID + "," +
+                       PhoenixDatabaseMetaData.TABLE_SCHEM + "," +
+                       PhoenixDatabaseMetaData.TABLE_NAME + "," +
+                       MetaDataEndpointImpl.ROW_KEY_ORDER_OPTIMIZABLE + " BOOLEAN"
+               + ") VALUES (" +
+                       "'" + (theTenantId == null ? StringUtil.EMPTY_STRING : theTenantId) + "'," +
+                       "'" + (theSchemaName == null ? StringUtil.EMPTY_STRING : theSchemaName) + "'," +
+                       "'" + theTableName + "'," +
+                       "TRUE)");
+        }
+        globalConn.commit();
+        for (int i = 0; i < tableNames.size(); i += 3) {
+            String theTenantId = tableNames.get(i);
+            String theSchemaName = tableNames.get(i+1);
+            String theTableName = tableNames.get(i+2);
+            globalConn.getQueryServices().clearTableFromCache(
+                    theTenantId == null ? ByteUtil.EMPTY_BYTE_ARRAY : Bytes.toBytes(theTenantId),
+                    theSchemaName == null ? ByteUtil.EMPTY_BYTE_ARRAY : Bytes.toBytes(schemaName),
+                    Bytes.toBytes(theTableName), HConstants.LATEST_TIMESTAMP);
+        }
+        msg = "Completed upgrade of " + escapedTableName + tenantInfo;
+        System.out.println(msg);
+        logger.info(msg);
+    }
+    
+    private static boolean isInvalidTableToUpgrade(PTable table) throws SQLException {
+        return (table.getType() != PTableType.TABLE || // Must be a table
+            table.getTenantId() != null || // Must be global
+            !table.getPhysicalName().equals(table.getName())); // Must be the physical table
+    }
+    /**
+     * Upgrade tables and their indexes due to a bug causing descending row keys to have a row key that
+     * prevents them from being sorted correctly (PHOENIX-2067).
+     */
+    public static void upgradeDescVarLengthRowKeys(PhoenixConnection conn, List<String> tablesToUpgrade) throws SQLException {
+        if (tablesToUpgrade.isEmpty()) {
+            return;
+        }
+        if (conn.getClientInfo(PhoenixRuntime.CURRENT_SCN_ATTRIB) != null) {
+            throw new SQLException("May not specify the CURRENT_SCN property when upgrading");
+        }
+        if (conn.getClientInfo(PhoenixRuntime.TENANT_ID_ATTRIB) != null) {
+            throw new SQLException("May not specify the TENANT_ID_ATTRIB property when upgrading");
+        }
+        List<PTable> tablesNeedingUpgrading = Lists.newArrayListWithExpectedSize(tablesToUpgrade.size());
+        List<String> invalidTables = Lists.newArrayListWithExpectedSize(tablesToUpgrade.size());
+        for (String fullTableName : tablesToUpgrade) {
+            PTable table = PhoenixRuntime.getTable(conn, fullTableName);
+            if (isInvalidTableToUpgrade(table)) {
+                invalidTables.add(fullTableName);
+            } else {
+                tablesNeedingUpgrading.add(table);
+            }
+        }
+        if (!invalidTables.isEmpty()) {
+            StringBuilder buf = new StringBuilder("Only physical tables should be upgraded as their views and indexes will be updated with them: ");
+            for (String fullTableName : invalidTables) {
+                buf.append(fullTableName);
+                buf.append(' ');
+            }
+            throw new SQLException(buf.toString());
+        }
+        PhoenixConnection upgradeConn = new PhoenixConnection(conn, true);
+        try {
+            upgradeConn.setAutoCommit(true);
+            for (PTable table : tablesNeedingUpgrading) {
+                boolean wasUpgraded = false;
+                if (!table.rowKeyOrderOptimizable()) {
+                    wasUpgraded = true;
+                    upgradeDescVarLengthRowKeys(upgradeConn, conn, table.getSchemaName().getString(), table.getTableName().getString(), true);
+                }
+                
+                // Upgrade global indexes
+                for (PTable index : table.getIndexes()) {
+                    if (!index.rowKeyOrderOptimizable() && index.getIndexType() != IndexType.LOCAL) {
+                        wasUpgraded = true;
+                        upgradeDescVarLengthRowKeys(upgradeConn, conn, index.getSchemaName().getString(), index.getTableName().getString(), false);
+                    }
+                }
+                
+                String sharedViewIndexName = Bytes.toString(MetaDataUtil.getViewIndexPhysicalName(table.getName().getBytes()));
+                // Upgrade view indexes
+                wasUpgraded |= upgradeSharedIndex(upgradeConn, conn, sharedViewIndexName);
+                String sharedLocalIndexName = Bytes.toString(MetaDataUtil.getLocalIndexPhysicalName(table.getName().getBytes()));
+                // Upgrade view indexes
+                wasUpgraded |= upgradeSharedIndex(upgradeConn, conn, sharedLocalIndexName);
+                
+                if (!wasUpgraded) {
+                    System.out.println("Upgrade not required for this table or its indexes: " + table.getName().getString());
+                }
+            }
+        } finally {
+            upgradeConn.close();
+        }
+    }
+    
+    /**
+     * Upgrade shared indexes by querying for all that are associated with our
+     * physical table.
+     * @return true if any upgrades were performed and false otherwise.
+     */
+    private static boolean upgradeSharedIndex(PhoenixConnection upgradeConn, PhoenixConnection globalConn, String physicalName) throws SQLException {
+        String query =
+                "SELECT TENANT_ID,TABLE_SCHEM,TABLE_NAME\n" + 
+                "FROM SYSTEM.CATALOG cat1\n" + 
+                "WHERE COLUMN_NAME IS NULL\n" + 
+                "AND COLUMN_FAMILY = '" + physicalName + "'\n" + 
+                "AND LINK_TYPE = " + LinkType.PHYSICAL_TABLE.getSerializedValue() + "\n" +
+                "ORDER BY TENANT_ID";
+        ResultSet rs = globalConn.createStatement().executeQuery(query);
+        String lastTenantId = null;
+        Connection conn = globalConn;
+        String url = globalConn.getURL();
+        boolean wasUpgraded = false;
+        while (rs.next()) {
+            String fullTableName = SchemaUtil.getTableName(
+                    rs.getString(PhoenixDatabaseMetaData.TABLE_SCHEM),
+                    rs.getString(PhoenixDatabaseMetaData.TABLE_NAME));
+            String tenantId = rs.getString(1);
+            if (tenantId != null && !tenantId.equals(lastTenantId))  {
+                if (lastTenantId != null) {
+                    conn.close();
+                }
+                // Open tenant-specific connection when we find a new one
+                Properties props = new Properties(globalConn.getClientInfo());
+                props.setProperty(PhoenixRuntime.TENANT_ID_ATTRIB, tenantId);
+                conn = DriverManager.getConnection(url, props);
+                lastTenantId = tenantId;
+            }
+            PTable table = PhoenixRuntime.getTable(conn, fullTableName);
+            String tableTenantId = table.getTenantId() == null ? null : table.getTenantId().getString();
+            if (Objects.equal(lastTenantId, tableTenantId) && !table.rowKeyOrderOptimizable()) {
+                upgradeDescVarLengthRowKeys(upgradeConn, globalConn, table.getSchemaName().getString(), table.getTableName().getString(), false);
+                wasUpgraded = true;
+            }
+        }
+        rs.close();
+        if (lastTenantId != null) {
+            conn.close();
+        }
+        return wasUpgraded;
+    }
+
+    public static void addRowKeyOrderOptimizableCell(List<Mutation> tableMetadata, byte[] tableHeaderRowKey, long clientTimeStamp) {
+        Put put = new Put(tableHeaderRowKey, clientTimeStamp);
+        put.addColumn(PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
+                MetaDataEndpointImpl.ROW_KEY_ORDER_OPTIMIZABLE_BYTES, PBoolean.INSTANCE.toBytes(true));
+        tableMetadata.add(put);
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java b/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java
index 6e637b7..0f34582 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java
@@ -1558,6 +1558,31 @@ public class QueryCompilerTest extends BaseConnectionlessQueryTest {
     }
     
     @Test 
+    public void testDescVarbinaryNotSupported() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        try {
+            conn.createStatement().execute("CREATE TABLE t (k VARBINARY PRIMARY KEY DESC)");
+            fail();
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.DESC_VARBINARY_NOT_SUPPORTED.getErrorCode(), e.getErrorCode());
+        }
+        try {
+            conn.createStatement().execute("CREATE TABLE t (k1 VARCHAR NOT NULL, k2 VARBINARY, CONSTRAINT pk PRIMARY KEY (k1,k2 DESC))");
+            fail();
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.DESC_VARBINARY_NOT_SUPPORTED.getErrorCode(), e.getErrorCode());
+        }
+        try {
+            conn.createStatement().execute("CREATE TABLE t (k1 VARCHAR PRIMARY KEY)");
+            conn.createStatement().execute("ALTER TABLE t ADD k2 VARBINARY PRIMARY KEY DESC");
+            fail();
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.DESC_VARBINARY_NOT_SUPPORTED.getErrorCode(), e.getErrorCode());
+        }
+        conn.close();
+    }
+    
+    @Test 
     public void testDivideByZeroExpressionIndex() throws Exception {
         String ddl = "CREATE TABLE t (k1 INTEGER PRIMARY KEY)";
         Connection conn = DriverManager.getConnection(getUrl());
@@ -1705,9 +1730,10 @@ public class QueryCompilerTest extends BaseConnectionlessQueryTest {
     @Test
     public void testOrderByOrderPreservingFwd() throws Exception {
         Connection conn = DriverManager.getConnection(getUrl());
-        conn.createStatement().execute("CREATE TABLE t (k1 date not null, k2 date not null, k3 date not null, v varchar, constraint pk primary key(k1,k2,k3))");
+        conn.createStatement().execute("CREATE TABLE t (k1 date not null, k2 date not null, k3 varchar, v varchar, constraint pk primary key(k1,k2,k3))");
         String[] queries = {
                 "SELECT * FROM T ORDER BY (k1,k2), k3",
+                "SELECT * FROM T ORDER BY k1,k2,k3 NULLS FIRST",
                 "SELECT * FROM T ORDER BY k1,k2,k3",
                 "SELECT * FROM T ORDER BY k1,k2",
                 "SELECT * FROM T ORDER BY k1",
@@ -1727,8 +1753,9 @@ public class QueryCompilerTest extends BaseConnectionlessQueryTest {
     @Test
     public void testOrderByOrderPreservingRev() throws Exception {
         Connection conn = DriverManager.getConnection(getUrl());
-        conn.createStatement().execute("CREATE TABLE t (k1 date not null, k2 date not null, k3 date not null, v varchar, constraint pk primary key(k1,k2 DESC,k3))");
+        conn.createStatement().execute("CREATE TABLE t (k1 date not null, k2 date not null, k3 varchar, v varchar, constraint pk primary key(k1,k2 DESC,k3 DESC))");
         String[] queries = {
+                "SELECT * FROM T ORDER BY INVERT(k1),k2,k3 nulls last",
                 "SELECT * FROM T ORDER BY INVERT(k1),k2",
                 "SELECT * FROM T ORDER BY INVERT(k1)",
                  "SELECT * FROM T ORDER BY TRUNC(k1, 'DAY') DESC, CEIL(k2, 'HOUR') DESC",
@@ -1745,8 +1772,10 @@ public class QueryCompilerTest extends BaseConnectionlessQueryTest {
     @Test
     public void testNotOrderByOrderPreserving() throws Exception {
         Connection conn = DriverManager.getConnection(getUrl());
-        conn.createStatement().execute("CREATE TABLE t (k1 date not null, k2 date not null, k3 date not null, v varchar, constraint pk primary key(k1,k2,k3))");
+        conn.createStatement().execute("CREATE TABLE t (k1 date not null, k2 varchar, k3 varchar, v varchar, constraint pk primary key(k1,k2,k3 desc))");
         String[] queries = {
+                "SELECT * FROM T ORDER BY k1,k2 NULLS LAST",
+                "SELECT * FROM T ORDER BY k1,k2, k3 NULLS LAST",
                 "SELECT * FROM T ORDER BY k1,k3",
                 "SELECT * FROM T ORDER BY SUBSTR(TO_CHAR(k1),1,4)",
                 "SELECT * FROM T ORDER BY k2",

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/test/java/org/apache/phoenix/query/OrderByTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/query/OrderByTest.java b/phoenix-core/src/test/java/org/apache/phoenix/query/OrderByTest.java
new file mode 100644
index 0000000..f270d9d
--- /dev/null
+++ b/phoenix-core/src/test/java/org/apache/phoenix/query/OrderByTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.query;
+
+import static org.junit.Assert.assertEquals;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.hadoop.hbase.KeyValue;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.hbase.util.Pair;
+import org.apache.phoenix.schema.SortOrder;
+import org.apache.phoenix.util.PhoenixRuntime;
+import org.junit.Test;
+
+public class OrderByTest extends BaseConnectionlessQueryTest {
+    @Test
+    public void testSortOrderForSingleDescVarLengthCol() throws SQLException {
+        Connection conn = DriverManager.getConnection(getUrl());
+        conn.createStatement().execute("CREATE TABLE t (k VARCHAR PRIMARY KEY DESC)");
+        conn.createStatement().execute("UPSERT INTO t VALUES ('a')");
+        conn.createStatement().execute("UPSERT INTO t VALUES ('ab')");
+
+        Iterator<Pair<byte[],List<KeyValue>>> dataIterator = PhoenixRuntime.getUncommittedDataIterator(conn);
+        List<KeyValue> kvs = dataIterator.next().getSecond();
+        Collections.sort(kvs, KeyValue.COMPARATOR);
+        KeyValue first = kvs.get(0);
+        assertEquals("ab", Bytes.toString(SortOrder.invert(first.getRowArray(), first.getRowOffset(), first.getRowLength()-1)));
+        KeyValue second = kvs.get(1);
+        assertEquals("a", Bytes.toString(SortOrder.invert(second.getRowArray(), second.getRowOffset(), second.getRowLength()-1)));
+    }
+
+    @Test
+    public void testSortOrderForLeadingDescVarLengthColWithNullFollowing() throws SQLException {
+        Connection conn = DriverManager.getConnection(getUrl());
+        conn.createStatement().execute("CREATE TABLE t (k1 VARCHAR, k2 VARCHAR, CONSTRAINT pk PRIMARY KEY (k1 DESC,k2))");
+        conn.createStatement().execute("UPSERT INTO t VALUES ('a')");
+        conn.createStatement().execute("UPSERT INTO t VALUES ('ab')");
+
+        Iterator<Pair<byte[],List<KeyValue>>> dataIterator = PhoenixRuntime.getUncommittedDataIterator(conn);
+        List<KeyValue> kvs = dataIterator.next().getSecond();
+        Collections.sort(kvs, KeyValue.COMPARATOR);
+        KeyValue first = kvs.get(0);
+        assertEquals("ab", Bytes.toString(SortOrder.invert(first.getRowArray(), first.getRowOffset(), first.getRowLength()-1)));
+        KeyValue second = kvs.get(1);
+        assertEquals("a", Bytes.toString(SortOrder.invert(second.getRowArray(), second.getRowOffset(), second.getRowLength()-1)));
+    }
+
+    @Test
+    public void testSortOrderForLeadingDescVarLengthColWithNonNullFollowing() throws SQLException {
+        Connection conn = DriverManager.getConnection(getUrl());
+        conn.createStatement().execute("CREATE TABLE t (k1 VARCHAR, k2 VARCHAR NOT NULL, CONSTRAINT pk PRIMARY KEY (k1 DESC,k2))");
+        conn.createStatement().execute("UPSERT INTO t VALUES ('a','x')");
+        conn.createStatement().execute("UPSERT INTO t VALUES ('ab', 'x')");
+
+        Iterator<Pair<byte[],List<KeyValue>>> dataIterator = PhoenixRuntime.getUncommittedDataIterator(conn);
+        List<KeyValue> kvs = dataIterator.next().getSecond();
+        Collections.sort(kvs, KeyValue.COMPARATOR);
+        KeyValue first = kvs.get(0);
+        assertEquals("ab", Bytes.toString(SortOrder.invert(first.getRowArray(), first.getRowOffset(), 2)));
+        KeyValue second = kvs.get(1);
+        assertEquals("a", Bytes.toString(SortOrder.invert(second.getRowArray(), second.getRowOffset(), 1)));
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/test/java/org/apache/phoenix/schema/types/PDataTypeTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/schema/types/PDataTypeTest.java b/phoenix-core/src/test/java/org/apache/phoenix/schema/types/PDataTypeTest.java
index 85f9436..7ab9093 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/schema/types/PDataTypeTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/schema/types/PDataTypeTest.java
@@ -38,6 +38,7 @@ import java.util.List;
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.phoenix.exception.SQLExceptionCode;
+import org.apache.phoenix.query.QueryConstants;
 import org.apache.phoenix.schema.ConstraintViolationException;
 import org.apache.phoenix.schema.SortOrder;
 import org.apache.phoenix.util.TestUtil;
@@ -1754,4 +1755,15 @@ public class PDataTypeTest {
         assertTrue(PLong.INSTANCE.compareTo(l, i, PInteger.INSTANCE)==0);
         assertTrue(PInteger.INSTANCE.compareTo(i, l, PLong.INSTANCE)==0);
     }
+    
+    @Test
+    public void testSeparatorBytes() {
+        byte biggest = (byte) 0xFF;
+        assertEquals(biggest, QueryConstants.DESC_SEPARATOR_BYTE);
+        byte[] array = new byte[1];
+        for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++) {
+            array[0] = (byte) i;
+            assertTrue(Bytes.compareTo(array, QueryConstants.DESC_SEPARATOR_BYTE_ARRAY) <= 0);
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-protocol/src/main/PTable.proto
----------------------------------------------------------------------
diff --git a/phoenix-protocol/src/main/PTable.proto b/phoenix-protocol/src/main/PTable.proto
index cbbbdd6..a327803 100644
--- a/phoenix-protocol/src/main/PTable.proto
+++ b/phoenix-protocol/src/main/PTable.proto
@@ -84,4 +84,5 @@ message PTable {
   optional int64 statsTimeStamp = 23;
   optional bool storeNulls = 24;
   optional int32 baseColumnCount = 25;
+  optional bool rowKeyOrderOptimizable = 26;
 }


[16/50] [abbrv] phoenix git commit: PHOENIX-1889 Support alter/replace and drop functions(Rajeshbabu)

Posted by ma...@apache.org.
PHOENIX-1889 Support alter/replace and drop functions(Rajeshbabu)


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/11bdb0ee
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/11bdb0ee
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/11bdb0ee

Branch: refs/heads/calcite
Commit: 11bdb0eedbbea1e58b60fe8ebba21e5c168261b7
Parents: fa2d79a
Author: Rajeshbabu Chintaguntla <ra...@apache.org>
Authored: Thu Jul 9 17:24:39 2015 +0530
Committer: Rajeshbabu Chintaguntla <ra...@apache.org>
Committed: Thu Jul 9 17:24:39 2015 +0530

----------------------------------------------------------------------
 .../phoenix/end2end/UserDefinedFunctionsIT.java | 145 ++++++++++++++++++-
 phoenix-core/src/main/antlr3/PhoenixSQL.g       |   5 +-
 .../phoenix/compile/CreateFunctionCompiler.java |   4 +-
 .../coprocessor/MetaDataEndpointImpl.java       |  40 +++--
 .../coprocessor/generated/PFunctionProtos.java  | 100 ++++++++++++-
 .../apache/phoenix/jdbc/PhoenixStatement.java   |  28 ++--
 .../phoenix/parse/CreateFunctionStatement.java  |   7 +-
 .../org/apache/phoenix/parse/PFunction.java     |  28 +++-
 .../apache/phoenix/parse/ParseNodeFactory.java  |   4 +-
 .../query/ConnectionQueryServicesImpl.java      |   1 +
 .../apache/phoenix/schema/MetaDataClient.java   |  20 ++-
 phoenix-protocol/src/main/PFunction.proto       |   1 +
 12 files changed, 336 insertions(+), 47 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/11bdb0ee/phoenix-core/src/it/java/org/apache/phoenix/end2end/UserDefinedFunctionsIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/UserDefinedFunctionsIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/UserDefinedFunctionsIT.java
index 613231d..e2b7b4c 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/UserDefinedFunctionsIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/UserDefinedFunctionsIT.java
@@ -17,15 +17,18 @@
  */
 package org.apache.phoenix.end2end;
 
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SYSTEM_CATALOG_SCHEMA;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SYSTEM_FUNCTION_TABLE;
+import static org.apache.phoenix.query.QueryServices.DYNAMIC_JARS_DIR_KEY;
 import static org.apache.phoenix.util.PhoenixRuntime.JDBC_PROTOCOL;
 import static org.apache.phoenix.util.PhoenixRuntime.JDBC_PROTOCOL_SEPARATOR;
 import static org.apache.phoenix.util.PhoenixRuntime.JDBC_PROTOCOL_TERMINATOR;
 import static org.apache.phoenix.util.PhoenixRuntime.PHOENIX_TEST_DRIVER_URL_PARAM;
-import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SYSTEM_CATALOG_SCHEMA;
-import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SYSTEM_FUNCTION_TABLE;
-import static org.apache.phoenix.query.QueryServices.DYNAMIC_JARS_DIR_KEY;
 import static org.apache.phoenix.util.TestUtil.LOCALHOST;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import java.io.BufferedInputStream;
 import java.io.File;
@@ -34,6 +37,7 @@ import java.io.FileOutputStream;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.sql.Connection;
+import java.sql.DriverManager;
 import java.sql.ResultSet;
 import java.sql.Statement;
 import java.util.HashSet;
@@ -60,8 +64,6 @@ import org.apache.phoenix.query.QueryServices;
 import org.apache.phoenix.schema.FunctionAlreadyExistsException;
 import org.apache.phoenix.schema.FunctionNotFoundException;
 import org.apache.phoenix.schema.ValueRangeExcpetion;
-import org.apache.phoenix.schema.types.PDataType;
-import org.apache.phoenix.schema.types.PArrayDataType;
 import org.apache.phoenix.util.PhoenixRuntime;
 import org.apache.phoenix.util.QueryUtil;
 import org.apache.phoenix.util.ReadOnlyProps;
@@ -561,6 +563,104 @@ public class UserDefinedFunctionsIT extends BaseOwnClusterIT{
     }
 
     @Test
+    public void testUDFsWhenTimestampManagedAtClient() throws Exception {
+        long ts = 100;
+        Properties props = new Properties();
+        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts));
+        Connection conn = DriverManager.getConnection(url, props);
+        Statement stmt = conn.createStatement();
+        String query = "select count(*) from "+ SYSTEM_CATALOG_SCHEMA + ".\"" + SYSTEM_FUNCTION_TABLE + "\"";
+        ResultSet rs = stmt.executeQuery(query);
+        rs.next();
+        int numRowsBefore = rs.getInt(1);
+        stmt.execute("create function mysum61(INTEGER, INTEGER CONSTANT defaultValue=10 minvalue=1 maxvalue=15 ) returns INTEGER as 'org.apache.phoenix.end2end."+MY_SUM_CLASS_NAME+"' using jar "
+                + "'"+util.getConfiguration().get(DYNAMIC_JARS_DIR_KEY) + "/myjar2.jar"+"'");
+        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 10));
+        conn = DriverManager.getConnection(url, props);
+        stmt = conn.createStatement();
+        rs = stmt.executeQuery(query);
+        rs.next();
+        int numRowsAfter= rs.getInt(1);
+        assertEquals(3, numRowsAfter - numRowsBefore);
+        stmt.execute("drop function mysum61");
+        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 20));
+        conn = DriverManager.getConnection(url, props);
+        stmt = conn.createStatement();
+        rs = stmt.executeQuery(query);
+        rs.next();
+        assertEquals(numRowsBefore, rs.getInt(1));
+        conn.createStatement().execute("create table t62(k integer primary key, k1 integer, lastname varchar)");
+        try {
+            rs = stmt.executeQuery("select mysum61(k1) from t62");
+            fail("FunctionNotFoundException should be thrown");
+        } catch(FunctionNotFoundException e) {
+            
+        }
+        try {
+            stmt.execute("drop function mysum61");
+            fail("FunctionNotFoundException should be thrown");
+        } catch(FunctionNotFoundException e) {
+            
+        }
+        try {
+            stmt.execute("drop function if exists mysum61");
+        } catch(FunctionNotFoundException e) {
+            fail("FunctionNotFoundException should not be thrown");
+        }
+        stmt.execute("create function mysum61(INTEGER, INTEGER CONSTANT defaultValue=10 minvalue=1 maxvalue=15 ) returns INTEGER as 'org.apache.phoenix.end2end."+MY_SUM_CLASS_NAME+"' using jar "
+                + "'"+util.getConfiguration().get(DYNAMIC_JARS_DIR_KEY) + "/myjar2.jar"+"'");
+        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 30));
+        conn = DriverManager.getConnection(url, props);
+        stmt = conn.createStatement();
+        try {
+            rs = stmt.executeQuery("select mysum61(k1) from t62");
+        } catch(FunctionNotFoundException e) {
+            fail("FunctionNotFoundException should not be thrown");
+        }
+        conn.createStatement().execute("create table t61(k integer primary key, k1 integer, lastname varchar)");
+        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 40));
+        conn = DriverManager.getConnection(url, props);
+        stmt = conn.createStatement();
+        stmt.execute("upsert into t61 values(1,1,'jock')");
+        conn.commit();
+        stmt.execute("create function myfunction6(VARCHAR) returns VARCHAR as 'org.apache.phoenix.end2end."+MY_REVERSE_CLASS_NAME+"' using jar "
+                + "'"+util.getConfiguration().get(DYNAMIC_JARS_DIR_KEY) + "/myjar1.jar"+"'");
+        stmt.execute("create or replace function myfunction6(INTEGER, INTEGER CONSTANT defaultValue=10 minvalue=1 maxvalue=15 ) returns INTEGER as 'org.apache.phoenix.end2end."+MY_SUM_CLASS_NAME+"' using jar "
+                + "'"+util.getConfiguration().get(DYNAMIC_JARS_DIR_KEY) + "/myjar2.jar"+"'");
+        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 50));
+        conn = DriverManager.getConnection(url, props);
+        stmt = conn.createStatement();
+        rs = stmt.executeQuery("select myfunction6(k,12) from t61");
+        assertTrue(rs.next());
+        assertEquals(13, rs.getInt(1));
+        rs = stmt.executeQuery("select myfunction6(k) from t61");
+        assertTrue(rs.next());
+        assertEquals(11, rs.getInt(1));
+        rs = stmt.executeQuery("select k from t61 where myfunction6(k)=11");
+        assertTrue(rs.next());
+        assertEquals(1, rs.getInt(1));
+        stmt.execute("create or replace function myfunction6(VARCHAR) returns VARCHAR as 'org.apache.phoenix.end2end."+MY_REVERSE_CLASS_NAME+"' using jar "
+                + "'"+util.getConfiguration().get(DYNAMIC_JARS_DIR_KEY) + "/myjar1.jar"+"'");
+        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 60));
+        conn = DriverManager.getConnection(url, props);
+        stmt = conn.createStatement();
+        rs = stmt.executeQuery("select k from t61 where myfunction6(lastname)='kcoj'");
+        assertTrue(rs.next());
+        assertEquals(1, rs.getInt(1));
+        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 60));
+        props.setProperty(QueryServices.ALLOW_USER_DEFINED_FUNCTIONS_ATTRIB, "false");
+        conn = DriverManager.getConnection(url, props);
+        stmt = conn.createStatement();
+        try {
+            rs = stmt.executeQuery("select k from t61 where reverse(lastname,11)='kcoj'");
+            fail("FunctionNotFoundException should be thrown.");
+        } catch(FunctionNotFoundException e) {
+            
+        }
+
+    }
+
+    @Test
     public void testFunctionalIndexesWithUDFFunction() throws Exception {
         Connection conn = driver.connect(url, EMPTY_PROPS);
         Statement stmt = conn.createStatement();
@@ -591,6 +691,39 @@ public class UserDefinedFunctionsIT extends BaseOwnClusterIT{
         assertFalse(rs.next());
     }
 
+    @Test
+    public void testReplaceFunction() throws Exception {
+        Connection conn = driver.connect(url, EMPTY_PROPS);
+        Statement stmt = conn.createStatement();
+        conn.createStatement().execute("create table t10(k integer primary key, k1 integer, lastname varchar)");
+        stmt.execute("upsert into t10 values(1,1,'jock')");
+        conn.commit();
+        stmt.execute("create function myfunction63(VARCHAR) returns VARCHAR as 'org.apache.phoenix.end2end."+MY_REVERSE_CLASS_NAME+"' using jar "
+                + "'"+util.getConfiguration().get(DYNAMIC_JARS_DIR_KEY) + "/myjar1.jar"+"'");
+        stmt.execute("create or replace function myfunction63(INTEGER, INTEGER CONSTANT defaultValue=10 minvalue=1 maxvalue=15 ) returns INTEGER as 'org.apache.phoenix.end2end."+MY_SUM_CLASS_NAME+"' using jar "
+                + "'"+util.getConfiguration().get(DYNAMIC_JARS_DIR_KEY) + "/myjar2.jar"+"'");
+        ResultSet rs = stmt.executeQuery("select myfunction63(k,12) from t10");
+        assertTrue(rs.next());
+        assertEquals(13, rs.getInt(1));
+        rs = stmt.executeQuery("select myfunction63(k) from t10");
+        assertTrue(rs.next());
+        assertEquals(11, rs.getInt(1));
+        rs = stmt.executeQuery("select k from t10 where myfunction63(k)=11");
+        assertTrue(rs.next());
+        assertEquals(1, rs.getInt(1));
+        Connection conn2 = driver.connect(url, EMPTY_PROPS);
+        stmt = conn2.createStatement();
+        rs = stmt.executeQuery("select myfunction63(k,12) from t10");
+        assertTrue(rs.next());
+        assertEquals(13, rs.getInt(1));
+        rs = stmt.executeQuery("select myfunction63(k) from t10");
+        assertTrue(rs.next());
+        assertEquals(11, rs.getInt(1));
+        rs = stmt.executeQuery("select k from t10 where myfunction63(k)=11");
+        assertTrue(rs.next());
+        assertEquals(1, rs.getInt(1));
+    }
+
     /**
      * Compiles the test class with bogus code into a .class file.
      */

http://git-wip-us.apache.org/repos/asf/phoenix/blob/11bdb0ee/phoenix-core/src/main/antlr3/PhoenixSQL.g
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/antlr3/PhoenixSQL.g b/phoenix-core/src/main/antlr3/PhoenixSQL.g
index 69014a2..ca5e778 100644
--- a/phoenix-core/src/main/antlr3/PhoenixSQL.g
+++ b/phoenix-core/src/main/antlr3/PhoenixSQL.g
@@ -122,6 +122,7 @@ tokens
     JAR='jar';
     DEFAULTVALUE='defaultvalue';
     CONSTANT = 'constant';
+    REPLACE = 'replace';
 }
 
 
@@ -540,12 +541,12 @@ trace_node returns [TraceStatement ret]
 
 // Parse a trace statement.
 create_function_node returns [CreateFunctionStatement ret]
-    :   CREATE (temp=TEMPORARY)? FUNCTION function=identifier 
+    :   CREATE (OR replace=REPLACE)? (temp=TEMPORARY)? FUNCTION function=identifier 
        (LPAREN args=zero_or_more_data_types RPAREN)
        RETURNS r=identifier AS (className= jar_path)
        (USING JAR (jarPath = jar_path))?
         {
-            $ret = factory.createFunction(new PFunction(SchemaUtil.normalizeIdentifier(function), args,r,(String)className.getValue(), jarPath == null ? null : (String)jarPath.getValue()), temp!=null);;
+            $ret = factory.createFunction(new PFunction(SchemaUtil.normalizeIdentifier(function), args,r,(String)className.getValue(), jarPath == null ? null : (String)jarPath.getValue()), temp!=null, replace!=null);
         } 
     ;
 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/11bdb0ee/phoenix-core/src/main/java/org/apache/phoenix/compile/CreateFunctionCompiler.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/CreateFunctionCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/CreateFunctionCompiler.java
index 2e3a873..138c75d 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/CreateFunctionCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/CreateFunctionCompiler.java
@@ -63,7 +63,9 @@ public class CreateFunctionCompiler {
 
             @Override
             public ExplainPlan getExplainPlan() throws SQLException {
-                return new ExplainPlan(Collections.singletonList("CREATE FUNCTION"));
+                return new ExplainPlan(Collections.singletonList("CREATE"
+                        + (create.getFunctionInfo().isReplace() ? " OR REPLACE" : "")
+                        + " FUNCTION"));
             }
 
             @Override

http://git-wip-us.apache.org/repos/asf/phoenix/blob/11bdb0ee/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 b27beb4..f786768 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
@@ -478,7 +478,7 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
     }
 
     private List<PFunction> buildFunctions(List<byte[]> keys, Region region,
-            long clientTimeStamp) throws IOException, SQLException {
+            long clientTimeStamp, boolean isReplace, List<Mutation> deleteMutationsForReplace) throws IOException, SQLException {
         List<KeyRange> keyRanges = Lists.newArrayListWithExpectedSize(keys.size());
         for (byte[] key : keys) {
             byte[] stopKey = ByteUtil.concat(key, QueryConstants.SEPARATOR_BYTE_ARRAY);
@@ -501,7 +501,8 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
         try {
             for(int i = 0; i< keys.size(); i++) {
                 function = null;
-                function = getFunction(scanner);
+                function =
+                        getFunction(scanner, isReplace, clientTimeStamp, deleteMutationsForReplace);
                 if (function == null) {
                     return null;
                 }
@@ -828,7 +829,7 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
             disableWAL, multiTenant, storeNulls, viewType, viewIndexId, indexType, stats, baseColumnCount);
     }
 
-    private PFunction getFunction(RegionScanner scanner)
+    private PFunction getFunction(RegionScanner scanner, final boolean isReplace, long clientTimeStamp, List<Mutation> deleteMutationsForReplace)
             throws IOException, SQLException {
         List<Cell> results = Lists.newArrayList();
         scanner.next(results);
@@ -837,12 +838,19 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
         }
         Cell[] functionKeyValues = new Cell[FUNCTION_KV_COLUMNS.size()];
         Cell[] functionArgKeyValues = new Cell[FUNCTION_ARG_KV_COLUMNS.size()];
-
         // Create PFunction based on KeyValues from scan
         Cell keyValue = results.get(0);
         byte[] keyBuffer = keyValue.getRowArray();
         int keyLength = keyValue.getRowLength();
         int keyOffset = keyValue.getRowOffset();
+        long currentTimeMillis = EnvironmentEdgeManager.currentTimeMillis();
+        if(isReplace) {
+            long deleteTimeStamp =
+                    clientTimeStamp == HConstants.LATEST_TIMESTAMP ? currentTimeMillis - 1
+                            : (keyValue.getTimestamp() < clientTimeStamp ? clientTimeStamp - 1
+                                    : keyValue.getTimestamp());
+            deleteMutationsForReplace.add(new Delete(keyBuffer, keyOffset, keyLength, deleteTimeStamp));
+        }
         PName tenantId = newPName(keyBuffer, keyOffset, keyLength);
         int tenantIdLength = (tenantId == null) ? 0 : tenantId.getBytes().length;
         if (tenantIdLength == 0) {
@@ -908,6 +916,14 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
                 break;
             }
             Cell typeKv = results.get(0);
+            if(isReplace) {
+                long deleteTimeStamp =
+                        clientTimeStamp == HConstants.LATEST_TIMESTAMP ? currentTimeMillis - 1
+                                : (typeKv.getTimestamp() < clientTimeStamp ? clientTimeStamp - 1
+                                        : typeKv.getTimestamp());
+                deleteMutationsForReplace.add(new Delete(typeKv.getRowArray(), typeKv
+                        .getRowOffset(), typeKv.getRowLength(), deleteTimeStamp));
+            }
             int typeKeyLength = typeKv.getRowLength();
             PName typeName =
                     newPName(typeKv.getRowArray(), typeKv.getRowOffset() + offset, typeKeyLength
@@ -1020,18 +1036,18 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
     }
 
     private PFunction loadFunction(RegionCoprocessorEnvironment env, byte[] key,
-            ImmutableBytesPtr cacheKey, long clientTimeStamp, long asOfTimeStamp)
+            ImmutableBytesPtr cacheKey, long clientTimeStamp, long asOfTimeStamp, boolean isReplace, List<Mutation> deleteMutationsForReplace)
             throws IOException, SQLException {
             Region region = env.getRegion();
             Cache<ImmutableBytesPtr,PMetaDataEntity> metaDataCache = GlobalCache.getInstance(this.env).getMetaDataCache();
             PFunction function = (PFunction)metaDataCache.getIfPresent(cacheKey);
             // We always cache the latest version - fault in if not in cache
-            if (function != null) {
+            if (function != null && !isReplace) {
                 return function;
             }
             ArrayList<byte[]> arrayList = new ArrayList<byte[]>(1);
             arrayList.add(key);
-            List<PFunction> functions = buildFunctions(arrayList, region, asOfTimeStamp);
+            List<PFunction> functions = buildFunctions(arrayList, region, asOfTimeStamp, isReplace, deleteMutationsForReplace);
             if(functions != null) return functions.get(0);
             // if not found then check if newer table already exists and add delete marker for timestamp
             // found
@@ -2075,7 +2091,9 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
             if(functionsAvailable.size() == numFunctions) return functionsAvailable;
 
             // Query for the latest table first, since it's not cached
-            List<PFunction> buildFunctions = buildFunctions(keys, region, clientTimeStamp);
+            List<PFunction> buildFunctions =
+                    buildFunctions(keys, region, clientTimeStamp, false,
+                        Collections.<Mutation> emptyList());
             if(buildFunctions == null || buildFunctions.isEmpty()) {
                 return null;
             }
@@ -2582,7 +2600,7 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
                 // exists without making an additional query
                 ImmutableBytesPtr cacheKey = new FunctionBytesPtr(lockKey);
                 PFunction function =
-                        loadFunction(env, lockKey, cacheKey, clientTimeStamp, clientTimeStamp);
+                        loadFunction(env, lockKey, cacheKey, clientTimeStamp, clientTimeStamp, request.getReplace(), functionMetaData);
                 if (function != null) {
                     if (function.getTimeStamp() < clientTimeStamp) {
                         // If the function is older than the client time stamp and it's deleted,
@@ -2592,7 +2610,9 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
                             builder.setMutationTime(EnvironmentEdgeManager.currentTimeMillis());
                             builder.addFunction(PFunction.toProto(function));
                             done.run(builder.build());
-                            return;
+                            if(!request.getReplace()) {
+                                return;
+                            }
                         }
                     } else {
                         builder.setReturnCode(MetaDataProtos.MutationCode.NEWER_FUNCTION_FOUND);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/11bdb0ee/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PFunctionProtos.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PFunctionProtos.java b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PFunctionProtos.java
index 12927aa..7783a95 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PFunctionProtos.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PFunctionProtos.java
@@ -1317,6 +1317,16 @@ public final class PFunctionProtos {
      * <code>optional bool isArrayReturnType = 8;</code>
      */
     boolean getIsArrayReturnType();
+
+    // optional bool isReplace = 9;
+    /**
+     * <code>optional bool isReplace = 9;</code>
+     */
+    boolean hasIsReplace();
+    /**
+     * <code>optional bool isReplace = 9;</code>
+     */
+    boolean getIsReplace();
   }
   /**
    * Protobuf type {@code PFunction}
@@ -1412,6 +1422,11 @@ public final class PFunctionProtos {
               isArrayReturnType_ = input.readBool();
               break;
             }
+            case 72: {
+              bitField0_ |= 0x00000080;
+              isReplace_ = input.readBool();
+              break;
+            }
           }
         }
       } catch (com.google.protobuf.InvalidProtocolBufferException e) {
@@ -1711,6 +1726,22 @@ public final class PFunctionProtos {
       return isArrayReturnType_;
     }
 
+    // optional bool isReplace = 9;
+    public static final int ISREPLACE_FIELD_NUMBER = 9;
+    private boolean isReplace_;
+    /**
+     * <code>optional bool isReplace = 9;</code>
+     */
+    public boolean hasIsReplace() {
+      return ((bitField0_ & 0x00000080) == 0x00000080);
+    }
+    /**
+     * <code>optional bool isReplace = 9;</code>
+     */
+    public boolean getIsReplace() {
+      return isReplace_;
+    }
+
     private void initFields() {
       functionName_ = "";
       arguments_ = java.util.Collections.emptyList();
@@ -1720,6 +1751,7 @@ public final class PFunctionProtos {
       tenantId_ = com.google.protobuf.ByteString.EMPTY;
       returnType_ = "";
       isArrayReturnType_ = false;
+      isReplace_ = false;
     }
     private byte memoizedIsInitialized = -1;
     public final boolean isInitialized() {
@@ -1775,6 +1807,9 @@ public final class PFunctionProtos {
       if (((bitField0_ & 0x00000040) == 0x00000040)) {
         output.writeBool(8, isArrayReturnType_);
       }
+      if (((bitField0_ & 0x00000080) == 0x00000080)) {
+        output.writeBool(9, isReplace_);
+      }
       getUnknownFields().writeTo(output);
     }
 
@@ -1816,6 +1851,10 @@ public final class PFunctionProtos {
         size += com.google.protobuf.CodedOutputStream
           .computeBoolSize(8, isArrayReturnType_);
       }
+      if (((bitField0_ & 0x00000080) == 0x00000080)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeBoolSize(9, isReplace_);
+      }
       size += getUnknownFields().getSerializedSize();
       memoizedSerializedSize = size;
       return size;
@@ -1876,6 +1915,11 @@ public final class PFunctionProtos {
         result = result && (getIsArrayReturnType()
             == other.getIsArrayReturnType());
       }
+      result = result && (hasIsReplace() == other.hasIsReplace());
+      if (hasIsReplace()) {
+        result = result && (getIsReplace()
+            == other.getIsReplace());
+      }
       result = result &&
           getUnknownFields().equals(other.getUnknownFields());
       return result;
@@ -1921,6 +1965,10 @@ public final class PFunctionProtos {
         hash = (37 * hash) + ISARRAYRETURNTYPE_FIELD_NUMBER;
         hash = (53 * hash) + hashBoolean(getIsArrayReturnType());
       }
+      if (hasIsReplace()) {
+        hash = (37 * hash) + ISREPLACE_FIELD_NUMBER;
+        hash = (53 * hash) + hashBoolean(getIsReplace());
+      }
       hash = (29 * hash) + getUnknownFields().hashCode();
       memoizedHashCode = hash;
       return hash;
@@ -2051,6 +2099,8 @@ public final class PFunctionProtos {
         bitField0_ = (bitField0_ & ~0x00000040);
         isArrayReturnType_ = false;
         bitField0_ = (bitField0_ & ~0x00000080);
+        isReplace_ = false;
+        bitField0_ = (bitField0_ & ~0x00000100);
         return this;
       }
 
@@ -2116,6 +2166,10 @@ public final class PFunctionProtos {
           to_bitField0_ |= 0x00000040;
         }
         result.isArrayReturnType_ = isArrayReturnType_;
+        if (((from_bitField0_ & 0x00000100) == 0x00000100)) {
+          to_bitField0_ |= 0x00000080;
+        }
+        result.isReplace_ = isReplace_;
         result.bitField0_ = to_bitField0_;
         onBuilt();
         return result;
@@ -2187,6 +2241,9 @@ public final class PFunctionProtos {
         if (other.hasIsArrayReturnType()) {
           setIsArrayReturnType(other.getIsArrayReturnType());
         }
+        if (other.hasIsReplace()) {
+          setIsReplace(other.getIsReplace());
+        }
         this.mergeUnknownFields(other.getUnknownFields());
         return this;
       }
@@ -2870,6 +2927,39 @@ public final class PFunctionProtos {
         return this;
       }
 
+      // optional bool isReplace = 9;
+      private boolean isReplace_ ;
+      /**
+       * <code>optional bool isReplace = 9;</code>
+       */
+      public boolean hasIsReplace() {
+        return ((bitField0_ & 0x00000100) == 0x00000100);
+      }
+      /**
+       * <code>optional bool isReplace = 9;</code>
+       */
+      public boolean getIsReplace() {
+        return isReplace_;
+      }
+      /**
+       * <code>optional bool isReplace = 9;</code>
+       */
+      public Builder setIsReplace(boolean value) {
+        bitField0_ |= 0x00000100;
+        isReplace_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>optional bool isReplace = 9;</code>
+       */
+      public Builder clearIsReplace() {
+        bitField0_ = (bitField0_ & ~0x00000100);
+        isReplace_ = false;
+        onChanged();
+        return this;
+      }
+
       // @@protoc_insertion_point(builder_scope:PFunction)
     }
 
@@ -2903,14 +2993,14 @@ public final class PFunctionProtos {
       "\n\017PFunction.proto\"\207\001\n\014PFunctionArg\022\024\n\014ar" +
       "gumentType\030\001 \002(\t\022\023\n\013isArrayType\030\002 \001(\010\022\022\n" +
       "\nisConstant\030\003 \001(\010\022\024\n\014defaultValue\030\004 \001(\t\022" +
-      "\020\n\010minValue\030\005 \001(\t\022\020\n\010maxValue\030\006 \001(\t\"\273\001\n\t" +
+      "\020\n\010minValue\030\005 \001(\t\022\020\n\010maxValue\030\006 \001(\t\"\316\001\n\t" +
       "PFunction\022\024\n\014functionName\030\001 \002(\t\022 \n\targum" +
       "ents\030\002 \003(\0132\r.PFunctionArg\022\021\n\tclassname\030\003" +
       " \002(\t\022\021\n\ttimeStamp\030\004 \002(\003\022\017\n\007jarPath\030\005 \001(\t" +
       "\022\020\n\010tenantId\030\006 \001(\014\022\022\n\nreturnType\030\007 \001(\t\022\031" +
-      "\n\021isArrayReturnType\030\010 \001(\010BC\n(org.apache." +
-      "phoenix.coprocessor.generatedB\017PFunction",
-      "ProtosH\001\210\001\001\240\001\001"
+      "\n\021isArrayReturnType\030\010 \001(\010\022\021\n\tisReplace\030\t" +
+      " \001(\010BC\n(org.apache.phoenix.coprocessor.g",
+      "eneratedB\017PFunctionProtosH\001\210\001\001\240\001\001"
     };
     com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
       new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
@@ -2928,7 +3018,7 @@ public final class PFunctionProtos {
           internal_static_PFunction_fieldAccessorTable = new
             com.google.protobuf.GeneratedMessage.FieldAccessorTable(
               internal_static_PFunction_descriptor,
-              new java.lang.String[] { "FunctionName", "Arguments", "Classname", "TimeStamp", "JarPath", "TenantId", "ReturnType", "IsArrayReturnType", });
+              new java.lang.String[] { "FunctionName", "Arguments", "Classname", "TimeStamp", "JarPath", "TenantId", "ReturnType", "IsArrayReturnType", "IsReplace", });
           return null;
         }
       };

http://git-wip-us.apache.org/repos/asf/phoenix/blob/11bdb0ee/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
index 2bb3b92..f323ec4 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
@@ -116,6 +116,7 @@ import org.apache.phoenix.query.QueryServices;
 import org.apache.phoenix.query.QueryServicesOptions;
 import org.apache.phoenix.schema.ExecuteQueryNotApplicableException;
 import org.apache.phoenix.schema.ExecuteUpdateNotApplicableException;
+import org.apache.phoenix.schema.FunctionNotFoundException;
 import org.apache.phoenix.schema.MetaDataClient;
 import org.apache.phoenix.schema.PDatum;
 import org.apache.phoenix.schema.PIndexState;
@@ -354,7 +355,7 @@ public class PhoenixStatement implements Statement, SQLCloseable, org.apache.pho
         @Override
         public QueryPlan compilePlan(PhoenixStatement stmt, Sequence.ValueOp seqAction) throws SQLException {
             if(!getUdfParseNodes().isEmpty()) {
-                stmt.throwIfUnallowedUserDefinedFunctions();
+                stmt.throwIfUnallowedUserDefinedFunctions(getUdfParseNodes());
             }
             SelectStatement select = SubselectRewriter.flatten(this, stmt.getConnection());
             ColumnResolver resolver = FromCompiler.getResolverForQuery(select, stmt.getConnection());
@@ -531,7 +532,7 @@ public class PhoenixStatement implements Statement, SQLCloseable, org.apache.pho
         @Override
         public MutationPlan compilePlan(PhoenixStatement stmt, Sequence.ValueOp seqAction) throws SQLException {
             if(!getUdfParseNodes().isEmpty()) {
-                stmt.throwIfUnallowedUserDefinedFunctions();
+                stmt.throwIfUnallowedUserDefinedFunctions(getUdfParseNodes());
             }
             UpsertCompiler compiler = new UpsertCompiler(stmt);
             MutationPlan plan = compiler.compile(this);
@@ -549,7 +550,7 @@ public class PhoenixStatement implements Statement, SQLCloseable, org.apache.pho
         @Override
         public MutationPlan compilePlan(PhoenixStatement stmt, Sequence.ValueOp seqAction) throws SQLException {
             if(!getUdfParseNodes().isEmpty()) {
-                stmt.throwIfUnallowedUserDefinedFunctions();
+                stmt.throwIfUnallowedUserDefinedFunctions(getUdfParseNodes());
             }
             DeleteCompiler compiler = new DeleteCompiler(stmt);
             MutationPlan plan = compiler.compile(this);
@@ -575,15 +576,15 @@ public class PhoenixStatement implements Statement, SQLCloseable, org.apache.pho
 
     private static class ExecutableCreateFunctionStatement extends CreateFunctionStatement implements CompilableStatement {
 
-        public ExecutableCreateFunctionStatement(PFunction functionInfo, boolean temporary) {
-            super(functionInfo, temporary);
+        public ExecutableCreateFunctionStatement(PFunction functionInfo, boolean temporary, boolean isReplace) {
+            super(functionInfo, temporary, isReplace);
         }
 
 
         @SuppressWarnings("unchecked")
         @Override
         public MutationPlan compilePlan(final PhoenixStatement stmt, Sequence.ValueOp seqAction) throws SQLException {
-            stmt.throwIfUnallowedUserDefinedFunctions();
+            stmt.throwIfUnallowedUserDefinedFunctions(Collections.EMPTY_MAP);
             CreateFunctionCompiler compiler = new CreateFunctionCompiler(stmt);
             return compiler.compile(this);
         }
@@ -641,7 +642,7 @@ public class PhoenixStatement implements Statement, SQLCloseable, org.apache.pho
         @Override
         public MutationPlan compilePlan(PhoenixStatement stmt, Sequence.ValueOp seqAction) throws SQLException {
             if(!getUdfParseNodes().isEmpty()) {
-                stmt.throwIfUnallowedUserDefinedFunctions();
+                stmt.throwIfUnallowedUserDefinedFunctions(getUdfParseNodes());
             }
             CreateIndexCompiler compiler = new CreateIndexCompiler(stmt);
             return compiler.compile(this);
@@ -1022,8 +1023,8 @@ public class PhoenixStatement implements Statement, SQLCloseable, org.apache.pho
         }
         
         @Override
-        public CreateFunctionStatement createFunction(PFunction functionInfo, boolean temporary) {
-            return new ExecutableCreateFunctionStatement(functionInfo, temporary);
+        public CreateFunctionStatement createFunction(PFunction functionInfo, boolean temporary, boolean isReplace) {
+            return new ExecutableCreateFunctionStatement(functionInfo, temporary, isReplace);
         }
         @Override
         public DropSequenceStatement dropSequence(TableName tableName, boolean ifExists, int bindCount){
@@ -1504,14 +1505,17 @@ public class PhoenixStatement implements Statement, SQLCloseable, org.apache.pho
         this.lastQueryPlan = lastQueryPlan;
     }
     
-    private void throwIfUnallowedUserDefinedFunctions() throws SQLException {
+    private void throwIfUnallowedUserDefinedFunctions(Map<String, UDFParseNode> udfParseNodes) throws SQLException {
         if (!connection
                 .getQueryServices()
                 .getProps()
                 .getBoolean(QueryServices.ALLOW_USER_DEFINED_FUNCTIONS_ATTRIB,
                     QueryServicesOptions.DEFAULT_ALLOW_USER_DEFINED_FUNCTIONS)) {
-            throw new SQLExceptionInfo.Builder(SQLExceptionCode.UNALLOWED_USER_DEFINED_FUNCTIONS)
-                    .build().buildException();
+            if(udfParseNodes.isEmpty()) {
+                throw new SQLExceptionInfo.Builder(SQLExceptionCode.UNALLOWED_USER_DEFINED_FUNCTIONS)
+                .build().buildException();
+            }
+            throw new FunctionNotFoundException(udfParseNodes.keySet().toString());
         }
     }
 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/11bdb0ee/phoenix-core/src/main/java/org/apache/phoenix/parse/CreateFunctionStatement.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/CreateFunctionStatement.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/CreateFunctionStatement.java
index 741e4df..863783b 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/CreateFunctionStatement.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/CreateFunctionStatement.java
@@ -20,10 +20,12 @@ package org.apache.phoenix.parse;
 public class CreateFunctionStatement extends MutableStatement {
     private final PFunction functionInfo;
     private final boolean temporary;
+    private final boolean isReplace;
 
-    public CreateFunctionStatement(PFunction functionInfo, boolean temporary) {
+    public CreateFunctionStatement(PFunction functionInfo, boolean temporary, boolean isReplace) {
         this.functionInfo = functionInfo;
         this.temporary = temporary;
+        this.isReplace = isReplace;
     }
 
     @Override
@@ -39,4 +41,7 @@ public class CreateFunctionStatement extends MutableStatement {
         return temporary;
     }
 
+    public boolean isReplace() {
+        return isReplace;
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/11bdb0ee/phoenix-core/src/main/java/org/apache/phoenix/parse/PFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/PFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/PFunction.java
index a1413de..a5263ad 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/PFunction.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/PFunction.java
@@ -46,6 +46,7 @@ public class PFunction implements PMetaDataEntity {
     private long timeStamp;
     private int estimatedSize;
     private boolean temporary;
+    private boolean replace;
 
     public PFunction(long timeStamp) { // For index delete marker
         this.timeStamp = timeStamp;
@@ -71,11 +72,23 @@ public class PFunction implements PMetaDataEntity {
     public PFunction(PFunction function, boolean temporary) {
         this(function.getTenantId(), function.getFunctionName(), function.getFunctionArguments(),
                 function.getReturnType(), function.getClassName(), function.getJarPath(), function
-                        .getTimeStamp(), temporary);
+                        .getTimeStamp(), temporary, function.isReplace());
+    }
+
+    public PFunction(PFunction function, boolean temporary, boolean isReplace) {
+        this(function.getTenantId(), function.getFunctionName(), function.getFunctionArguments(),
+                function.getReturnType(), function.getClassName(), function.getJarPath(), function
+                        .getTimeStamp(), temporary, isReplace);
+    }
+
+    public PFunction(PName tenantId, String functionName, List<FunctionArgument> args,
+            String returnType, String className, String jarPath, long timeStamp, boolean temporary) {
+        this(tenantId, functionName, args, returnType, className, jarPath, timeStamp, temporary,
+                false);
     }
 
     public PFunction(PName tenantId, String functionName, List<FunctionArgument> args, String returnType,
-            String className, String jarPath, long timeStamp, boolean temporary) {
+            String className, String jarPath, long timeStamp, boolean temporary, boolean replace) {
         this.tenantId = tenantId;
         this.functionName = PNameFactory.newName(functionName);
         if (args == null){ 
@@ -94,6 +107,7 @@ public class PFunction implements PMetaDataEntity {
                 PNameFactory.getEstimatedSize(this.className) +
                  (jarPath==null?0:PNameFactory.getEstimatedSize(this.jarPath));
         this.temporary = temporary;
+        this.replace = replace;
     }
 
     public PFunction(PFunction function) {
@@ -217,6 +231,9 @@ public class PFunction implements PMetaDataEntity {
             }
             builder.addArguments(argBuilder.build());
         }
+        if(builder.hasIsReplace()) {
+            builder.setIsReplace(function.isReplace());
+        }
         return builder.build();
       }
 
@@ -248,11 +265,16 @@ public class PFunction implements PMetaDataEntity {
                     minValue == null ? null : LiteralExpression.newConstant((new LiteralParseNode(dataType.toObject(minValue))).getValue()),
                     maxValue == null ? null : LiteralExpression.newConstant((new LiteralParseNode(dataType.toObject(maxValue))).getValue())));
         }
-        return new PFunction(tenantId,functionName, args, returnType, className, jarPath, timeStamp);
+        return new PFunction(tenantId, functionName, args, returnType, className, jarPath,
+                timeStamp, false, function.hasIsReplace() ? true : false);
     }
 
     public int getEstimatedSize() {
         return estimatedSize;
     }
+
+    public boolean isReplace() {
+        return this.replace;
+    }
 }
 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/11bdb0ee/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
index 291c84c..44359a7 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
@@ -297,8 +297,8 @@ public class ParseNodeFactory {
                 maxValue, cycle, ifNotExits, bindCount);
     }
 
-    public CreateFunctionStatement createFunction(PFunction functionInfo, boolean temporary) {
-        return new CreateFunctionStatement(functionInfo, temporary);
+    public CreateFunctionStatement createFunction(PFunction functionInfo, boolean temporary, boolean isReplace) {
+        return new CreateFunctionStatement(functionInfo, temporary, isReplace);
     }
 
     public DropFunctionStatement dropFunction(String functionName, boolean ifExists) {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/11bdb0ee/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
index ddebf9f..69520b0 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
@@ -2709,6 +2709,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
                             builder.addTableMetadataMutations(mp.toByteString());
                         }
                         builder.setTemporary(temporary);
+                        builder.setReplace(function.isReplace());
                         instance.createFunction(controller, builder.build(), rpcCallback);
                         if(controller.getFailedOn() != null) {
                             throw controller.getFailedOn();

http://git-wip-us.apache.org/repos/asf/phoenix/blob/11bdb0ee/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
index d2d4338..9e74d2a 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
@@ -1348,7 +1348,7 @@ public class MetaDataClient {
         boolean wasAutoCommit = connection.getAutoCommit();
         connection.rollback();
         try {
-            PFunction function = new PFunction(stmt.getFunctionInfo(), stmt.isTemporary());
+            PFunction function = new PFunction(stmt.getFunctionInfo(), stmt.isTemporary(), stmt.isReplace());
             connection.setAutoCommit(false);
             String tenantIdStr = connection.getTenantId() == null ? null : connection.getTenantId().getString();
             List<Mutation> functionData = Lists.newArrayListWithExpectedSize(function.getFunctionArguments().size() + 1);
@@ -1377,16 +1377,26 @@ public class MetaDataClient {
             MutationCode code = result.getMutationCode();
             switch(code) {
             case FUNCTION_ALREADY_EXISTS:
-                throw new FunctionAlreadyExistsException(function.getFunctionName(), result
+                if (!function.isReplace()) {
+                    throw new FunctionAlreadyExistsException(function.getFunctionName(), result
                         .getFunctions().get(0));
+                } else {
+                    connection.removeFunction(function.getTenantId(), function.getFunctionName(),
+                        result.getMutationTime());
+                    addFunctionToCache(result);
+                }
             case NEWER_FUNCTION_FOUND:
-                // Add function to ConnectionQueryServices so it's cached, but don't add
-                // it to this connection as we can't see it.
-                throw new NewerFunctionAlreadyExistsException(function.getFunctionName(), result.getFunctions().get(0));
+                    // Add function to ConnectionQueryServices so it's cached, but don't add
+                    // it to this connection as we can't see it.
+                    throw new NewerFunctionAlreadyExistsException(function.getFunctionName(), result.getFunctions().get(0));
             default:
                 List<PFunction> functions = new ArrayList<PFunction>(1);
                 functions.add(function);
                 result = new MetaDataMutationResult(code, result.getMutationTime(), functions, true);
+                if(function.isReplace()) {
+                    connection.removeFunction(function.getTenantId(), function.getFunctionName(),
+                        result.getMutationTime());
+                }
                 addFunctionToCache(result);
             }
         } finally {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/11bdb0ee/phoenix-protocol/src/main/PFunction.proto
----------------------------------------------------------------------
diff --git a/phoenix-protocol/src/main/PFunction.proto b/phoenix-protocol/src/main/PFunction.proto
index 07cbac7..0b44d5c 100644
--- a/phoenix-protocol/src/main/PFunction.proto
+++ b/phoenix-protocol/src/main/PFunction.proto
@@ -42,4 +42,5 @@ message PFunction {
   optional bytes tenantId = 6;
   optional string returnType = 7;
   optional bool isArrayReturnType = 8;
+  optional bool isReplace = 9;
 }


[37/50] [abbrv] phoenix git commit: PHOENIX-2111 Race condition on creation of new view and adding of column to base table

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/phoenix/blob/9f09f1a5/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
index feb5989..52b038b 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
@@ -18,6 +18,9 @@
 package org.apache.phoenix.query;
 
 import static org.apache.hadoop.hbase.HColumnDescriptor.TTL;
+import static org.apache.phoenix.coprocessor.MetaDataProtocol.PHOENIX_MAJOR_VERSION;
+import static org.apache.phoenix.coprocessor.MetaDataProtocol.PHOENIX_MINOR_VERSION;
+import static org.apache.phoenix.coprocessor.MetaDataProtocol.PHOENIX_PATCH_NUMBER;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SYSTEM_CATALOG_NAME_BYTES;
 import static org.apache.phoenix.query.QueryServicesOptions.DEFAULT_DROP_METADATA;
 import static org.apache.phoenix.util.UpgradeUtil.upgradeTo4_5_0;
@@ -110,6 +113,7 @@ import org.apache.phoenix.hbase.index.Indexer;
 import org.apache.phoenix.hbase.index.covered.CoveredColumnsIndexBuilder;
 import org.apache.phoenix.hbase.index.util.ImmutableBytesPtr;
 import org.apache.phoenix.hbase.index.util.KeyValueBuilder;
+import org.apache.phoenix.hbase.index.util.VersionUtil;
 import org.apache.phoenix.index.PhoenixIndexBuilder;
 import org.apache.phoenix.index.PhoenixIndexCodec;
 import org.apache.phoenix.jdbc.PhoenixConnection;
@@ -966,7 +970,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
                             BlockingRpcCallback<GetVersionResponse> rpcCallback =
                                     new BlockingRpcCallback<GetVersionResponse>();
                             GetVersionRequest.Builder builder = GetVersionRequest.newBuilder();
-
+                            builder.setClientVersion(VersionUtil.encodeVersion(PHOENIX_MAJOR_VERSION, PHOENIX_MINOR_VERSION, PHOENIX_PATCH_NUMBER));
                             instance.getVersion(controller, builder.build(), rpcCallback);
                             if(controller.getFailedOn() != null) {
                                 throw controller.getFailedOn();
@@ -1265,6 +1269,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
                             MutationProto mp = ProtobufUtil.toProto(m);
                             builder.addTableMetadataMutations(mp.toByteString());
                         }
+                        builder.setClientVersion(VersionUtil.encodeVersion(PHOENIX_MAJOR_VERSION, PHOENIX_MINOR_VERSION, PHOENIX_PATCH_NUMBER));
                         instance.createTable(controller, builder.build(), rpcCallback);
                         if(controller.getFailedOn() != null) {
                             throw controller.getFailedOn();
@@ -1293,12 +1298,12 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
                     builder.setTableName(ByteStringer.wrap(tableBytes));
                     builder.setTableTimestamp(tableTimestamp);
                     builder.setClientTimestamp(clientTimestamp);
-
-                   instance.getTable(controller, builder.build(), rpcCallback);
-                   if(controller.getFailedOn() != null) {
-                       throw controller.getFailedOn();
-                   }
-                   return rpcCallback.get();
+                    builder.setClientVersion(VersionUtil.encodeVersion(PHOENIX_MAJOR_VERSION, PHOENIX_MINOR_VERSION, PHOENIX_PATCH_NUMBER));
+                    instance.getTable(controller, builder.build(), rpcCallback);
+                    if(controller.getFailedOn() != null) {
+                        throw controller.getFailedOn();
+                    }
+                    return rpcCallback.get();
                 }
             });
     }
@@ -1325,7 +1330,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
                         }
                         builder.setTableType(tableType.getSerializedValue());
                         builder.setCascade(cascade);
-
+                        builder.setClientVersion(VersionUtil.encodeVersion(PHOENIX_MAJOR_VERSION, PHOENIX_MINOR_VERSION, PHOENIX_PATCH_NUMBER));
                         instance.dropTable(controller, builder.build(), rpcCallback);
                         if(controller.getFailedOn() != null) {
                             throw controller.getFailedOn();
@@ -1379,6 +1384,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
                             builder.addTableMetadataMutations(mp.toByteString());
                         }
                         builder.setIfExists(ifExists);
+                        builder.setClientVersion(VersionUtil.encodeVersion(PHOENIX_MAJOR_VERSION, PHOENIX_MINOR_VERSION, PHOENIX_PATCH_NUMBER));
                         instance.dropFunction(controller, builder.build(), rpcCallback);
                         if(controller.getFailedOn() != null) {
                             throw controller.getFailedOn();
@@ -1553,7 +1559,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
                         MutationProto mp = ProtobufUtil.toProto(m);
                         builder.addTableMetadataMutations(mp.toByteString());
                     }
-
+                    builder.setClientVersion(VersionUtil.encodeVersion(PHOENIX_MAJOR_VERSION, PHOENIX_MINOR_VERSION, PHOENIX_PATCH_NUMBER));
                     instance.addColumn(controller, builder.build(), rpcCallback);
                     if(controller.getFailedOn() != null) {
                         throw controller.getFailedOn();
@@ -1804,6 +1810,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
                         MutationProto mp = ProtobufUtil.toProto(m);
                         builder.addTableMetadataMutations(mp.toByteString());
                     }
+                    builder.setClientVersion(VersionUtil.encodeVersion(PHOENIX_MAJOR_VERSION, PHOENIX_MINOR_VERSION, PHOENIX_PATCH_NUMBER));
                     instance.dropColumn(controller, builder.build(), rpcCallback);
                     if(controller.getFailedOn() != null) {
                         throw controller.getFailedOn();
@@ -2125,6 +2132,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
                         BlockingRpcCallback<ClearCacheResponse> rpcCallback =
                                 new BlockingRpcCallback<ClearCacheResponse>();
                         ClearCacheRequest.Builder builder = ClearCacheRequest.newBuilder();
+                        builder.setClientVersion(VersionUtil.encodeVersion(PHOENIX_MAJOR_VERSION, PHOENIX_MINOR_VERSION, PHOENIX_PATCH_NUMBER));
                         instance.clearCache(controller, builder.build(), rpcCallback);
                         if(controller.getFailedOn() != null) {
                             throw controller.getFailedOn();
@@ -2189,23 +2197,24 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
         byte[] tableKey = SchemaUtil.getTableKey(ByteUtil.EMPTY_BYTE_ARRAY, rowKeyMetadata[PhoenixDatabaseMetaData.SCHEMA_NAME_INDEX], rowKeyMetadata[PhoenixDatabaseMetaData.TABLE_NAME_INDEX]);
         return metaDataCoprocessorExec(tableKey,
                 new Batch.Call<MetaDataService, MetaDataResponse>() {
-                    @Override
-                    public MetaDataResponse call(MetaDataService instance) throws IOException {
-                        ServerRpcController controller = new ServerRpcController();
-                        BlockingRpcCallback<MetaDataResponse> rpcCallback =
-                                new BlockingRpcCallback<MetaDataResponse>();
-                        UpdateIndexStateRequest.Builder builder = UpdateIndexStateRequest.newBuilder();
-                        for (Mutation m : tableMetaData) {
-                            MutationProto mp = ProtobufUtil.toProto(m);
-                            builder.addTableMetadataMutations(mp.toByteString());
-                        }
-                        instance.updateIndexState(controller, builder.build(), rpcCallback);
-                        if(controller.getFailedOn() != null) {
-                            throw controller.getFailedOn();
-                        }
-                        return rpcCallback.get();
-                    }
-                });
+            @Override
+            public MetaDataResponse call(MetaDataService instance) throws IOException {
+                ServerRpcController controller = new ServerRpcController();
+                BlockingRpcCallback<MetaDataResponse> rpcCallback =
+                        new BlockingRpcCallback<MetaDataResponse>();
+                UpdateIndexStateRequest.Builder builder = UpdateIndexStateRequest.newBuilder();
+                for (Mutation m : tableMetaData) {
+                    MutationProto mp = ProtobufUtil.toProto(m);
+                    builder.addTableMetadataMutations(mp.toByteString());
+                }
+                builder.setClientVersion(VersionUtil.encodeVersion(PHOENIX_MAJOR_VERSION, PHOENIX_MINOR_VERSION, PHOENIX_PATCH_NUMBER));
+                instance.updateIndexState(controller, builder.build(), rpcCallback);
+                if(controller.getFailedOn() != null) {
+                    throw controller.getFailedOn();
+                }
+                return rpcCallback.get();
+            }
+        });
     }
 
     @Override
@@ -2408,6 +2417,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
                                 builder.setTableName(ByteStringer.wrap(tableName));
                                 builder.setSchemaName(ByteStringer.wrap(schemaName));
                                 builder.setClientTimestamp(clientTS);
+                                builder.setClientVersion(VersionUtil.encodeVersion(PHOENIX_MAJOR_VERSION, PHOENIX_MINOR_VERSION, PHOENIX_PATCH_NUMBER));
                                 instance.clearTableFromCache(controller, builder.build(), rpcCallback);
                                 if (controller.getFailedOn() != null) { throw controller.getFailedOn(); }
                                 return rpcCallback.get();
@@ -2696,12 +2706,12 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
                         builder.addFunctionTimestamps(function.getSecond().longValue());
                     }
                     builder.setClientTimestamp(clientTimestamp);
-
-                   instance.getFunctions(controller, builder.build(), rpcCallback);
-                   if(controller.getFailedOn() != null) {
-                       throw controller.getFailedOn();
-                   }
-                   return rpcCallback.get();
+                    builder.setClientVersion(VersionUtil.encodeVersion(PHOENIX_MAJOR_VERSION, PHOENIX_MINOR_VERSION, PHOENIX_PATCH_NUMBER));
+                    instance.getFunctions(controller, builder.build(), rpcCallback);
+                    if(controller.getFailedOn() != null) {
+                        throw controller.getFailedOn();
+                    }
+                    return rpcCallback.get();
                 }
             }, PhoenixDatabaseMetaData.SYSTEM_FUNCTION_NAME_BYTES);
 
@@ -2732,6 +2742,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
                         }
                         builder.setTemporary(temporary);
                         builder.setReplace(function.isReplace());
+                        builder.setClientVersion(VersionUtil.encodeVersion(PHOENIX_MAJOR_VERSION, PHOENIX_MINOR_VERSION, PHOENIX_PATCH_NUMBER));
                         instance.createFunction(controller, builder.build(), rpcCallback);
                         if(controller.getFailedOn() != null) {
                             throw controller.getFailedOn();

http://git-wip-us.apache.org/repos/asf/phoenix/blob/9f09f1a5/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
index b1fcf30..f74133a 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
@@ -234,8 +234,9 @@ public class MetaDataClient {
             TABLE_SCHEM + "," +
             TABLE_NAME + "," +
             COLUMN_FAMILY + "," +
-            LINK_TYPE +
-            ") VALUES (?, ?, ?, ?, ?)";
+            LINK_TYPE + "," +
+            TABLE_SEQ_NUM + // this is actually set to the parent table's sequence number
+            ") VALUES (?, ?, ?, ?, ?, ?)";
     private static final String CREATE_VIEW_LINK =
             "UPSERT INTO " + SYSTEM_CATALOG_SCHEMA + ".\"" + SYSTEM_CATALOG_TABLE + "\"( " +
             TENANT_ID + "," +
@@ -1490,6 +1491,7 @@ public class MetaDataClient {
                 linkStatement.setString(3, parentTableName);
                 linkStatement.setString(4, tableName);
                 linkStatement.setByte(5, LinkType.INDEX_TABLE.getSerializedValue());
+                linkStatement.setLong(6, parent.getSequenceNumber());
                 linkStatement.execute();
             }
 
@@ -1649,6 +1651,12 @@ public class MetaDataClient {
                         linkStatement.setString(3, tableName);
                         linkStatement.setString(4, physicalName.getString());
                         linkStatement.setByte(5, LinkType.PHYSICAL_TABLE.getSerializedValue());
+                        if (tableType == PTableType.VIEW) {
+                            PTable physicalTable = connection.getMetaDataCache().getTable(new PTableKey(null, physicalName.getString()));
+                            linkStatement.setLong(6, physicalTable.getSequenceNumber());
+                        } else {
+                            linkStatement.setLong(6, parent.getSequenceNumber());
+                        }
                         linkStatement.execute();
                     }
                 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/9f09f1a5/phoenix-core/src/main/java/org/apache/phoenix/util/MetaDataUtil.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/MetaDataUtil.java b/phoenix-core/src/main/java/org/apache/phoenix/util/MetaDataUtil.java
index fbc15be..ee73a58 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/util/MetaDataUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/util/MetaDataUtil.java
@@ -51,6 +51,7 @@ import org.apache.phoenix.jdbc.PhoenixDatabaseMetaData;
 import org.apache.phoenix.query.QueryConstants;
 import org.apache.phoenix.schema.PName;
 import org.apache.phoenix.schema.PTable;
+import org.apache.phoenix.schema.PTable.LinkType;
 import org.apache.phoenix.schema.PTableType;
 import org.apache.phoenix.schema.SequenceKey;
 import org.apache.phoenix.schema.SortOrder;
@@ -59,6 +60,7 @@ import org.apache.phoenix.schema.types.PBoolean;
 import org.apache.phoenix.schema.types.PDataType;
 import org.apache.phoenix.schema.types.PLong;
 import org.apache.phoenix.schema.types.PSmallint;
+import org.apache.phoenix.schema.types.PUnsignedTinyint;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -437,4 +439,16 @@ public class MetaDataUtil {
         scan.setStopRow(stopKey);
         return scan;
     }
+    
+    public static LinkType getLinkType(Mutation tableMutation) {
+        List<Cell> kvs = tableMutation.getFamilyCellMap().get(PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES);
+        if (kvs != null) {
+            for (Cell kv : kvs) {
+                if (Bytes.compareTo(kv.getQualifierArray(), kv.getQualifierOffset(), kv.getQualifierLength(), PhoenixDatabaseMetaData.LINK_TYPE_BYTES, 0, PhoenixDatabaseMetaData.LINK_TYPE_BYTES.length) == 0) {
+                    return LinkType.fromSerializedValue(PUnsignedTinyint.INSTANCE.getCodec().decodeByte(kv.getValueArray(), kv.getValueOffset(), SortOrder.getDefault()));
+                }
+            }
+        }
+        return null;
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/9f09f1a5/phoenix-core/src/main/java/org/apache/phoenix/util/PhoenixRuntime.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/PhoenixRuntime.java b/phoenix-core/src/main/java/org/apache/phoenix/util/PhoenixRuntime.java
index 61642bc..1a2019d 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/util/PhoenixRuntime.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/util/PhoenixRuntime.java
@@ -83,8 +83,6 @@ import org.apache.phoenix.schema.RowKeyValueAccessor;
 import org.apache.phoenix.schema.TableNotFoundException;
 import org.apache.phoenix.schema.ValueBitSet;
 import org.apache.phoenix.schema.types.PDataType;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Joiner;
 import com.google.common.base.Splitter;
@@ -99,8 +97,6 @@ import com.google.common.collect.Lists;
  * @since 0.1
  */
 public class PhoenixRuntime {
-    private static final Logger logger = LoggerFactory.getLogger(PhoenixRuntime.class);
-
     /**
      * Use this connection property to control HBase timestamps
      * by specifying your own long timestamp value at connection time. All

http://git-wip-us.apache.org/repos/asf/phoenix/blob/9f09f1a5/phoenix-core/src/main/java/org/apache/phoenix/util/UpgradeUtil.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/UpgradeUtil.java b/phoenix-core/src/main/java/org/apache/phoenix/util/UpgradeUtil.java
index 0ad6b9d..e1e2515 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/util/UpgradeUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/util/UpgradeUtil.java
@@ -466,8 +466,8 @@ public class UpgradeUtil {
     public static void upgradeTo4_5_0(PhoenixConnection oldMetaConnection) throws SQLException {
         PhoenixConnection metaConnection = null;
         try {
-            // Need to use own connection without any SCN to be able to read all data from SYSTEM.CATALOG 
-            metaConnection = new PhoenixConnection(oldMetaConnection);
+            // Need to use own connection with max time stamp to be able to read all data from SYSTEM.CATALOG 
+            metaConnection = new PhoenixConnection(oldMetaConnection, HConstants.LATEST_TIMESTAMP);
             logger.info("Upgrading metadata to support adding columns to tables with views");
             String getBaseTableAndViews = "SELECT "
                     + COLUMN_FAMILY + " AS BASE_PHYSICAL_TABLE, "

http://git-wip-us.apache.org/repos/asf/phoenix/blob/9f09f1a5/phoenix-protocol/src/main/MetaDataService.proto
----------------------------------------------------------------------
diff --git a/phoenix-protocol/src/main/MetaDataService.proto b/phoenix-protocol/src/main/MetaDataService.proto
index e79f846..c265158 100644
--- a/phoenix-protocol/src/main/MetaDataService.proto
+++ b/phoenix-protocol/src/main/MetaDataService.proto
@@ -61,6 +61,7 @@ message GetTableRequest {
   required bytes tableName = 3;
   required int64 tableTimestamp = 4;
   required int64 clientTimestamp = 5;
+  optional int32 clientVersion = 6;
 }
 
 message GetFunctionsRequest {
@@ -68,11 +69,13 @@ message GetFunctionsRequest {
   repeated bytes functionNames = 2;
   repeated int64 functionTimestamps = 3;
   required int64 clientTimestamp = 4;
+  optional int32 clientVersion = 5;
 }
 
 // each byte array represents a MutationProto instance
 message CreateTableRequest {
-  repeated bytes tableMetadataMutations = 1; 
+  repeated bytes tableMetadataMutations = 1;
+  optional int32 clientVersion = 2;
 }
 
 // each byte array represents a MutationProto instance
@@ -80,38 +83,46 @@ message CreateFunctionRequest {
   repeated bytes tableMetadataMutations = 1;
   required bool temporary = 2;
   optional bool replace = 3;
+  optional int32 clientVersion = 4;
 }
 
 message DropTableRequest {
   repeated bytes tableMetadataMutations = 1;
   required string tableType = 2;
   optional bool cascade = 3;
+  optional int32 clientVersion = 4;
 }
 
 message AddColumnRequest {
   repeated bytes tableMetadataMutations = 1;
+  optional int32 clientVersion = 2;
 }
 
 message DropColumnRequest {
   repeated bytes tableMetadataMutations = 1;
+  optional int32 clientVersion = 2;
 }
 
 message DropFunctionRequest {
   repeated bytes tableMetadataMutations = 1;
   optional bool ifExists = 2;
+  optional int32 clientVersion = 3;
 }
 
 message UpdateIndexStateRequest {
   repeated bytes tableMetadataMutations = 1;
+  optional int32 clientVersion = 2;
 }
 
 message ClearCacheRequest {
+  optional int32 clientVersion = 1;
 }
 
 message ClearCacheResponse {
 }
 
 message GetVersionRequest {
+  optional int32 clientVersion = 1;
 }
 
 message GetVersionResponse {
@@ -123,6 +134,7 @@ message ClearTableFromCacheRequest {
   required bytes schemaName  = 2;
   required bytes tableName = 3;
   required int64 clientTimestamp = 4;
+  optional int32 clientVersion = 5;
 }
 
 message ClearTableFromCacheResponse {


[06/50] [abbrv] phoenix git commit: PHOENIX-2036 PhoenixConfigurationUtil should provide a pre-normalize table name to PhoenixRuntime

Posted by ma...@apache.org.
PHOENIX-2036 PhoenixConfigurationUtil should provide a pre-normalize table name to PhoenixRuntime

Update phoenix-spark to follow the same normalization requirement.


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/39c982f9
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/39c982f9
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/39c982f9

Branch: refs/heads/calcite
Commit: 39c982f923033b97c477464d0c4e27221421774d
Parents: 1e606d5
Author: Josh Mahonin <jm...@gmail.com>
Authored: Mon Jul 6 19:39:31 2015 -0400
Committer: Josh Mahonin <jm...@apache.org>
Committed: Mon Jul 6 19:41:38 2015 -0400

----------------------------------------------------------------------
 phoenix-spark/src/it/resources/setup.sql        |  4 +-
 .../apache/phoenix/spark/PhoenixSparkIT.scala   | 58 ++++++++++++--------
 .../org/apache/phoenix/spark/PhoenixRDD.scala   | 24 +++-----
 3 files changed, 46 insertions(+), 40 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/39c982f9/phoenix-spark/src/it/resources/setup.sql
----------------------------------------------------------------------
diff --git a/phoenix-spark/src/it/resources/setup.sql b/phoenix-spark/src/it/resources/setup.sql
index 40157a2..154a996 100644
--- a/phoenix-spark/src/it/resources/setup.sql
+++ b/phoenix-spark/src/it/resources/setup.sql
@@ -32,4 +32,6 @@ CREATE TABLE ARRAY_TEST_TABLE (ID BIGINT NOT NULL PRIMARY KEY, VCARRAY VARCHAR[]
 UPSERT INTO ARRAY_TEST_TABLE (ID, VCARRAY) VALUES (1, ARRAY['String1', 'String2', 'String3'])
 CREATE TABLE DATE_PREDICATE_TEST_TABLE (ID BIGINT NOT NULL, TIMESERIES_KEY TIMESTAMP NOT NULL CONSTRAINT pk PRIMARY KEY (ID, TIMESERIES_KEY))
 UPSERT INTO DATE_PREDICATE_TEST_TABLE (ID, TIMESERIES_KEY) VALUES (1, CAST(CURRENT_TIME() AS TIMESTAMP))
-CREATE TABLE OUTPUT_TEST_TABLE (id BIGINT NOT NULL PRIMARY KEY, col1 VARCHAR, col2 INTEGER, col3 DATE)
\ No newline at end of file
+CREATE TABLE OUTPUT_TEST_TABLE (id BIGINT NOT NULL PRIMARY KEY, col1 VARCHAR, col2 INTEGER, col3 DATE)
+CREATE TABLE CUSTOM_ENTITY."z02"(id BIGINT NOT NULL PRIMARY KEY)
+UPSERT INTO CUSTOM_ENTITY."z02" (id) VALUES(1)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/phoenix/blob/39c982f9/phoenix-spark/src/it/scala/org/apache/phoenix/spark/PhoenixSparkIT.scala
----------------------------------------------------------------------
diff --git a/phoenix-spark/src/it/scala/org/apache/phoenix/spark/PhoenixSparkIT.scala b/phoenix-spark/src/it/scala/org/apache/phoenix/spark/PhoenixSparkIT.scala
index 5f256e6..e1c9df4 100644
--- a/phoenix-spark/src/it/scala/org/apache/phoenix/spark/PhoenixSparkIT.scala
+++ b/phoenix-spark/src/it/scala/org/apache/phoenix/spark/PhoenixSparkIT.scala
@@ -20,9 +20,9 @@ import org.apache.hadoop.conf.Configuration
 import org.apache.hadoop.hbase.{HBaseConfiguration, HConstants, HBaseTestingUtility}
 import org.apache.phoenix.end2end.BaseHBaseManagedTimeIT
 import org.apache.phoenix.query.BaseTest
-import org.apache.phoenix.schema.ColumnNotFoundException
+import org.apache.phoenix.schema.{TableNotFoundException, ColumnNotFoundException}
 import org.apache.phoenix.schema.types.PVarchar
-import org.apache.phoenix.util.ColumnInfo
+import org.apache.phoenix.util.{SchemaUtil, ColumnInfo}
 import org.apache.spark.sql.{SaveMode, execution, SQLContext}
 import org.apache.spark.sql.types.{LongType, DataType, StringType, StructField}
 import org.apache.spark.{SparkConf, SparkContext}
@@ -96,23 +96,6 @@ class PhoenixSparkIT extends FunSuite with Matchers with BeforeAndAfterAll {
     PhoenixSparkITHelper.doTeardown
   }
 
-  def buildSql(table: String, columns: Seq[String], predicate: Option[String]): String = {
-    val query = "SELECT %s FROM \"%s\"" format(columns.map(f => "\"" + f + "\"").mkString(", "), table)
-
-    query + (predicate match {
-      case Some(p: String) => " WHERE " + p
-      case _ => ""
-    })
-  }
-
-  test("Can create valid SQL") {
-    val rdd = new PhoenixRDD(sc, "MyTable", Array("Foo", "Bar"),
-      conf = hbaseConfiguration)
-
-    rdd.buildSql("MyTable", Array("Foo", "Bar"), None) should
-      equal("SELECT \"Foo\", \"Bar\" FROM \"MyTable\"")
-  }
-
   test("Can convert Phoenix schema") {
     val phoenixSchema = List(
       new ColumnInfo("varcharColumn", PVarchar.INSTANCE.getSqlType)
@@ -154,7 +137,9 @@ class PhoenixSparkIT extends FunSuite with Matchers with BeforeAndAfterAll {
     val sqlContext = new SQLContext(sc)
 
 
-    val df1 = sqlContext.phoenixTableAsDataFrame("table3", Array("id", "col1"),
+    val df1 = sqlContext.phoenixTableAsDataFrame(
+      SchemaUtil.getEscapedArgument("table3"),
+      Array("id", "col1"),
       zkUrl = Some(quorumAddress))
 
     df1.registerTempTable("table3")
@@ -191,10 +176,12 @@ class PhoenixSparkIT extends FunSuite with Matchers with BeforeAndAfterAll {
   }
 
   test("Using a predicate referring to a non-existent column should fail") {
-    intercept[RuntimeException] {
+    intercept[Exception] {
       val sqlContext = new SQLContext(sc)
 
-      val df1 = sqlContext.phoenixTableAsDataFrame("table3", Array("id", "col1"),
+      val df1 = sqlContext.phoenixTableAsDataFrame(
+        SchemaUtil.getEscapedArgument("table3"),
+        Array("id", "col1"),
         predicate = Some("foo = bar"),
         conf = hbaseConfiguration)
 
@@ -210,7 +197,9 @@ class PhoenixSparkIT extends FunSuite with Matchers with BeforeAndAfterAll {
   test("Can create schema RDD with predicate that will never match") {
     val sqlContext = new SQLContext(sc)
 
-    val df1 = sqlContext.phoenixTableAsDataFrame("table3", Array("id", "col1"),
+    val df1 = sqlContext.phoenixTableAsDataFrame(
+      SchemaUtil.getEscapedArgument("table3"),
+      Array("id", "col1"),
       predicate = Some("\"id\" = -1"),
       conf = hbaseConfiguration)
 
@@ -436,4 +425,27 @@ class PhoenixSparkIT extends FunSuite with Matchers with BeforeAndAfterAll {
     // Verify the arrays are equal
     sqlArray shouldEqual dataSet(0)._2
   }
+
+  test("Can read from table with schema and escaped table name") {
+    // Manually escape
+    val rdd1 = sc.phoenixTableAsRDD(
+      "CUSTOM_ENTITY.\"z02\"",
+      Seq("ID"),
+      conf = hbaseConfiguration)
+
+    var count = rdd1.count()
+
+    count shouldEqual 1L
+
+    // Use SchemaUtil
+    val rdd2 = sc.phoenixTableAsRDD(
+      SchemaUtil.getEscapedFullTableName("CUSTOM_ENTITY.z02"),
+      Seq("ID"),
+      conf = hbaseConfiguration)
+
+    count = rdd2.count()
+
+    count shouldEqual 1L
+
+  }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/phoenix/blob/39c982f9/phoenix-spark/src/main/scala/org/apache/phoenix/spark/PhoenixRDD.scala
----------------------------------------------------------------------
diff --git a/phoenix-spark/src/main/scala/org/apache/phoenix/spark/PhoenixRDD.scala b/phoenix-spark/src/main/scala/org/apache/phoenix/spark/PhoenixRDD.scala
index 773026d..427fb24 100644
--- a/phoenix-spark/src/main/scala/org/apache/phoenix/spark/PhoenixRDD.scala
+++ b/phoenix-spark/src/main/scala/org/apache/phoenix/spark/PhoenixRDD.scala
@@ -63,30 +63,22 @@ class PhoenixRDD(sc: SparkContext, table: String, columns: Seq[String],
     }
   }
 
-  def buildSql(table: String, columns: Seq[String], predicate: Option[String]): String = {
-    val query = "SELECT %s FROM \"%s\"".format(
-      if (columns.isEmpty) "*" else columns.map(f => "\"" + f + "\"").mkString(", "),
-      table
-    )
-
-    query + (predicate match {
-      case Some(p: String) if p.length > 0 => " WHERE " + p
-      case _ => ""
-    })
-  }
-
   def getPhoenixConfiguration: Configuration = {
 
     // This is just simply not serializable, so don't try, but clone it because
     // PhoenixConfigurationUtil mutates it.
     val config = HBaseConfiguration.create(conf)
 
-    PhoenixConfigurationUtil.setInputQuery(config, buildSql(table, columns, predicate))
+    PhoenixConfigurationUtil.setInputClass(config, classOf[PhoenixRecordWritable])
+    PhoenixConfigurationUtil.setInputTableName(config, table)
+
     if(!columns.isEmpty) {
-      PhoenixConfigurationUtil.setSelectColumnNames(config, columns.mkString(","))
+      PhoenixConfigurationUtil.setSelectColumnNames(config, columns.toArray)
+    }
+
+    if(predicate.isDefined) {
+      PhoenixConfigurationUtil.setInputTableConditions(config, predicate.get)
     }
-    PhoenixConfigurationUtil.setInputTableName(config, table)
-    PhoenixConfigurationUtil.setInputClass(config, classOf[PhoenixRecordWritable])
 
     // Override the Zookeeper URL if present. Throw exception if no address given.
     zkUrl match {


[46/50] [abbrv] phoenix git commit: PHOENIX-1890 Provide queries for adding/deleting jars to/from common place in hdfs which is used by dynamic class loader(Rajeshbabu)

Posted by ma...@apache.org.
PHOENIX-1890 Provide queries for adding/deleting jars to/from common place in hdfs which is used by dynamic class loader(Rajeshbabu)


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/f006df54
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/f006df54
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/f006df54

Branch: refs/heads/calcite
Commit: f006df5451859eb9d22130bb46b58460eee49674
Parents: 236ce1c
Author: Rajeshbabu Chintaguntla <ra...@apache.org>
Authored: Thu Jul 16 23:18:38 2015 +0530
Committer: Rajeshbabu Chintaguntla <ra...@apache.org>
Committed: Thu Jul 16 23:18:38 2015 +0530

----------------------------------------------------------------------
 .../phoenix/end2end/UserDefinedFunctionsIT.java |  67 ++++--
 phoenix-core/src/main/antlr3/PhoenixSQL.g       |  23 ++
 .../phoenix/compile/ListJarsQueryPlan.java      | 216 +++++++++++++++++++
 .../apache/phoenix/jdbc/PhoenixStatement.java   | 175 +++++++++++++++
 .../apache/phoenix/parse/AddJarsStatement.java  |  38 ++++
 .../phoenix/parse/DeleteJarStatement.java       |  19 ++
 .../apache/phoenix/parse/ListJarsStatement.java |  34 +++
 .../apache/phoenix/parse/ParseNodeFactory.java  |  12 ++
 8 files changed, 564 insertions(+), 20 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/f006df54/phoenix-core/src/it/java/org/apache/phoenix/end2end/UserDefinedFunctionsIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/UserDefinedFunctionsIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/UserDefinedFunctionsIT.java
index e2b7b4c..cd1e380 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/UserDefinedFunctionsIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/UserDefinedFunctionsIT.java
@@ -207,9 +207,6 @@ public class UserDefinedFunctionsIT extends BaseOwnClusterIT{
         conf.set(DYNAMIC_JARS_DIR_KEY, string+"/hbase/tmpjars");
         util.startMiniHBaseCluster(1, 1);
         UDFExpression.setConfig(conf);
-        compileTestClass(MY_REVERSE_CLASS_NAME, MY_REVERSE_PROGRAM, 1);
-        compileTestClass(MY_SUM_CLASS_NAME, MY_SUM_PROGRAM, 2);
-        compileTestClass(MY_ARRAY_INDEX_CLASS_NAME, MY_ARRAY_INDEX_PROGRAM, 3);
 
         String clientPort = util.getConfiguration().get(QueryServices.ZOOKEEPER_PORT_ATTRIB);
         url =
@@ -217,10 +214,54 @@ public class UserDefinedFunctionsIT extends BaseOwnClusterIT{
                         + clientPort + JDBC_PROTOCOL_TERMINATOR + PHOENIX_TEST_DRIVER_URL_PARAM;
         Map<String, String> props = Maps.newHashMapWithExpectedSize(1);
         props.put(QueryServices.ALLOW_USER_DEFINED_FUNCTIONS_ATTRIB, "true");
+        props.put(QueryServices.DYNAMIC_JARS_DIR_KEY,string+"/hbase/tmpjars/");
         driver = initAndRegisterDriver(url, new ReadOnlyProps(props.entrySet().iterator()));
+        compileTestClass(MY_REVERSE_CLASS_NAME, MY_REVERSE_PROGRAM, 1);
+        compileTestClass(MY_SUM_CLASS_NAME, MY_SUM_PROGRAM, 2);
+        compileTestClass(MY_ARRAY_INDEX_CLASS_NAME, MY_ARRAY_INDEX_PROGRAM, 3);
+        compileTestClass(MY_ARRAY_INDEX_CLASS_NAME, MY_ARRAY_INDEX_PROGRAM, 4);
     }
     
     @Test
+    public void testListJars() throws Exception {
+        Connection conn = driver.connect(url, EMPTY_PROPS);
+        Statement stmt = conn.createStatement();
+        ResultSet rs = stmt.executeQuery("list jars");
+        assertTrue(rs.next());
+        assertEquals(util.getConfiguration().get(QueryServices.DYNAMIC_JARS_DIR_KEY)+"/"+"myjar1.jar", rs.getString("jar_location"));
+        assertTrue(rs.next());
+        assertEquals(util.getConfiguration().get(QueryServices.DYNAMIC_JARS_DIR_KEY)+"/"+"myjar2.jar", rs.getString("jar_location"));
+        assertTrue(rs.next());
+        assertEquals(util.getConfiguration().get(QueryServices.DYNAMIC_JARS_DIR_KEY)+"/"+"myjar3.jar", rs.getString("jar_location"));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testDeleteJar() throws Exception {
+        Connection conn = driver.connect(url, EMPTY_PROPS);
+        Statement stmt = conn.createStatement();
+        ResultSet rs = stmt.executeQuery("list jars");
+        assertTrue(rs.next());
+        assertEquals(util.getConfiguration().get(QueryServices.DYNAMIC_JARS_DIR_KEY)+"/"+"myjar1.jar", rs.getString("jar_location"));
+        assertTrue(rs.next());
+        assertEquals(util.getConfiguration().get(QueryServices.DYNAMIC_JARS_DIR_KEY)+"/"+"myjar2.jar", rs.getString("jar_location"));
+        assertTrue(rs.next());
+        assertEquals(util.getConfiguration().get(QueryServices.DYNAMIC_JARS_DIR_KEY)+"/"+"myjar3.jar", rs.getString("jar_location"));
+        assertTrue(rs.next());
+        assertEquals(util.getConfiguration().get(QueryServices.DYNAMIC_JARS_DIR_KEY)+"/"+"myjar4.jar", rs.getString("jar_location"));
+        assertFalse(rs.next());
+        stmt.execute("delete jar '"+ util.getConfiguration().get(QueryServices.DYNAMIC_JARS_DIR_KEY)+"/"+"myjar4.jar'");
+        rs = stmt.executeQuery("list jars");
+        assertTrue(rs.next());
+        assertEquals(util.getConfiguration().get(QueryServices.DYNAMIC_JARS_DIR_KEY)+"/"+"myjar1.jar", rs.getString("jar_location"));
+        assertTrue(rs.next());
+        assertEquals(util.getConfiguration().get(QueryServices.DYNAMIC_JARS_DIR_KEY)+"/"+"myjar2.jar", rs.getString("jar_location"));
+        assertTrue(rs.next());
+        assertEquals(util.getConfiguration().get(QueryServices.DYNAMIC_JARS_DIR_KEY)+"/"+"myjar3.jar", rs.getString("jar_location"));
+        assertFalse(rs.next());
+    }
+
+    @Test
     public void testCreateFunction() throws Exception {
         Connection conn = driver.connect(url, EMPTY_PROPS);
         Statement stmt = conn.createStatement();
@@ -776,23 +817,9 @@ public class UserDefinedFunctionsIT extends BaseOwnClusterIT{
             jarFos.close();
             
             assertTrue(jarFile.exists());
-            
-            InputStream inputStream = new BufferedInputStream(new FileInputStream(jarPath));
-            FileSystem fs = util.getDefaultRootDirPath().getFileSystem(util.getConfiguration());
-            Path jarsLocation = new Path(util.getConfiguration().get(DYNAMIC_JARS_DIR_KEY));
-            Path myJarPath;
-            if (jarsLocation.toString().endsWith("/")) {
-                myJarPath = new Path(jarsLocation.toString() + jarName);
-            } else {
-                myJarPath = new Path(jarsLocation.toString() + "/" + jarName);
-            }
-            OutputStream outputStream = fs.create(myJarPath);
-            try {
-                IOUtils.copyBytes(inputStream, outputStream, 4096, false);
-            } finally {
-                IOUtils.closeStream(inputStream);
-                IOUtils.closeStream(outputStream);
-            }
+            Connection conn = driver.connect(url, EMPTY_PROPS);
+            Statement stmt = conn.createStatement();
+            stmt.execute("add jars '"+jarFile.getAbsolutePath()+"'");
         } finally {
             if (javaFile != null) javaFile.delete();
             if (classFile != null) classFile.delete();

http://git-wip-us.apache.org/repos/asf/phoenix/blob/f006df54/phoenix-core/src/main/antlr3/PhoenixSQL.g
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/antlr3/PhoenixSQL.g b/phoenix-core/src/main/antlr3/PhoenixSQL.g
index 10fda68..9fde48f 100644
--- a/phoenix-core/src/main/antlr3/PhoenixSQL.g
+++ b/phoenix-core/src/main/antlr3/PhoenixSQL.g
@@ -123,6 +123,8 @@ tokens
     DEFAULTVALUE='defaultvalue';
     CONSTANT = 'constant';
     REPLACE = 'replace';
+    LIST = 'list';
+    JARS='jars';
 }
 
 
@@ -182,6 +184,7 @@ import org.apache.phoenix.schema.types.PUnsignedTimestamp;
 import org.apache.phoenix.util.SchemaUtil;
 import org.apache.phoenix.parse.LikeParseNode.LikeType;
 import org.apache.phoenix.trace.util.Tracing;
+import org.apache.phoenix.parse.AddJarsStatement;
 }
 
 @lexer::header {
@@ -398,6 +401,9 @@ oneStatement returns [BindableStatement ret]
     |   s=trace_node
     |   s=create_function_node
     |   s=drop_function_node
+    |   s=add_jars_node
+    |   s=list_jars_node
+    |   s=delete_jar_node
     |   s=alter_session_node
     |	s=create_sequence_node
     |	s=drop_sequence_node
@@ -558,6 +564,18 @@ drop_function_node returns [DropFunctionStatement ret]
     : DROP FUNCTION (IF ex=EXISTS)? function=identifier {$ret = factory.dropFunction(SchemaUtil.normalizeIdentifier(function), ex!=null);}
     ;
 
+add_jars_node returns [AddJarsStatement ret]
+    : ADD JARS jarPaths = one_or_more_jarpaths { $ret = factory.addJars(jarPaths);}
+    ;
+
+list_jars_node returns [ListJarsStatement ret]
+    : LIST JARS { $ret = factory.listJars();}
+    ;
+
+delete_jar_node returns [DeleteJarStatement ret]
+    : DELETE JAR jarPath = jar_path { $ret = factory.deleteJar(jarPath);}
+    ;
+
 // Parse an alter session statement.
 alter_session_node returns [AlterSessionStatement ret]
     :   ALTER SESSION (SET p=properties)
@@ -914,6 +932,11 @@ one_or_more_expressions returns [List<ParseNode> ret]
     :  e = expression {$ret.add(e);}  (COMMA e = expression {$ret.add(e);} )*
 ;
 
+one_or_more_jarpaths returns [List<LiteralParseNode> ret]
+@init{ret = new ArrayList<LiteralParseNode>(); }
+    :  jarPath = jar_path {$ret.add(jarPath);}  (COMMA jarPath = jar_path {$ret.add(jarPath);} )*
+	;
+
 zero_or_more_expressions returns [List<ParseNode> ret]
 @init{ret = new ArrayList<ParseNode>(); }
     :  (v = expression {$ret.add(v);})?  (COMMA v = expression {$ret.add(v);} )*

http://git-wip-us.apache.org/repos/asf/phoenix/blob/f006df54/phoenix-core/src/main/java/org/apache/phoenix/compile/ListJarsQueryPlan.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/ListJarsQueryPlan.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/ListJarsQueryPlan.java
new file mode 100644
index 0000000..9fdf35b
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/ListJarsQueryPlan.java
@@ -0,0 +1,216 @@
+package org.apache.phoenix.compile;
+
+import java.io.IOException;
+import java.sql.ParameterMetaData;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.LocatedFileStatus;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.fs.RemoteIterator;
+import org.apache.hadoop.hbase.Cell;
+import org.apache.hadoop.hbase.CellUtil;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.KeyValue.Type;
+import org.apache.hadoop.hbase.client.Result;
+import org.apache.hadoop.hbase.client.Scan;
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.phoenix.compile.GroupByCompiler.GroupBy;
+import org.apache.phoenix.compile.OrderByCompiler.OrderBy;
+import org.apache.phoenix.expression.Determinism;
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.expression.LiteralExpression;
+import org.apache.phoenix.expression.RowKeyColumnExpression;
+import org.apache.phoenix.iterate.DefaultParallelScanGrouper;
+import org.apache.phoenix.iterate.ParallelScanGrouper;
+import org.apache.phoenix.iterate.ResultIterator;
+import org.apache.phoenix.jdbc.PhoenixParameterMetaData;
+import org.apache.phoenix.jdbc.PhoenixStatement;
+import org.apache.phoenix.parse.FilterableStatement;
+import org.apache.phoenix.parse.LiteralParseNode;
+import org.apache.phoenix.parse.ParseNodeFactory;
+import org.apache.phoenix.query.HBaseFactoryProvider;
+import org.apache.phoenix.query.KeyRange;
+import org.apache.phoenix.query.QueryServices;
+import org.apache.phoenix.schema.PColumn;
+import org.apache.phoenix.schema.PColumnImpl;
+import org.apache.phoenix.schema.PNameFactory;
+import org.apache.phoenix.schema.RowKeyValueAccessor;
+import org.apache.phoenix.schema.SortOrder;
+import org.apache.phoenix.schema.TableRef;
+import org.apache.phoenix.schema.tuple.ResultTuple;
+import org.apache.phoenix.schema.tuple.Tuple;
+import org.apache.phoenix.schema.types.PVarchar;
+import org.apache.phoenix.util.ByteUtil;
+import org.apache.phoenix.util.SizedUtil;
+
+public class ListJarsQueryPlan implements QueryPlan {
+
+    private PhoenixStatement stmt = null;
+    private StatementContext context = null;
+    private boolean first = true;
+
+    private static final RowProjector JARS_PROJECTOR;
+    
+    static {
+        List<ExpressionProjector> projectedColumns = new ArrayList<ExpressionProjector>();
+        PColumn column =
+                new PColumnImpl(PNameFactory.newName("jar_location"), null,
+                        PVarchar.INSTANCE, null, null, false, 0, SortOrder.getDefault(), 0, null,
+                        false, null);
+        List<PColumn> columns = new ArrayList<PColumn>();
+        columns.add(column);
+        Expression expression =
+                new RowKeyColumnExpression(column, new RowKeyValueAccessor(columns, 0));
+        projectedColumns.add(new ExpressionProjector("jar_location", "", expression,
+                true));
+        int estimatedByteSize = SizedUtil.KEY_VALUE_SIZE;
+        JARS_PROJECTOR = new RowProjector(projectedColumns, estimatedByteSize, false);
+    }
+
+    public ListJarsQueryPlan(PhoenixStatement stmt) {
+        this.stmt = stmt;
+        this.context = new StatementContext(stmt);
+    }
+
+    @Override
+    public StatementContext getContext() {
+        return this.context;
+    }
+
+    @Override
+    public ParameterMetaData getParameterMetaData() {
+        return PhoenixParameterMetaData.EMPTY_PARAMETER_META_DATA;
+    }
+
+    @Override
+    public ExplainPlan getExplainPlan() throws SQLException {
+        return ExplainPlan.EMPTY_PLAN;
+    }
+    
+    @Override
+    public ResultIterator iterator() throws SQLException {
+        return iterator(DefaultParallelScanGrouper.getInstance());
+    }
+
+    @Override
+    public ResultIterator iterator(ParallelScanGrouper scanGrouper) throws SQLException {
+        return new ResultIterator() {
+            private RemoteIterator<LocatedFileStatus> listFiles = null;
+
+            @Override
+            public void close() throws SQLException {
+                
+            }
+            
+            @Override
+            public Tuple next() throws SQLException {
+                try {
+                    if(first) {
+                        String dynamicJarsDir =
+                                stmt.getConnection().getQueryServices().getProps()
+                                        .get(QueryServices.DYNAMIC_JARS_DIR_KEY);
+                        if(dynamicJarsDir == null) {
+                            throw new SQLException(QueryServices.DYNAMIC_JARS_DIR_KEY
+                                    + " is not configured for the listing the jars.");
+                        }
+                        dynamicJarsDir =
+                                dynamicJarsDir.endsWith("/") ? dynamicJarsDir : dynamicJarsDir + '/';
+                        Configuration conf = HBaseFactoryProvider.getConfigurationFactory().getConfiguration();
+                        Path dynamicJarsDirPath = new Path(dynamicJarsDir);
+                        FileSystem fs = dynamicJarsDirPath.getFileSystem(conf);
+                        listFiles = fs.listFiles(dynamicJarsDirPath, true);
+                        first = false;
+                    }
+                    if(listFiles == null || !listFiles.hasNext()) return null;
+                    ImmutableBytesWritable ptr = new ImmutableBytesWritable();
+                    ParseNodeFactory factory = new ParseNodeFactory();
+                    LiteralParseNode literal =
+                            factory.literal(listFiles.next().getPath().toString());
+                    LiteralExpression expression =
+                            LiteralExpression.newConstant(literal.getValue(), PVarchar.INSTANCE,
+                                Determinism.ALWAYS);
+                    expression.evaluate(null, ptr);
+                    byte[] rowKey = ByteUtil.copyKeyBytesIfNecessary(ptr);
+                    Cell cell =
+                            CellUtil.createCell(rowKey, HConstants.EMPTY_BYTE_ARRAY,
+                                HConstants.EMPTY_BYTE_ARRAY, System.currentTimeMillis(),
+                                Type.Put.getCode(), HConstants.EMPTY_BYTE_ARRAY);
+                    List<Cell> cells = new ArrayList<Cell>(1);
+                    cells.add(cell);
+                    return new ResultTuple(Result.create(cells));
+                } catch (IOException e) {
+                    throw new SQLException(e);
+                }  
+            }
+            
+            @Override
+            public void explain(List<String> planSteps) {
+            }
+        };
+    }
+    
+    @Override
+    public long getEstimatedSize() {
+        return PVarchar.INSTANCE.getByteSize();
+    }
+
+    @Override
+    public TableRef getTableRef() {
+        return null;
+    }
+
+    @Override
+    public RowProjector getProjector() {
+        return JARS_PROJECTOR;
+    }
+
+    @Override
+    public Integer getLimit() {
+        return null;
+    }
+
+    @Override
+    public OrderBy getOrderBy() {
+        return OrderBy.EMPTY_ORDER_BY;
+    }
+
+    @Override
+    public GroupBy getGroupBy() {
+        return GroupBy.EMPTY_GROUP_BY;
+    }
+
+    @Override
+    public List<KeyRange> getSplits() {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public List<List<Scan>> getScans() {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public FilterableStatement getStatement() {
+        return null;
+    }
+
+    @Override
+    public boolean isDegenerate() {
+        return false;
+    }
+
+    @Override
+    public boolean isRowKeyOrdered() {
+        return false;
+    }
+    
+    @Override
+    public boolean useRoundRobinIterator() {
+        return false;
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/f006df54/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
index 9689589..056263a 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
@@ -21,6 +21,7 @@ import static org.apache.phoenix.monitoring.GlobalClientMetrics.GLOBAL_MUTATION_
 import static org.apache.phoenix.monitoring.GlobalClientMetrics.GLOBAL_QUERY_TIME;
 import static org.apache.phoenix.monitoring.GlobalClientMetrics.GLOBAL_SELECT_SQL_COUNTER;
 
+import java.io.File;
 import java.io.IOException;
 import java.io.Reader;
 import java.sql.ParameterMetaData;
@@ -36,6 +37,11 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.LocatedFileStatus;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.fs.RemoteIterator;
 import org.apache.hadoop.hbase.client.Consistency;
 import org.apache.hadoop.hbase.client.Scan;
 import org.apache.hadoop.hbase.util.Pair;
@@ -52,6 +58,7 @@ import org.apache.phoenix.compile.ExplainPlan;
 import org.apache.phoenix.compile.ExpressionProjector;
 import org.apache.phoenix.compile.FromCompiler;
 import org.apache.phoenix.compile.GroupByCompiler.GroupBy;
+import org.apache.phoenix.compile.ListJarsQueryPlan;
 import org.apache.phoenix.compile.MutationPlan;
 import org.apache.phoenix.compile.OrderByCompiler.OrderBy;
 import org.apache.phoenix.compile.QueryCompiler;
@@ -74,6 +81,7 @@ import org.apache.phoenix.iterate.MaterializedResultIterator;
 import org.apache.phoenix.iterate.ParallelScanGrouper;
 import org.apache.phoenix.iterate.ResultIterator;
 import org.apache.phoenix.parse.AddColumnStatement;
+import org.apache.phoenix.parse.AddJarsStatement;
 import org.apache.phoenix.parse.AliasedNode;
 import org.apache.phoenix.parse.AlterIndexStatement;
 import org.apache.phoenix.parse.AlterSessionStatement;
@@ -84,6 +92,7 @@ import org.apache.phoenix.parse.CreateFunctionStatement;
 import org.apache.phoenix.parse.CreateIndexStatement;
 import org.apache.phoenix.parse.CreateSequenceStatement;
 import org.apache.phoenix.parse.CreateTableStatement;
+import org.apache.phoenix.parse.DeleteJarStatement;
 import org.apache.phoenix.parse.DeleteStatement;
 import org.apache.phoenix.parse.DropColumnStatement;
 import org.apache.phoenix.parse.DropFunctionStatement;
@@ -95,6 +104,8 @@ import org.apache.phoenix.parse.FilterableStatement;
 import org.apache.phoenix.parse.HintNode;
 import org.apache.phoenix.parse.IndexKeyConstraint;
 import org.apache.phoenix.parse.LimitNode;
+import org.apache.phoenix.parse.ListJarsStatement;
+import org.apache.phoenix.parse.LiteralParseNode;
 import org.apache.phoenix.parse.NamedNode;
 import org.apache.phoenix.parse.NamedTableNode;
 import org.apache.phoenix.parse.OrderByNode;
@@ -110,6 +121,7 @@ import org.apache.phoenix.parse.TraceStatement;
 import org.apache.phoenix.parse.UDFParseNode;
 import org.apache.phoenix.parse.UpdateStatisticsStatement;
 import org.apache.phoenix.parse.UpsertStatement;
+import org.apache.phoenix.query.HBaseFactoryProvider;
 import org.apache.phoenix.query.KeyRange;
 import org.apache.phoenix.query.QueryConstants;
 import org.apache.phoenix.query.QueryServices;
@@ -631,6 +643,152 @@ public class PhoenixStatement implements Statement, SQLCloseable, org.apache.pho
         }
     }
     
+    private static class ExecutableAddJarsStatement extends AddJarsStatement implements CompilableStatement {
+
+        public ExecutableAddJarsStatement(List<LiteralParseNode> jarPaths) {
+            super(jarPaths);
+        }
+
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public MutationPlan compilePlan(final PhoenixStatement stmt, Sequence.ValueOp seqAction) throws SQLException {
+            final StatementContext context = new StatementContext(stmt);
+            return new MutationPlan() {
+
+                @Override
+                public StatementContext getContext() {
+                    return context;
+                }
+
+                @Override
+                public ParameterMetaData getParameterMetaData() {
+                    return PhoenixParameterMetaData.EMPTY_PARAMETER_META_DATA;
+                }
+
+                @Override
+                public ExplainPlan getExplainPlan() throws SQLException {
+                    return new ExplainPlan(Collections.singletonList("ADD JARS"));
+                }
+
+                @Override
+                public PhoenixConnection getConnection() {
+                    return stmt.getConnection();
+                }
+
+                @Override
+                public MutationState execute() throws SQLException {
+                    String dynamicJarsDir = stmt.getConnection().getQueryServices().getProps().get(QueryServices.DYNAMIC_JARS_DIR_KEY);
+                    if(dynamicJarsDir == null) {
+                        throw new SQLException(QueryServices.DYNAMIC_JARS_DIR_KEY+" is not configured for placing the jars.");
+                    }
+                    dynamicJarsDir =
+                            dynamicJarsDir.endsWith("/") ? dynamicJarsDir : dynamicJarsDir + '/';
+                    Configuration conf = HBaseFactoryProvider.getConfigurationFactory().getConfiguration();
+                    Path dynamicJarsDirPath = new Path(dynamicJarsDir);
+                    for (LiteralParseNode jarPath : getJarPaths()) {
+                        String jarPathStr = (String)jarPath.getValue();
+                        if(!jarPathStr.endsWith(".jar")) {
+                            throw new SQLException(jarPathStr + " is not a valid jar file path.");
+                        }
+                    }
+
+                    try {
+                        FileSystem fs = dynamicJarsDirPath.getFileSystem(conf);
+                        List<LiteralParseNode> jarPaths = getJarPaths();
+                        for (LiteralParseNode jarPath : jarPaths) {
+                            File f = new File((String) jarPath.getValue());
+                            fs.copyFromLocalFile(new Path(f.getAbsolutePath()), new Path(
+                                    dynamicJarsDir + f.getName()));
+                        }
+                    } catch(IOException e) {
+                        throw new SQLException(e);
+                    }
+                    return new MutationState(0, context.getConnection());
+                }
+            };
+            
+        }
+    }
+
+    private static class ExecutableDeleteJarStatement extends DeleteJarStatement implements CompilableStatement {
+
+        public ExecutableDeleteJarStatement(LiteralParseNode jarPath) {
+            super(jarPath);
+        }
+
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public MutationPlan compilePlan(final PhoenixStatement stmt, Sequence.ValueOp seqAction) throws SQLException {
+            final StatementContext context = new StatementContext(stmt);
+            return new MutationPlan() {
+
+                @Override
+                public StatementContext getContext() {
+                    return context;
+                }
+
+                @Override
+                public ParameterMetaData getParameterMetaData() {
+                    return PhoenixParameterMetaData.EMPTY_PARAMETER_META_DATA;
+                }
+
+                @Override
+                public ExplainPlan getExplainPlan() throws SQLException {
+                    return new ExplainPlan(Collections.singletonList("DELETE JAR"));
+                }
+
+                @Override
+                public PhoenixConnection getConnection() {
+                    return stmt.getConnection();
+                }
+
+                @Override
+                public MutationState execute() throws SQLException {
+                    String dynamicJarsDir = stmt.getConnection().getQueryServices().getProps().get(QueryServices.DYNAMIC_JARS_DIR_KEY);
+                    if (dynamicJarsDir == null) {
+                        throw new SQLException(QueryServices.DYNAMIC_JARS_DIR_KEY
+                                + " is not configured.");
+                    }
+                    dynamicJarsDir =
+                            dynamicJarsDir.endsWith("/") ? dynamicJarsDir : dynamicJarsDir + '/';
+                    Configuration conf = HBaseFactoryProvider.getConfigurationFactory().getConfiguration();
+                    Path dynamicJarsDirPath = new Path(dynamicJarsDir);
+                    try {
+                        FileSystem fs = dynamicJarsDirPath.getFileSystem(conf);
+                        String jarPathStr = (String)getJarPath().getValue();
+                        if(!jarPathStr.endsWith(".jar")) {
+                            throw new SQLException(jarPathStr + " is not a valid jar file path.");
+                        }
+                        Path p = new Path(jarPathStr);
+                        if(fs.exists(p)) {
+                            fs.delete(p, false);
+                        }
+                    } catch(IOException e) {
+                        throw new SQLException(e);
+                    }
+                    return new MutationState(0, context.getConnection());
+                }
+            };
+            
+        }
+    }
+
+    private static class ExecutableListJarsStatement extends ListJarsStatement implements CompilableStatement {
+
+        public ExecutableListJarsStatement() {
+            super();
+        }
+
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public QueryPlan compilePlan(final PhoenixStatement stmt, Sequence.ValueOp seqAction) throws SQLException {
+            return new ListJarsQueryPlan(stmt);
+        }
+    }
+
     private static class ExecutableCreateIndexStatement extends CreateIndexStatement implements CompilableStatement {
 
         public ExecutableCreateIndexStatement(NamedNode indexName, NamedTableNode dataTable, IndexKeyConstraint ikConstraint, List<ColumnName> includeColumns, List<ParseNode> splits,
@@ -1026,6 +1184,23 @@ public class PhoenixStatement implements Statement, SQLCloseable, org.apache.pho
         public CreateFunctionStatement createFunction(PFunction functionInfo, boolean temporary, boolean isReplace) {
             return new ExecutableCreateFunctionStatement(functionInfo, temporary, isReplace);
         }
+
+        @Override
+        public AddJarsStatement addJars(List<LiteralParseNode> jarPaths) {
+            return new ExecutableAddJarsStatement(jarPaths);
+        }
+
+        @Override
+        public DeleteJarStatement deleteJar(LiteralParseNode jarPath)  {
+            return new ExecutableDeleteJarStatement(jarPath);
+        }
+
+        @Override
+        public ListJarsStatement listJars() {
+            return new ExecutableListJarsStatement();
+        }
+
+
         @Override
         public DropSequenceStatement dropSequence(TableName tableName, boolean ifExists, int bindCount){
             return new ExecutableDropSequenceStatement(tableName, ifExists, bindCount);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/f006df54/phoenix-core/src/main/java/org/apache/phoenix/parse/AddJarsStatement.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/AddJarsStatement.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/AddJarsStatement.java
new file mode 100644
index 0000000..b1eeea6
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/AddJarsStatement.java
@@ -0,0 +1,38 @@
+/*
+ * 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.parse;
+
+import java.util.List;
+
+public class AddJarsStatement extends MutableStatement {
+    
+    List<LiteralParseNode> jarPaths;
+
+    public AddJarsStatement(List<LiteralParseNode> jarPaths) {
+        this.jarPaths = jarPaths;
+    }
+
+    @Override
+    public int getBindCount() {
+        return 0;
+    }
+
+    public List<LiteralParseNode> getJarPaths() {
+        return jarPaths;
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/f006df54/phoenix-core/src/main/java/org/apache/phoenix/parse/DeleteJarStatement.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/DeleteJarStatement.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/DeleteJarStatement.java
new file mode 100644
index 0000000..7ccdc72
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/DeleteJarStatement.java
@@ -0,0 +1,19 @@
+package org.apache.phoenix.parse;
+
+public class DeleteJarStatement extends MutableStatement {
+
+    private LiteralParseNode jarPath;
+
+    public DeleteJarStatement(LiteralParseNode jarPath) {
+        this.jarPath = jarPath;
+    }
+
+    @Override
+    public int getBindCount() {
+        return 0;
+    }
+
+    public LiteralParseNode getJarPath() {
+        return jarPath;
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/f006df54/phoenix-core/src/main/java/org/apache/phoenix/parse/ListJarsStatement.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/ListJarsStatement.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/ListJarsStatement.java
new file mode 100644
index 0000000..e9821fb
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/ListJarsStatement.java
@@ -0,0 +1,34 @@
+/*
+ * 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.parse;
+
+import org.apache.phoenix.jdbc.PhoenixStatement.Operation;
+
+public class ListJarsStatement implements BindableStatement {
+
+    @Override
+    public int getBindCount() {
+        return 0;
+    }
+
+    @Override
+    public Operation getOperation() {
+        return Operation.QUERY;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/f006df54/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
index 49b14c6..cd239ac 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
@@ -301,6 +301,18 @@ public class ParseNodeFactory {
         return new CreateFunctionStatement(functionInfo, temporary, isReplace);
     }
 
+    public AddJarsStatement addJars(List<LiteralParseNode> jarPaths) {
+        return new AddJarsStatement(jarPaths);
+    }
+
+    public ListJarsStatement listJars() {
+        return new ListJarsStatement();
+    }
+
+    public DeleteJarStatement deleteJar(LiteralParseNode jarPath) {
+        return new DeleteJarStatement(jarPath);
+    }
+
     public DropFunctionStatement dropFunction(String functionName, boolean ifExists) {
         return new DropFunctionStatement(functionName, ifExists);
     }


[21/50] [abbrv] phoenix git commit: PHOENIX-2099 Backward Compatibility - Concurrent modification error on connect

Posted by ma...@apache.org.
PHOENIX-2099 Backward Compatibility - Concurrent modification error on connect


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/39afa9f1
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/39afa9f1
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/39afa9f1

Branch: refs/heads/calcite
Commit: 39afa9f18cad132a8a5c49b0e2e31671c85a3c7e
Parents: 997de5e
Author: Samarth <sa...@salesforce.com>
Authored: Fri Jul 10 11:04:44 2015 -0700
Committer: Samarth <sa...@salesforce.com>
Committed: Fri Jul 10 11:04:44 2015 -0700

----------------------------------------------------------------------
 .../coprocessor/MetaDataEndpointImpl.java       |  24 +-
 .../query/ConnectionQueryServicesImpl.java      |   8 +-
 .../org/apache/phoenix/util/UpgradeUtil.java    | 416 ++++++++++---------
 phoenix-protocol/src/main/PTable.proto          |   1 +
 4 files changed, 237 insertions(+), 212 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/39afa9f1/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 f786768..399d56e 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
@@ -39,7 +39,6 @@ import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.IS_ARRAY_BYTES;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.IS_CONSTANT_BYTES;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.IS_VIEW_REFERENCED_BYTES;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.JAR_PATH_BYTES;
-import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.KEY_SEQ_BYTES;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.LINK_TYPE_BYTES;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.MAX_VALUE_BYTES;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.MIN_VALUE_BYTES;
@@ -1567,12 +1566,6 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
                     // 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); }
-                    if (table.getBaseColumnCount() == 0) {
-                        // If the base column count hasn't been set, then it means that the upgrade
-                        // to 4.5.0 is in progress. Have the client retry the mutation operation.
-                        return new MetaDataMutationResult(MutationCode.CONCURRENT_TABLE_MUTATION,
-                                EnvironmentEdgeManager.currentTimeMillis(), table);
-                    }
                 }
                 result = mutator.updateMutation(table, rowKeyMetaData, tableMetadata, region,
                             invalidateList, locks, clientTimeStamp);
@@ -1658,7 +1651,7 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
                     }
                     
                     // if there is already a view column with the same name as the base table column we are trying to add
-                	if (existingViewColumn!=null) {
+                	if (existingViewColumn != null) {
                 		List<Cell> dataTypes = viewColumnDefinitionPut
                                 .get(PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
                                         PhoenixDatabaseMetaData.DATA_TYPE_BYTES);
@@ -1684,7 +1677,7 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
 	                        }
                         }
                 		// if there is an existing view column that matches the column being added to the base table and if the column being added has a null
-                    	// scale or maxLength, we need to explicity do a put to set the scale or maxLength to null (in case the view column has the scale or 
+                    	// scale or maxLength, we need to explicitly do a put to set the scale or maxLength to null (in case the view column has the scale or 
                     	// max length set)
                 		List<Cell> columnSizes = viewColumnDefinitionPut.get(
                                 PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
@@ -1714,7 +1707,7 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
                         }
                 	}
                 	else {
-                    	// if we are adding a column that already exists in the view, no need to updates the base table or view table column count
+                    	// if we are adding a column that already exists in the view, no need to update the base table or view table column count
                     	numColsAddedToView++;
                     } 
                     	
@@ -1914,9 +1907,14 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
                     if (type == PTableType.TABLE || type == PTableType.SYSTEM) {
                         TableViewFinderResult childViewsResult = findChildViews(region, tenantId, table, PHYSICAL_TABLE_BYTES);
                         if (childViewsResult.hasViews()) {
-                            // Adding a column is not allowed if the meta-data for child view/s spans over
-                            // more than one region (since the changes cannot be done in a transactional fashion)
-                            if (!childViewsResult.allViewsInSingleRegion()) {
+                           /* 
+                            * Adding a column is not allowed if the meta-data for child view/s spans over
+                            * more than one region (since the changes cannot be made in a transactional fashion)
+                            * A base column count of 0 means that the metadata hasn't been upgraded yet or
+                            * the upgrade is currently in progress. If that is the case, disallow adding columns
+                            *  for tables with views.
+                            */
+                            if (!childViewsResult.allViewsInSingleRegion() || table.getBaseColumnCount() == 0) {
                                 return new MetaDataMutationResult(MutationCode.UNALLOWED_TABLE_MUTATION,
                                         EnvironmentEdgeManager.currentTimeMillis(), null);
                             } else {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/39afa9f1/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
index a9f455a..9cd32e8 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
@@ -1826,7 +1826,10 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
         return result;
 
     }
-
+    
+    /**
+     * This closes the passed connection.
+     */
     private PhoenixConnection addColumn(PhoenixConnection oldMetaConnection, String tableName, long timestamp, String columns, boolean addIfNotExists) throws SQLException {
         Properties props = new Properties(oldMetaConnection.getClientInfo());
         props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(timestamp));
@@ -1956,7 +1959,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
                                     columnsToAdd = PhoenixDatabaseMetaData.BASE_COLUMN_COUNT + " "
                                             + PInteger.INSTANCE.getSqlTypeName();
                                     try {
-                                        addColumn(metaConnection, PhoenixDatabaseMetaData.SYSTEM_CATALOG,
+                                        metaConnection = addColumn(metaConnection, PhoenixDatabaseMetaData.SYSTEM_CATALOG,
                                                 MetaDataProtocol.MIN_SYSTEM_TABLE_TIMESTAMP, columnsToAdd, false);
                                         upgradeTo4_5_0(metaConnection);
                                     } catch (ColumnAlreadyExistsException ignored) {
@@ -1965,6 +1968,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
                                          * BASE_COLUMN_COUNT is already part of the meta-data schema as the signal that
                                          * the server side upgrade has finished or is in progress.
                                          */
+                                        logger.debug("No need to run 4.5 upgrade");
                                     }
                                 }
                             }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/39afa9f1/phoenix-core/src/main/java/org/apache/phoenix/util/UpgradeUtil.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/UpgradeUtil.java b/phoenix-core/src/main/java/org/apache/phoenix/util/UpgradeUtil.java
index 8d574ce..c829799 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/util/UpgradeUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/util/UpgradeUtil.java
@@ -443,216 +443,238 @@ public class UpgradeUtil {
                 buf, keyValue.getValueOffset(), keyValue.getValueLength());
     }
     
-    public static void upgradeTo4_5_0(PhoenixConnection metaConnection) throws SQLException {
-        String getBaseTableAndViews = "SELECT "
-                + COLUMN_FAMILY + " AS BASE_PHYSICAL_TABLE, "
-                + TENANT_ID + ", "
-                + TABLE_SCHEM + " AS VIEW_SCHEMA, "
-                + TABLE_NAME + " AS VIEW_NAME "
-                + "FROM " + SYSTEM_CATALOG_NAME 
-                + " WHERE " + COLUMN_FAMILY + " IS NOT NULL " // column_family column points to the physical table name.
-                + " AND " + COLUMN_NAME + " IS NULL "
-                + " AND " + LINK_TYPE + " = ? ";
-        // Build a map of base table name -> list of views on the table. 
-        Map<String, List<ViewKey>> parentTableViewsMap = new HashMap<>();
-        try (PreparedStatement stmt = metaConnection.prepareStatement(getBaseTableAndViews)) {
-            // Get back view rows that have links back to the base physical table. This takes care
-            // of cases when we have a hierarchy of views too.
-            stmt.setByte(1, LinkType.PHYSICAL_TABLE.getSerializedValue());
-            try (ResultSet rs = stmt.executeQuery()) {
-                while (rs.next()) {
-                    // this is actually SCHEMANAME.TABLENAME
-                    String parentTable = rs.getString("BASE_PHYSICAL_TABLE");
-                    String tenantId = rs.getString(TENANT_ID);
-                    String viewSchema = rs.getString("VIEW_SCHEMA");
-                    String viewName = rs.getString("VIEW_NAME");
-                    List<ViewKey> viewKeysList = parentTableViewsMap.get(parentTable);
-                    if (viewKeysList == null) {
-                        viewKeysList = new ArrayList<>();
-                        parentTableViewsMap.put(parentTable, viewKeysList);
+    /**
+     * Upgrade the metadata in the catalog table to enable adding columns to tables with views
+     * @param oldMetaConnection caller should take care of closing the passed connection appropriately
+     * @throws SQLException
+     */
+    public static void upgradeTo4_5_0(PhoenixConnection oldMetaConnection) throws SQLException {
+        PhoenixConnection metaConnection = null;
+        try {
+            // Need to use own connection without any SCN to be able to read all data from SYSTEM.CATALOG 
+            metaConnection = new PhoenixConnection(oldMetaConnection);
+            logger.info("Upgrading metadata to support adding columns to tables with views");
+            String getBaseTableAndViews = "SELECT "
+                    + COLUMN_FAMILY + " AS BASE_PHYSICAL_TABLE, "
+                    + TENANT_ID + ", "
+                    + TABLE_SCHEM + " AS VIEW_SCHEMA, "
+                    + TABLE_NAME + " AS VIEW_NAME "
+                    + "FROM " + SYSTEM_CATALOG_NAME 
+                    + " WHERE " + COLUMN_FAMILY + " IS NOT NULL " // column_family column points to the physical table name.
+                    + " AND " + COLUMN_NAME + " IS NULL "
+                    + " AND " + LINK_TYPE + " = ? ";
+            // Build a map of base table name -> list of views on the table. 
+            Map<String, List<ViewKey>> parentTableViewsMap = new HashMap<>();
+            try (PreparedStatement stmt = metaConnection.prepareStatement(getBaseTableAndViews)) {
+                // Get back view rows that have links back to the base physical table. This takes care
+                // of cases when we have a hierarchy of views too.
+                stmt.setByte(1, LinkType.PHYSICAL_TABLE.getSerializedValue());
+                try (ResultSet rs = stmt.executeQuery()) {
+                    while (rs.next()) {
+                        // this is actually SCHEMANAME.TABLENAME
+                        String parentTable = rs.getString("BASE_PHYSICAL_TABLE");
+                        String tenantId = rs.getString(TENANT_ID);
+                        String viewSchema = rs.getString("VIEW_SCHEMA");
+                        String viewName = rs.getString("VIEW_NAME");
+                        List<ViewKey> viewKeysList = parentTableViewsMap.get(parentTable);
+                        if (viewKeysList == null) {
+                            viewKeysList = new ArrayList<>();
+                            parentTableViewsMap.put(parentTable, viewKeysList);
+                        }
+                        viewKeysList.add(new ViewKey(tenantId, viewSchema, viewName));
                     }
-                    viewKeysList.add(new ViewKey(tenantId, viewSchema, viewName));
                 }
             }
-        }
-        
-        for (Entry<String, List<ViewKey>> entry : parentTableViewsMap.entrySet()) {
-            // Fetch column information for the base physical table
-            String physicalTable = entry.getKey();
-            String baseTableSchemaName = SchemaUtil.getSchemaNameFromFullName(physicalTable).equals(StringUtil.EMPTY_STRING) ? null : SchemaUtil.getSchemaNameFromFullName(physicalTable);
-            String baseTableName = SchemaUtil.getTableNameFromFullName(physicalTable);
-            List<ColumnDetails> basePhysicalTableColumns = new ArrayList<>(); 
-            
-            // Columns fetched in order of ordinal position
-            String fetchColumnInfoForBasePhysicalTable = "SELECT " +
-                    COLUMN_NAME + "," +
-                    COLUMN_FAMILY + "," +
-                    DATA_TYPE + "," +
-                    COLUMN_SIZE + "," +
-                    DECIMAL_DIGITS + "," +
-                    ORDINAL_POSITION + "," +
-                    SORT_ORDER + "," +
-                    ARRAY_SIZE + " " +
-                    "FROM SYSTEM.CATALOG " +
-                    "WHERE " +
-                    "TABLE_SCHEM %s " +
-                    "AND TABLE_NAME = ? " +
-                    "AND COLUMN_NAME IS NOT NULL " +
-                    "ORDER BY " + 
-                    ORDINAL_POSITION;
-           
-            PreparedStatement stmt = null;
-            if (baseTableSchemaName == null) {
-                fetchColumnInfoForBasePhysicalTable =
-                        String.format(fetchColumnInfoForBasePhysicalTable, "IS NULL ");
-                stmt = metaConnection.prepareStatement(fetchColumnInfoForBasePhysicalTable);
-                stmt.setString(1, baseTableName);
-            } else {
-                fetchColumnInfoForBasePhysicalTable =
-                        String.format(fetchColumnInfoForBasePhysicalTable, " = ? ");
-                stmt = metaConnection.prepareStatement(fetchColumnInfoForBasePhysicalTable);
-                stmt.setString(1, baseTableSchemaName);
-                stmt.setString(2, baseTableName);
-            }
+            boolean clearCache = false;
+            for (Entry<String, List<ViewKey>> entry : parentTableViewsMap.entrySet()) {
+                // Fetch column information for the base physical table
+                String physicalTable = entry.getKey();
+                String baseTableSchemaName = SchemaUtil.getSchemaNameFromFullName(physicalTable).equals(StringUtil.EMPTY_STRING) ? null : SchemaUtil.getSchemaNameFromFullName(physicalTable);
+                String baseTableName = SchemaUtil.getTableNameFromFullName(physicalTable);
+                List<ColumnDetails> basePhysicalTableColumns = new ArrayList<>(); 
+
+                // Columns fetched in order of ordinal position
+                String fetchColumnInfoForBasePhysicalTable = "SELECT " +
+                        COLUMN_NAME + "," +
+                        COLUMN_FAMILY + "," +
+                        DATA_TYPE + "," +
+                        COLUMN_SIZE + "," +
+                        DECIMAL_DIGITS + "," +
+                        ORDINAL_POSITION + "," +
+                        SORT_ORDER + "," +
+                        ARRAY_SIZE + " " +
+                        "FROM SYSTEM.CATALOG " +
+                        "WHERE " +
+                        "TABLE_SCHEM %s " +
+                        "AND TABLE_NAME = ? " +
+                        "AND COLUMN_NAME IS NOT NULL " +
+                        "ORDER BY " + 
+                        ORDINAL_POSITION;
+
+                PreparedStatement stmt = null;
+                if (baseTableSchemaName == null) {
+                    fetchColumnInfoForBasePhysicalTable =
+                            String.format(fetchColumnInfoForBasePhysicalTable, "IS NULL ");
+                    stmt = metaConnection.prepareStatement(fetchColumnInfoForBasePhysicalTable);
+                    stmt.setString(1, baseTableName);
+                } else {
+                    fetchColumnInfoForBasePhysicalTable =
+                            String.format(fetchColumnInfoForBasePhysicalTable, " = ? ");
+                    stmt = metaConnection.prepareStatement(fetchColumnInfoForBasePhysicalTable);
+                    stmt.setString(1, baseTableSchemaName);
+                    stmt.setString(2, baseTableName);
+                }
 
-            try (ResultSet rs = stmt.executeQuery()) {
-                while (rs.next()) {
-                    basePhysicalTableColumns.add(new ColumnDetails(rs.getString(COLUMN_FAMILY), rs
-                            .getString(COLUMN_NAME), rs.getInt(ORDINAL_POSITION), rs
-                            .getInt(DATA_TYPE), rs.getInt(COLUMN_SIZE), rs.getInt(DECIMAL_DIGITS),
-                            rs.getInt(SORT_ORDER), rs.getInt(ARRAY_SIZE)));
+                try (ResultSet rs = stmt.executeQuery()) {
+                    while (rs.next()) {
+                        basePhysicalTableColumns.add(new ColumnDetails(rs.getString(COLUMN_FAMILY), rs
+                                .getString(COLUMN_NAME), rs.getInt(ORDINAL_POSITION), rs
+                                .getInt(DATA_TYPE), rs.getInt(COLUMN_SIZE), rs.getInt(DECIMAL_DIGITS),
+                                rs.getInt(SORT_ORDER), rs.getInt(ARRAY_SIZE)));
+                    }
                 }
-            }
-            
-            // Fetch column information for all the views on the base physical table ordered by ordinal position.
-            List<ViewKey> viewKeys = entry.getValue();
-            StringBuilder sb = new StringBuilder();
-            sb.append("SELECT " + 
-                    TENANT_ID + "," +
-                    TABLE_SCHEM + "," +
-                    TABLE_NAME + "," +
-                    COLUMN_NAME + "," +
-                    COLUMN_FAMILY + "," +
-                    DATA_TYPE + "," +
-                    COLUMN_SIZE + "," +
-                    DECIMAL_DIGITS + "," +
-                    ORDINAL_POSITION + "," +
-                    SORT_ORDER + "," +
-                    ARRAY_SIZE + " " + 
-                    "FROM SYSTEM.CATALOG " +
-                    "WHERE " +
-                    COLUMN_NAME + " IS NOT NULL " +
-                    "AND " +
-                    ORDINAL_POSITION + " <= ? " + // fetch only those columns that would impact setting of base column count
-                    "AND " +
-                    "(" + TENANT_ID+ ", " + TABLE_SCHEM + ", " + TABLE_NAME + ") IN (");
-            
-            int numViews = viewKeys.size();
-            for (int i = 0; i < numViews; i++) {
-                sb.append(" (?, ?, ?) ");
-                if (i < numViews - 1) {
-                    sb.append(", ");
+
+                // Fetch column information for all the views on the base physical table ordered by ordinal position.
+                List<ViewKey> viewKeys = entry.getValue();
+                StringBuilder sb = new StringBuilder();
+                sb.append("SELECT " + 
+                        TENANT_ID + "," +
+                        TABLE_SCHEM + "," +
+                        TABLE_NAME + "," +
+                        COLUMN_NAME + "," +
+                        COLUMN_FAMILY + "," +
+                        DATA_TYPE + "," +
+                        COLUMN_SIZE + "," +
+                        DECIMAL_DIGITS + "," +
+                        ORDINAL_POSITION + "," +
+                        SORT_ORDER + "," +
+                        ARRAY_SIZE + " " + 
+                        "FROM SYSTEM.CATALOG " +
+                        "WHERE " +
+                        COLUMN_NAME + " IS NOT NULL " +
+                        "AND " +
+                        ORDINAL_POSITION + " <= ? " + // fetch only those columns that would impact setting of base column count
+                        "AND " +
+                        "(" + TENANT_ID+ ", " + TABLE_SCHEM + ", " + TABLE_NAME + ") IN (");
+
+                int numViews = viewKeys.size();
+                for (int i = 0; i < numViews; i++) {
+                    sb.append(" (?, ?, ?) ");
+                    if (i < numViews - 1) {
+                        sb.append(", ");
+                    }
                 }
-            }
-            sb.append(" ) ");
-            sb.append(" GROUP BY " +
-                    TENANT_ID + "," +
-                    TABLE_SCHEM + "," +
-                    TABLE_NAME + "," +
-                    COLUMN_NAME + "," +
-                    COLUMN_FAMILY + "," +
-                    DATA_TYPE + "," +
-                    COLUMN_SIZE + "," +
-                    DECIMAL_DIGITS + "," +
-                    ORDINAL_POSITION + "," +
-                    SORT_ORDER + "," +
-                    ARRAY_SIZE + " " + 
-                    "ORDER BY " + 
-                    TENANT_ID + "," + TABLE_SCHEM + ", " + TABLE_NAME + ", " + ORDINAL_POSITION);
-            String fetchViewColumnsSql = sb.toString();
-            stmt = metaConnection.prepareStatement(fetchViewColumnsSql);
-            int numColsInBaseTable = basePhysicalTableColumns.size();
-            stmt.setInt(1, numColsInBaseTable);
-            int paramIndex = 1;
-            stmt.setInt(paramIndex++, numColsInBaseTable);
-            for (ViewKey view : viewKeys) {
-                stmt.setString(paramIndex++, view.tenantId);
-                stmt.setString(paramIndex++, view.schema);
-                stmt.setString(paramIndex++, view.name);
-            }
-            String currentTenantId = null;
-            String currentViewSchema = null;
-            String currentViewName = null;
-            try (ResultSet rs = stmt.executeQuery()) {
-                int numBaseTableColsMatched = 0;
-                boolean ignore = false;
-                boolean baseColumnCountUpserted = false;
-                while (rs.next()) {
-                    String viewTenantId = rs.getString(TENANT_ID);
-                    String viewSchema = rs.getString(TABLE_SCHEM);
-                    String viewName = rs.getString(TABLE_NAME);
-                    if (!(Objects.equal(viewTenantId, currentTenantId) && Objects.equal(viewSchema, currentViewSchema) && Objects.equal(viewName, currentViewName))) {
-                        // We are about to iterate through columns of a different view. Check whether base column count was upserted.
-                        // If it wasn't then it is likely the case that a column inherited from the base table was dropped from view.
-                        if (currentViewName != null && !baseColumnCountUpserted && numBaseTableColsMatched < numColsInBaseTable) {
-                            upsertBaseColumnCountInHeaderRow(metaConnection, currentTenantId, currentViewSchema, currentViewName, DIVERGED_VIEW_BASE_COLUMN_COUNT);
+                sb.append(" ) ");
+                sb.append(" GROUP BY " +
+                        TENANT_ID + "," +
+                        TABLE_SCHEM + "," +
+                        TABLE_NAME + "," +
+                        COLUMN_NAME + "," +
+                        COLUMN_FAMILY + "," +
+                        DATA_TYPE + "," +
+                        COLUMN_SIZE + "," +
+                        DECIMAL_DIGITS + "," +
+                        ORDINAL_POSITION + "," +
+                        SORT_ORDER + "," +
+                        ARRAY_SIZE + " " + 
+                        "ORDER BY " + 
+                        TENANT_ID + "," + TABLE_SCHEM + ", " + TABLE_NAME + ", " + ORDINAL_POSITION);
+                String fetchViewColumnsSql = sb.toString();
+                stmt = metaConnection.prepareStatement(fetchViewColumnsSql);
+                int numColsInBaseTable = basePhysicalTableColumns.size();
+                stmt.setInt(1, numColsInBaseTable);
+                int paramIndex = 1;
+                stmt.setInt(paramIndex++, numColsInBaseTable);
+                for (ViewKey view : viewKeys) {
+                    stmt.setString(paramIndex++, view.tenantId);
+                    stmt.setString(paramIndex++, view.schema);
+                    stmt.setString(paramIndex++, view.name);
+                }
+                String currentTenantId = null;
+                String currentViewSchema = null;
+                String currentViewName = null;
+                try (ResultSet rs = stmt.executeQuery()) {
+                    int numBaseTableColsMatched = 0;
+                    boolean ignore = false;
+                    boolean baseColumnCountUpserted = false;
+                    while (rs.next()) {
+                        String viewTenantId = rs.getString(TENANT_ID);
+                        String viewSchema = rs.getString(TABLE_SCHEM);
+                        String viewName = rs.getString(TABLE_NAME);
+                        if (!(Objects.equal(viewTenantId, currentTenantId) && Objects.equal(viewSchema, currentViewSchema) && Objects.equal(viewName, currentViewName))) {
+                            // We are about to iterate through columns of a different view. Check whether base column count was upserted.
+                            // If it wasn't then it is likely the case that a column inherited from the base table was dropped from view.
+                            if (currentViewName != null && !baseColumnCountUpserted && numBaseTableColsMatched < numColsInBaseTable) {
+                                upsertBaseColumnCountInHeaderRow(metaConnection, currentTenantId, currentViewSchema, currentViewName, DIVERGED_VIEW_BASE_COLUMN_COUNT);
+                                clearCache = true;
+                            }
+                            // reset the values as we are now going to iterate over columns of a new view.
+                            numBaseTableColsMatched = 0;
+                            currentTenantId = viewTenantId;
+                            currentViewSchema = viewSchema;
+                            currentViewName = viewName;
+                            ignore = false;
+                            baseColumnCountUpserted = false;
                         }
-                        // reset the values as we are now going to iterate over columns of a new view.
-                        numBaseTableColsMatched = 0;
-                        currentTenantId = viewTenantId;
-                        currentViewSchema = viewSchema;
-                        currentViewName = viewName;
-                        ignore = false;
-                        baseColumnCountUpserted = false;
-                    }
-                    if (!ignore) {
-                        /*
-                         * Iterate over all the columns of the base physical table and the columns of the view. Compare the
-                         * two till one of the following happens: 
-                         * 
-                         * 1) We run into a view column which is different from column in the base physical table.
-                         * This means that the view has divorced itself from the base physical table. In such a case
-                         * we will set a special value for the base column count. That special value will also be used
-                         * on the server side to filter out the divorced view so that meta-data changes on the base 
-                         * physical table are not propagated to it.
-                         * 
-                         * 2) Every physical table column is present in the view. In that case we set the base column count
-                         * as the number of columns in the base physical table. At that point we ignore rest of the columns
-                         * of the view.
-                         * 
-                         */
-                        ColumnDetails baseTableColumn = basePhysicalTableColumns.get(numBaseTableColsMatched);
-                        String columName = rs.getString(COLUMN_NAME);
-                        String columnFamily = rs.getString(COLUMN_FAMILY);
-                        int ordinalPos = rs.getInt(ORDINAL_POSITION);
-                        int dataType = rs.getInt(DATA_TYPE);
-                        int columnSize = rs.getInt(COLUMN_SIZE);
-                        int decimalDigits = rs.getInt(DECIMAL_DIGITS);
-                        int sortOrder = rs.getInt(SORT_ORDER);
-                        int arraySize = rs.getInt(ARRAY_SIZE);
-                        ColumnDetails viewColumn = new ColumnDetails(columnFamily, columName, ordinalPos, dataType, columnSize, decimalDigits, sortOrder, arraySize);
-                        if (baseTableColumn.equals(viewColumn)) {
-                            numBaseTableColsMatched++;
-                            if (numBaseTableColsMatched == numColsInBaseTable) {
-                                upsertBaseColumnCountInHeaderRow(metaConnection, viewTenantId, viewSchema, viewName, numColsInBaseTable);
-                                // No need to ignore the rest of the columns of the view here since the
-                                // query retrieved only those columns that had ordinal position <= numColsInBaseTable
+                        if (!ignore) {
+                            /*
+                             * Iterate over all the columns of the base physical table and the columns of the view. Compare the
+                             * two till one of the following happens: 
+                             * 
+                             * 1) We run into a view column which is different from column in the base physical table.
+                             * This means that the view has divorced itself from the base physical table. In such a case
+                             * we will set a special value for the base column count. That special value will also be used
+                             * on the server side to filter out the divorced view so that meta-data changes on the base 
+                             * physical table are not propagated to it.
+                             * 
+                             * 2) Every physical table column is present in the view. In that case we set the base column count
+                             * as the number of columns in the base physical table. At that point we ignore rest of the columns
+                             * of the view.
+                             * 
+                             */
+                            ColumnDetails baseTableColumn = basePhysicalTableColumns.get(numBaseTableColsMatched);
+                            String columName = rs.getString(COLUMN_NAME);
+                            String columnFamily = rs.getString(COLUMN_FAMILY);
+                            int ordinalPos = rs.getInt(ORDINAL_POSITION);
+                            int dataType = rs.getInt(DATA_TYPE);
+                            int columnSize = rs.getInt(COLUMN_SIZE);
+                            int decimalDigits = rs.getInt(DECIMAL_DIGITS);
+                            int sortOrder = rs.getInt(SORT_ORDER);
+                            int arraySize = rs.getInt(ARRAY_SIZE);
+                            ColumnDetails viewColumn = new ColumnDetails(columnFamily, columName, ordinalPos, dataType, columnSize, decimalDigits, sortOrder, arraySize);
+                            if (baseTableColumn.equals(viewColumn)) {
+                                numBaseTableColsMatched++;
+                                if (numBaseTableColsMatched == numColsInBaseTable) {
+                                    upsertBaseColumnCountInHeaderRow(metaConnection, viewTenantId, viewSchema, viewName, numColsInBaseTable);
+                                    // No need to ignore the rest of the columns of the view here since the
+                                    // query retrieved only those columns that had ordinal position <= numColsInBaseTable
+                                    baseColumnCountUpserted = true;
+                                    clearCache = true;
+                                }
+                            } else {
+                                // special value to denote that the view has divorced itself from the base physical table.
+                                upsertBaseColumnCountInHeaderRow(metaConnection, viewTenantId, viewSchema, viewName, DIVERGED_VIEW_BASE_COLUMN_COUNT);
                                 baseColumnCountUpserted = true;
+                                clearCache = true;
+                                // ignore rest of the rows for the view.
+                                ignore = true;
                             }
-                        } else {
-                            // special value to denote that the view has divorced itself from the base physical table.
-                            upsertBaseColumnCountInHeaderRow(metaConnection, viewTenantId, viewSchema, viewName, DIVERGED_VIEW_BASE_COLUMN_COUNT);
-                            baseColumnCountUpserted = true;
-                            // ignore rest of the rows for the view.
-                            ignore = true;
                         }
                     }
                 }
+                // set base column count for the header row of the base table too. We use this information
+                // to figure out whether the upgrade is in progress or hasn't started.
+                upsertBaseColumnCountInHeaderRow(metaConnection, null, baseTableSchemaName, baseTableName, BASE_TABLE_BASE_COLUMN_COUNT);
+                metaConnection.commit();
+            }
+            // clear metadata cache on region servers to force loading of the latest metadata
+            if (clearCache) {
+                metaConnection.getQueryServices().clearCache();
+            }
+        } finally {
+            if (metaConnection != null) {
+                metaConnection.close();
             }
-            // set base column count for the header row of the base table too. We use this information
-            // to figure out whether the upgrade is in progress or hasn't started.
-            upsertBaseColumnCountInHeaderRow(metaConnection, null, baseTableSchemaName, baseTableName, BASE_TABLE_BASE_COLUMN_COUNT);
-            metaConnection.commit();
         }
     }
     

http://git-wip-us.apache.org/repos/asf/phoenix/blob/39afa9f1/phoenix-protocol/src/main/PTable.proto
----------------------------------------------------------------------
diff --git a/phoenix-protocol/src/main/PTable.proto b/phoenix-protocol/src/main/PTable.proto
index 348631f..cbbbdd6 100644
--- a/phoenix-protocol/src/main/PTable.proto
+++ b/phoenix-protocol/src/main/PTable.proto
@@ -83,4 +83,5 @@ message PTable {
   optional bytes indexType = 22;
   optional int64 statsTimeStamp = 23;
   optional bool storeNulls = 24;
+  optional int32 baseColumnCount = 25;
 }


[40/50] [abbrv] phoenix git commit: PHOENIX-2117 Fix flapping DataIngestIT

Posted by ma...@apache.org.
PHOENIX-2117 Fix flapping DataIngestIT


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/cf2bc551
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/cf2bc551
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/cf2bc551

Branch: refs/heads/calcite
Commit: cf2bc55175788603830ba8bc8b3eacc0998361c1
Parents: 9f09f1a
Author: Samarth <sa...@salesforce.com>
Authored: Tue Jul 14 17:40:29 2015 -0700
Committer: Samarth <sa...@salesforce.com>
Committed: Tue Jul 14 17:40:46 2015 -0700

----------------------------------------------------------------------
 phoenix-pherf/src/test/resources/datamodel/test_schema.sql | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/cf2bc551/phoenix-pherf/src/test/resources/datamodel/test_schema.sql
----------------------------------------------------------------------
diff --git a/phoenix-pherf/src/test/resources/datamodel/test_schema.sql b/phoenix-pherf/src/test/resources/datamodel/test_schema.sql
index 162d288..4e6b9d4 100644
--- a/phoenix-pherf/src/test/resources/datamodel/test_schema.sql
+++ b/phoenix-pherf/src/test/resources/datamodel/test_schema.sql
@@ -29,4 +29,4 @@ CREATE TABLE IF NOT EXISTS PHERF.TEST_TABLE (
         PARENT_ID,
         CREATED_DATE DESC
     )
-) VERSIONS=1,MULTI_TENANT=true,SALT_BUCKETS=16
+) VERSIONS=1,MULTI_TENANT=true


[07/50] [abbrv] phoenix git commit: PHOENIX-2074 StackOverflowError for hash join with round robin

Posted by ma...@apache.org.
PHOENIX-2074 StackOverflowError for hash join with round robin


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/d0c8f9db
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/d0c8f9db
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/d0c8f9db

Branch: refs/heads/calcite
Commit: d0c8f9dbd15460c023d50c8b8fd9334fb18c05dc
Parents: 39c982f
Author: maryannxue <we...@intel.com>
Authored: Tue Jul 7 13:27:49 2015 -0400
Committer: maryannxue <we...@intel.com>
Committed: Tue Jul 7 13:27:49 2015 -0400

----------------------------------------------------------------------
 .../iterate/RoundRobinResultIteratorIT.java     | 80 ++++++++++++++++++++
 .../iterate/RoundRobinResultIterator.java       |  2 +-
 2 files changed, 81 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/d0c8f9db/phoenix-core/src/it/java/org/apache/phoenix/iterate/RoundRobinResultIteratorIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/iterate/RoundRobinResultIteratorIT.java b/phoenix-core/src/it/java/org/apache/phoenix/iterate/RoundRobinResultIteratorIT.java
index 6a9b3d4..224ed95 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/iterate/RoundRobinResultIteratorIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/iterate/RoundRobinResultIteratorIT.java
@@ -17,6 +17,7 @@
  */
 package org.apache.phoenix.iterate;
 
+import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
@@ -30,6 +31,7 @@ import java.sql.Statement;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Map;
+import java.util.Properties;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -43,6 +45,7 @@ import org.apache.phoenix.jdbc.PhoenixConnection;
 import org.apache.phoenix.jdbc.PhoenixResultSet;
 import org.apache.phoenix.query.ConnectionQueryServices;
 import org.apache.phoenix.query.QueryServices;
+import org.apache.phoenix.util.PropertiesUtil;
 import org.apache.phoenix.util.ReadOnlyProps;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -301,6 +304,83 @@ public class RoundRobinResultIteratorIT extends BaseHBaseManagedTimeIT {
         assertEquals("Number of rows retrieved didnt match for tableB", insertedRowsB, rowsB);
         assertEquals("Number of rows retrieved didn't match for tableC", insertedRowsC, rowsC);
     }
+    
+    @Test
+    public void testBug2074() throws Exception {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            conn.createStatement().execute("CREATE TABLE EVENTS" 
+                    + "   (id VARCHAR(10) PRIMARY KEY, " 
+                    + "    article VARCHAR(10), " 
+                    + "    misc VARCHAR(10))");
+            
+            PreparedStatement upsertStmt = conn.prepareStatement(
+                    "upsert into EVENTS(id, article, misc) " + "values (?, ?, ?)");
+            upsertStmt.setString(1, "001");
+            upsertStmt.setString(2, "A");
+            upsertStmt.setString(3, "W");
+            upsertStmt.execute();
+            upsertStmt.setString(1, "002");
+            upsertStmt.setString(2, "B");
+            upsertStmt.setString(3, "X");
+            upsertStmt.execute();
+            upsertStmt.setString(1, "003");
+            upsertStmt.setString(2, "C");
+            upsertStmt.setString(3, "Y");
+            upsertStmt.execute();
+            upsertStmt.setString(1, "004");
+            upsertStmt.setString(2, "D");
+            upsertStmt.setString(3, "Z");
+            upsertStmt.execute();
+            conn.commit();
+
+            conn.createStatement().execute("CREATE TABLE MAPPING" 
+                    + "   (id VARCHAR(10) PRIMARY KEY, " 
+                    + "    article VARCHAR(10), " 
+                    + "    category VARCHAR(10))");
+            
+            upsertStmt = conn.prepareStatement(
+                    "upsert into MAPPING(id, article, category) " + "values (?, ?, ?)");
+            upsertStmt.setString(1, "002");
+            upsertStmt.setString(2, "A");
+            upsertStmt.setString(3, "X");
+            upsertStmt.execute();
+            upsertStmt.setString(1, "003");
+            upsertStmt.setString(2, "B");
+            upsertStmt.setString(3, "Y");
+            upsertStmt.execute();
+            upsertStmt.setString(1, "004");
+            upsertStmt.setString(2, "C");
+            upsertStmt.setString(3, "Z");
+            upsertStmt.execute();
+            upsertStmt.setString(1, "005");
+            upsertStmt.setString(2, "E");
+            upsertStmt.setString(3, "Z");
+            upsertStmt.execute();
+            upsertStmt.setString(1, "006");
+            upsertStmt.setString(2, "C");
+            upsertStmt.setString(3, "Z");
+            upsertStmt.execute();
+            upsertStmt.setString(1, "007");
+            upsertStmt.setString(2, "C");
+            upsertStmt.setString(3, "Z");
+            upsertStmt.execute();
+            conn.commit();
+
+            // No leading part of PK
+            String query = "select count(MAPPING.article) as cnt,MAPPING.category from EVENTS"
+                    + " join MAPPING on MAPPING.article = EVENTS.article"
+                    + " group by category order by cnt";
+            PreparedStatement statement = conn.prepareStatement(query);
+            statement.setFetchSize(3);
+            ResultSet rs = statement.executeQuery();
+            while(rs.next());
+            
+        } finally {
+            conn.close();
+        }
+    }
 
 
     private static ResultIterator getResultIterator(ResultSet rs) throws SQLException {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/d0c8f9db/phoenix-core/src/main/java/org/apache/phoenix/iterate/RoundRobinResultIterator.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/iterate/RoundRobinResultIterator.java b/phoenix-core/src/main/java/org/apache/phoenix/iterate/RoundRobinResultIterator.java
index 92ac570..5624f5f 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/iterate/RoundRobinResultIterator.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/iterate/RoundRobinResultIterator.java
@@ -240,7 +240,7 @@ public class RoundRobinResultIterator implements ResultIterator {
             for (Future<Tuple> future : futures) {
                 Tuple tuple = future.get();
                 if (tuple != null) {
-                    results.add(new RoundRobinIterator(openIterators.get(i), tuple));
+                    results.add(new RoundRobinIterator(openIterators.get(i).delegate, tuple));
                 } else {
                     // Underlying scanner is exhausted. So close it.
                     openIterators.get(i).close();


[39/50] [abbrv] phoenix git commit: PHOENIX-2111 Race condition on creation of new view and adding of column to base table

Posted by ma...@apache.org.
PHOENIX-2111 Race condition on creation of new view and adding of column to base table


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/9f09f1a5
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/9f09f1a5
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/9f09f1a5

Branch: refs/heads/calcite
Commit: 9f09f1a5ddce38c256c647ca7cd80617259e35ea
Parents: 4b99c63
Author: Samarth <sa...@salesforce.com>
Authored: Tue Jul 14 17:24:01 2015 -0700
Committer: Samarth <sa...@salesforce.com>
Committed: Tue Jul 14 17:24:01 2015 -0700

----------------------------------------------------------------------
 .../coprocessor/MetaDataEndpointImpl.java       |  239 ++--
 .../coprocessor/generated/MetaDataProtos.java   | 1243 +++++++++++++++++-
 .../query/ConnectionQueryServicesImpl.java      |   75 +-
 .../apache/phoenix/schema/MetaDataClient.java   |   12 +-
 .../org/apache/phoenix/util/MetaDataUtil.java   |   14 +
 .../org/apache/phoenix/util/PhoenixRuntime.java |    4 -
 .../org/apache/phoenix/util/UpgradeUtil.java    |    4 +-
 phoenix-protocol/src/main/MetaDataService.proto |   14 +-
 8 files changed, 1414 insertions(+), 191 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/9f09f1a5/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 dcfe61d..5396a69 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
@@ -1068,6 +1068,50 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
             return null;
         }
 
+    /**
+     * 
+     * @return null if the physical table row information is not present.
+     * 
+     */
+    private static Mutation getPhysicalTableForView(List<Mutation> tableMetadata, byte[][] parentSchemaTableNames) {
+        int size = tableMetadata.size();
+        byte[][] rowKeyMetaData = new byte[3][];
+        MetaDataUtil.getTenantIdAndSchemaAndTableName(tableMetadata, rowKeyMetaData);
+        Mutation physicalTableRow = null;
+        boolean physicalTableLinkFound = false;
+        if (size >= 2) {
+            int i = size - 1;
+            while (i >= 1) {
+                Mutation m = tableMetadata.get(i);
+                if (m instanceof Put) {
+                    LinkType linkType = MetaDataUtil.getLinkType(m);
+                    if (linkType == LinkType.PHYSICAL_TABLE) {
+                        physicalTableRow = m;
+                        physicalTableLinkFound = true;
+                        break;
+                    }
+                }
+                i--;
+            }
+        }
+        if (!physicalTableLinkFound) {
+            parentSchemaTableNames[0] = null;
+            parentSchemaTableNames[1] = null;
+            return null;
+        }
+        rowKeyMetaData = new byte[5][];
+        getVarChars(physicalTableRow.getRow(), 5, rowKeyMetaData);
+        byte[] colBytes = rowKeyMetaData[PhoenixDatabaseMetaData.COLUMN_NAME_INDEX];
+        byte[] famBytes = rowKeyMetaData[PhoenixDatabaseMetaData.FAMILY_NAME_INDEX];
+        if ((colBytes == null || colBytes.length == 0) && (famBytes != null && famBytes.length > 0)) {
+            byte[] sName = SchemaUtil.getSchemaNameFromFullName(famBytes).getBytes();
+            byte[] tName = SchemaUtil.getTableNameFromFullName(famBytes).getBytes();
+            parentSchemaTableNames[0] = sName;
+            parentSchemaTableNames[1] = tName;
+        }
+        return physicalTableRow;
+    }
+    
     @Override
     public void createTable(RpcController controller, CreateTableRequest request,
             RpcCallback<MetaDataResponse> done) {
@@ -1075,66 +1119,101 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
         byte[][] rowKeyMetaData = new byte[3][];
         byte[] schemaName = null;
         byte[] tableName = null;
-
         try {
             List<Mutation> tableMetadata = ProtobufUtil.getMutations(request);
             MetaDataUtil.getTenantIdAndSchemaAndTableName(tableMetadata, rowKeyMetaData);
             byte[] tenantIdBytes = rowKeyMetaData[PhoenixDatabaseMetaData.TENANT_ID_INDEX];
             schemaName = rowKeyMetaData[PhoenixDatabaseMetaData.SCHEMA_NAME_INDEX];
             tableName = rowKeyMetaData[PhoenixDatabaseMetaData.TABLE_NAME_INDEX];
-            byte[] parentTableName = MetaDataUtil.getParentTableName(tableMetadata);
-            byte[] lockTableName = parentTableName == null ? tableName : parentTableName;
-            byte[] lockKey = SchemaUtil.getTableKey(tenantIdBytes, schemaName, lockTableName);
-            byte[] key =
-                    parentTableName == null ? lockKey : SchemaUtil.getTableKey(tenantIdBytes,
-                        schemaName, tableName);
-            byte[] parentKey = parentTableName == null ? null : lockKey;
 
-            Region region = env.getRegion();
-            MetaDataMutationResult result = checkTableKeyInRegion(lockKey, region);
-            if (result != null) {
-                done.run(MetaDataMutationResult.toProto(result));
-                return;
+            byte[] parentSchemaName = null;
+            byte[] parentTableName = null;
+            PTableType tableType = MetaDataUtil.getTableType(tableMetadata, GenericKeyValueBuilder.INSTANCE, new ImmutableBytesWritable());
+            byte[] parentTableKey = null;
+            Mutation viewPhysicalTableRow = null;
+            if (tableType == PTableType.VIEW) {
+                byte[][] parentSchemaTableNames = new byte[2][];
+                /*
+                 * For a view, we lock the base physical table row. For a mapped view, there is 
+                 * no link present to the physical table. So the viewPhysicalTableRow is null
+                 * in that case.
+                 */
+                viewPhysicalTableRow = getPhysicalTableForView(tableMetadata, parentSchemaTableNames);
+                parentSchemaName = parentSchemaTableNames[0];
+                parentTableName = parentSchemaTableNames[1];
+                if (parentTableName != null) {
+                    parentTableKey = SchemaUtil.getTableKey(ByteUtil.EMPTY_BYTE_ARRAY, parentSchemaName, parentTableName);
+                }
+            } else if (tableType == PTableType.INDEX) {
+                parentSchemaName = schemaName;
+                /* 
+                 * For an index we lock the parent table's row which could be a physical table or a view.
+                 * If the parent table is a physical table, then the tenantIdBytes is empty because
+                 * we allow creating an index with a tenant connection only if the parent table is a view.
+                 */ 
+                parentTableName = MetaDataUtil.getParentTableName(tableMetadata);
+                parentTableKey = SchemaUtil.getTableKey(tenantIdBytes, parentSchemaName, parentTableName);
             }
+
+            Region region = env.getRegion();
             List<RowLock> locks = Lists.newArrayList();
-            long clientTimeStamp = MetaDataUtil.getClientTimeStamp(tableMetadata);
+            // Place a lock using key for the table to be created
+            byte[] tableKey = SchemaUtil.getTableKey(tenantIdBytes, schemaName, tableName);
             try {
-                acquireLock(region, lockKey, locks);
-                if (key != lockKey) {
-                    acquireLock(region, key, locks);
+                acquireLock(region, tableKey, locks);
+
+                // If the table key resides outside the region, return without doing anything
+                MetaDataMutationResult result = checkTableKeyInRegion(tableKey, region);
+                if (result != null) {
+                    done.run(MetaDataMutationResult.toProto(result));
+                    return;
                 }
-                // Load parent table first
-                PTable parentTable = null;
+
+                long clientTimeStamp = MetaDataUtil.getClientTimeStamp(tableMetadata);
                 ImmutableBytesPtr parentCacheKey = null;
-                if (parentKey != null) {
-                    parentCacheKey = new ImmutableBytesPtr(parentKey);
-                    parentTable =
-                            loadTable(env, parentKey, parentCacheKey, clientTimeStamp,
+                if (parentTableName != null) {
+                    // Check if the parent table resides in the same region. If not, don't worry about locking the parent table row
+                    // or loading the parent table. For a view, the parent table that needs to be locked is the base physical table.
+                    // For an index on view, the view header row needs to be locked. 
+                    result = checkTableKeyInRegion(parentTableKey, region);
+                    if (result == null) {
+                        acquireLock(region, parentTableKey, locks);
+                        parentCacheKey = new ImmutableBytesPtr(parentTableKey);
+                        PTable parentTable = loadTable(env, parentTableKey, parentCacheKey, clientTimeStamp,
                                 clientTimeStamp);
-                    if (parentTable == null || isTableDeleted(parentTable)) {
-                        builder.setReturnCode(MetaDataProtos.MutationCode.PARENT_TABLE_NOT_FOUND);
-                        builder.setMutationTime(EnvironmentEdgeManager.currentTimeMillis());
-                        builder.setTable(PTableImpl.toProto(parentTable));
-                        done.run(builder.build());
-                        return;
-                    }
-                    // If parent table isn't at the expected sequence number, then return
-                    if (parentTable.getSequenceNumber() != MetaDataUtil
-                            .getParentSequenceNumber(tableMetadata)) {
-                        builder.setReturnCode(MetaDataProtos.MutationCode.CONCURRENT_TABLE_MUTATION);
-                        builder.setMutationTime(EnvironmentEdgeManager.currentTimeMillis());
-                        builder.setTable(PTableImpl.toProto(parentTable));
-                        done.run(builder.build());
-                        return;
+                        if (parentTable == null || isTableDeleted(parentTable)) {
+                            builder.setReturnCode(MetaDataProtos.MutationCode.PARENT_TABLE_NOT_FOUND);
+                            builder.setMutationTime(EnvironmentEdgeManager.currentTimeMillis());
+                            done.run(builder.build());
+                            return;
+                        }
+                        long parentTableSeqNumber;
+                        if (tableType == PTableType.VIEW && viewPhysicalTableRow != null && request.hasClientVersion()) {
+                            // Starting 4.5, the client passes the sequence number of the physical table in the table metadata.
+                            parentTableSeqNumber = MetaDataUtil.getSequenceNumber(viewPhysicalTableRow);
+                        } else if (tableType == PTableType.VIEW && !request.hasClientVersion()) {
+                            // Before 4.5, due to a bug, the parent table key wasn't available.
+                            // So don't do anything and prevent the exception from being thrown.
+                            parentTableSeqNumber = parentTable.getSequenceNumber();
+                        } else {
+                            parentTableSeqNumber = MetaDataUtil.getParentSequenceNumber(tableMetadata);
+                        }
+                        // If parent table isn't at the expected sequence number, then return
+                        if (parentTable.getSequenceNumber() != parentTableSeqNumber) {
+                            builder.setReturnCode(MetaDataProtos.MutationCode.CONCURRENT_TABLE_MUTATION);
+                            builder.setMutationTime(EnvironmentEdgeManager.currentTimeMillis());
+                            builder.setTable(PTableImpl.toProto(parentTable));
+                            done.run(builder.build());
+                            return;
+                        }
                     }
                 }
                 // Load child table next
-                ImmutableBytesPtr cacheKey = new ImmutableBytesPtr(key);
+                ImmutableBytesPtr cacheKey = new ImmutableBytesPtr(tableKey);
                 // Get as of latest timestamp so we can detect if we have a newer table that already
-                // exists
-                // without making an additional query
+                // exists without making an additional query
                 PTable table =
-                        loadTable(env, key, cacheKey, clientTimeStamp, HConstants.LATEST_TIMESTAMP);
+                        loadTable(env, tableKey, cacheKey, clientTimeStamp, HConstants.LATEST_TIMESTAMP);
                 if (table != null) {
                     if (table.getTimeStamp() < clientTimeStamp) {
                         // If the table is older than the client time stamp and it's deleted,
@@ -1154,26 +1233,19 @@ 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);
+                    UpgradeUtil.addRowKeyOrderOptimizableCell(tableMetadata, tableKey, 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
+                // TODO: Switch this to HRegion#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
                 // tableMetadata rows. This ensures we don't have deadlock situations (ensuring
-                // primary and
-                // then index table locks are held, in that order). For now, we just don't support
-                // indexing
-                // on the system table. This is an issue because of the way we manage batch mutation
-                // in the
-                // Indexer.
-                region.mutateRowsWithLocks(tableMetadata, Collections.<byte[]> emptySet(), HConstants.NO_NONCE,
-                    HConstants.NO_NONCE);
+                // primary and then index table locks are held, in that order). For now, we just don't support
+                // indexing on the system table. This is an issue because of the way we manage batch mutation
+                // in the Indexer.
+                region.mutateRowsWithLocks(tableMetadata, Collections.<byte[]> emptySet(), HConstants.NO_NONCE, HConstants.NO_NONCE);
 
                 // Invalidate the cache - the next getTable call will add it
                 // TODO: consider loading the table that was just created here, patching up the parent table, and updating the cache
@@ -1192,14 +1264,12 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
                 region.releaseRowLocks(locks);
             }
         } catch (Throwable t) {
-          logger.error("createTable failed", t);
+            logger.error("createTable failed", t);
             ProtobufUtil.setControllerException(controller,
-                ServerUtil.createIOException(SchemaUtil.getTableName(schemaName, tableName), t));
+                    ServerUtil.createIOException(SchemaUtil.getTableName(schemaName, tableName), t));
         }
     }
 
-
-
     private static RowLock acquireLock(Region region, byte[] key, List<RowLock> locks)
         throws IOException {
         RowLock rowLock = region.getRowLock(key, true);
@@ -1609,10 +1679,6 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
         }
     }
     
-    private boolean isvalidAttribute(Object obj1, Object obj2) {
-    	return (obj1!=null && obj1.equals(obj2)) || (obj1==null && obj2==null); 
-    }
-
     private MetaDataMutationResult addRowsToChildViews(PTable table, List<Mutation> tableMetadata, List<Mutation> mutationsForAddingColumnsToViews, byte[] schemaName, byte[] tableName,
             List<ImmutableBytesPtr> invalidateList, long clientTimeStamp, TableViewFinderResult childViewsResult,
             Region region, List<RowLock> locks) throws IOException, SQLException {
@@ -1924,7 +1990,7 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
     }
     
     @Override
-    public void addColumn(RpcController controller, AddColumnRequest request,
+    public void addColumn(RpcController controller, final AddColumnRequest request,
             RpcCallback<MetaDataResponse> done) {
         try {
             List<Mutation> tableMetaData = ProtobufUtil.getMutations(request);
@@ -1953,25 +2019,30 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
                     if (type == PTableType.TABLE || type == PTableType.SYSTEM) {
                         TableViewFinderResult childViewsResult = findChildViews(region, tenantId, table, PHYSICAL_TABLE_BYTES);
                         if (childViewsResult.hasViews()) {
-                           /* 
-                            * Adding a column is not allowed if the meta-data for child view/s spans over
-                            * more than one region (since the changes cannot be made in a transactional fashion)
-                            * A base column count of 0 means that the metadata hasn't been upgraded yet or
-                            * the upgrade is currently in progress. If that is the case, disallow adding columns
-                            *  for tables with views.
-                            */
-                            if (!childViewsResult.allViewsInSingleRegion() || table.getBaseColumnCount() == 0) {
-                                return new MetaDataMutationResult(MutationCode.UNALLOWED_TABLE_MUTATION,
-                                        EnvironmentEdgeManager.currentTimeMillis(), null);
-                            } else {
-                                mutationsForAddingColumnsToViews = new ArrayList<>(childViewsResult.getResults().size() * tableMetaData.size());
-                                MetaDataMutationResult mutationResult = addRowsToChildViews(table, tableMetaData, mutationsForAddingColumnsToViews, schemaName, tableName, invalidateList, clientTimeStamp,
-                                        childViewsResult, region, locks);
-                                // return if we were not able to add the column successfully
-                                if (mutationResult!=null)
-                                	return mutationResult;
-                            } 
-                        }
+                            /* 
+                             * Adding a column is not allowed if
+                             * 1) The meta-data for child view/s spans over
+                             * more than one region (since the changes cannot be made in a transactional fashion)
+                             * 
+                             * 2) The base column count is 0 which means that the metadata hasn't been upgraded yet or
+                             * the upgrade is currently in progress.
+                             * 
+                             * 3) If the request is from a client that is older than 4.5 version of phoenix. 
+                             * Starting from 4.5, metadata requests have the client version included in them. 
+                             * We don't want to allow clients before 4.5 to add a column to the base table if it has views.
+                             */
+                             if (!childViewsResult.allViewsInSingleRegion() || table.getBaseColumnCount() == 0 || !request.hasClientVersion()) {
+                                 return new MetaDataMutationResult(MutationCode.UNALLOWED_TABLE_MUTATION,
+                                         EnvironmentEdgeManager.currentTimeMillis(), null);
+                             } else {
+                                 mutationsForAddingColumnsToViews = new ArrayList<>(childViewsResult.getResults().size() * tableMetaData.size());
+                                 MetaDataMutationResult mutationResult = addRowsToChildViews(table, tableMetaData, mutationsForAddingColumnsToViews, schemaName, tableName, invalidateList, clientTimeStamp,
+                                         childViewsResult, region, locks);
+                                 // return if we were not able to add the column successfully
+                                 if (mutationResult!=null)
+                                     return mutationResult;
+                             } 
+                         }
                     }
                     for (Mutation m : tableMetaData) {
                         byte[] key = m.getRow();


[26/50] [abbrv] phoenix git commit: PHOENIX-2109 - Joda time version conflict between phoenix and pherf

Posted by ma...@apache.org.
PHOENIX-2109 - Joda time version conflict between phoenix and pherf


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/a772a4a0
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/a772a4a0
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/a772a4a0

Branch: refs/heads/calcite
Commit: a772a4a0ca3c9ac98335ce2148eb2b34d3ce1369
Parents: 70c542d
Author: Cody Marcel <co...@gmail.com>
Authored: Mon Jul 13 14:23:53 2015 -0700
Committer: Cody Marcel <co...@gmail.com>
Committed: Mon Jul 13 14:23:53 2015 -0700

----------------------------------------------------------------------
 phoenix-pherf/pom.xml                                        | 2 +-
 .../src/it/java/org/apache/phoenix/pherf/DataIngestIT.java   | 8 ++++++++
 2 files changed, 9 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/a772a4a0/phoenix-pherf/pom.xml
----------------------------------------------------------------------
diff --git a/phoenix-pherf/pom.xml b/phoenix-pherf/pom.xml
index 0facbde..4d27305 100644
--- a/phoenix-pherf/pom.xml
+++ b/phoenix-pherf/pom.xml
@@ -219,7 +219,7 @@
         <dependency>
             <groupId>joda-time</groupId>
             <artifactId>joda-time</artifactId>
-            <version>1.6.2</version>
+            <version>${jodatime.version}</version>
         </dependency>
         <dependency>
             <groupId>com.googlecode.java-diff-utils</groupId>

http://git-wip-us.apache.org/repos/asf/phoenix/blob/a772a4a0/phoenix-pherf/src/it/java/org/apache/phoenix/pherf/DataIngestIT.java
----------------------------------------------------------------------
diff --git a/phoenix-pherf/src/it/java/org/apache/phoenix/pherf/DataIngestIT.java b/phoenix-pherf/src/it/java/org/apache/phoenix/pherf/DataIngestIT.java
index 828ac38..8a340b3 100644
--- a/phoenix-pherf/src/it/java/org/apache/phoenix/pherf/DataIngestIT.java
+++ b/phoenix-pherf/src/it/java/org/apache/phoenix/pherf/DataIngestIT.java
@@ -27,6 +27,7 @@ import org.apache.phoenix.pherf.configuration.Scenario;
 import org.apache.phoenix.pherf.rules.DataValue;
 import org.apache.phoenix.pherf.rules.RulesApplier;
 import org.apache.phoenix.pherf.workload.QueryExecutor;
+import org.apache.phoenix.pherf.workload.Workload;
 import org.apache.phoenix.pherf.workload.WorkloadExecutor;
 import org.apache.phoenix.pherf.workload.WriteWorkload;
 import org.junit.Before;
@@ -68,6 +69,7 @@ public class DataIngestIT extends ResultBaseTestIT {
             WriteWorkload loader = new WriteWorkload(util, parser, scenario);
             WorkloadExecutor executor = new WorkloadExecutor();
             executor.add(loader);
+            executor.get();
 
             RulesApplier rulesApplier = loader.getRulesApplier();
             List<Map> modelList = rulesApplier.getModelList();
@@ -90,6 +92,12 @@ public class DataIngestIT extends ResultBaseTestIT {
                             data.getDistribution() == Integer.MIN_VALUE);
                 }
             }
+
+            // Run some queries
+            Workload query = new QueryExecutor(parser, util, executor.getPool());
+            executor.add(query);
+            executor.get();
+
         } catch (Exception e) {
             fail("We had an exception: " + e.getMessage());
         }


[41/50] [abbrv] phoenix git commit: PHOENIX-2118 Remove/modfiy usages of Guava StopWatch and deprecated ComparisonChain methods

Posted by ma...@apache.org.
PHOENIX-2118 Remove/modfiy usages of Guava StopWatch and deprecated ComparisonChain methods


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/a8f0d769
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/a8f0d769
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/a8f0d769

Branch: refs/heads/calcite
Commit: a8f0d7696b6ee105d55c7d3dce50563e816cd857
Parents: cf2bc55
Author: Samarth <sa...@salesforce.com>
Authored: Wed Jul 15 12:21:09 2015 -0700
Committer: Samarth <sa...@salesforce.com>
Committed: Wed Jul 15 12:21:09 2015 -0700

----------------------------------------------------------------------
 .../phoenix/monitoring/MetricsStopWatch.java    |  8 +-
 .../query/ConnectionQueryServicesImpl.java      |  4 +-
 .../java/org/apache/phoenix/query/KeyRange.java | 13 +---
 .../apache/phoenix/util/PhoenixStopWatch.java   | 81 ++++++++++++++++++++
 4 files changed, 91 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/a8f0d769/phoenix-core/src/main/java/org/apache/phoenix/monitoring/MetricsStopWatch.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/monitoring/MetricsStopWatch.java b/phoenix-core/src/main/java/org/apache/phoenix/monitoring/MetricsStopWatch.java
index bffb9ad..ee260a8 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/monitoring/MetricsStopWatch.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/monitoring/MetricsStopWatch.java
@@ -17,23 +17,23 @@
  */
 package org.apache.phoenix.monitoring;
 
-import com.google.common.base.Stopwatch;
+import org.apache.phoenix.util.PhoenixStopWatch;
 
 /**
  * 
  * Stop watch that is cognizant of the fact whether or not metrics is enabled.
  * If metrics isn't enabled it doesn't do anything. Otherwise, it delegates
- * calls to a {@code Stopwatch}.
+ * calls to a {@code PhoenixStopWatch}.
  *
  */
 final class MetricsStopWatch {
     
     private final boolean isMetricsEnabled;
-    private final Stopwatch stopwatch;
+    private final PhoenixStopWatch stopwatch;
     
     MetricsStopWatch(boolean isMetricsEnabled) {
         this.isMetricsEnabled = isMetricsEnabled;
-        this.stopwatch = new Stopwatch();
+        this.stopwatch = new PhoenixStopWatch();
     }
     
     void start()  {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/a8f0d769/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
index 52b038b..a17e28a 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
@@ -158,6 +158,7 @@ import org.apache.phoenix.util.JDBCUtil;
 import org.apache.phoenix.util.MetaDataUtil;
 import org.apache.phoenix.util.PhoenixContextExecutor;
 import org.apache.phoenix.util.PhoenixRuntime;
+import org.apache.phoenix.util.PhoenixStopWatch;
 import org.apache.phoenix.util.PropertiesUtil;
 import org.apache.phoenix.util.ReadOnlyProps;
 import org.apache.phoenix.util.SchemaUtil;
@@ -167,7 +168,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Joiner;
-import com.google.common.base.Stopwatch;
 import com.google.common.base.Throwables;
 import com.google.common.cache.Cache;
 import com.google.common.cache.CacheBuilder;
@@ -757,7 +757,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
                         QueryServicesOptions.DEFAULT_DELAY_FOR_SCHEMA_UPDATE_CHECK);
         boolean success = false;
         int numTries = 1;
-        Stopwatch watch = new Stopwatch();
+        PhoenixStopWatch watch = new PhoenixStopWatch();
         watch.start();
         do {
             try {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/a8f0d769/phoenix-core/src/main/java/org/apache/phoenix/query/KeyRange.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/KeyRange.java b/phoenix-core/src/main/java/org/apache/phoenix/query/KeyRange.java
index bca55e8..0612046 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/KeyRange.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/KeyRange.java
@@ -80,23 +80,18 @@ public class KeyRange implements Writable {
         }
     };
     public static final Comparator<KeyRange> COMPARATOR = new Comparator<KeyRange>() {
-        @SuppressWarnings("deprecation")
         @Override public int compare(KeyRange o1, KeyRange o2) {
             return ComparisonChain.start()
-//                    .compareFalseFirst(o1.lowerUnbound(), o2.lowerUnbound())
-                    .compare(o2.lowerUnbound(), o1.lowerUnbound())
+                     .compareFalseFirst(o2.lowerUnbound(), o1.lowerUnbound())
                     .compare(o1.getLowerRange(), o2.getLowerRange(), Bytes.BYTES_COMPARATOR)
                     // we want o1 lower inclusive to come before o2 lower inclusive, but
                     // false comes before true, so we have to negate
-//                    .compareTrueFirst(o1.isLowerInclusive(), o2.isLowerInclusive())
-                    .compare(o2.isLowerInclusive(), o1.isLowerInclusive())
+                    .compareFalseFirst(o2.isLowerInclusive(), o1.isLowerInclusive())
                     // for the same lower bounding, we want a finite upper bound to
                     // be ordered before an infinite upper bound
-//                    .compareTrueFirst(o1.upperUnbound(), o2.upperUnbound())
-                    .compare(o1.upperUnbound(), o2.upperUnbound())
+                    .compareFalseFirst(o1.upperUnbound(), o2.upperUnbound())
                     .compare(o1.getUpperRange(), o2.getUpperRange(), Bytes.BYTES_COMPARATOR)
-//                    .compareFalseFirst(o1.isUpperInclusive(), o2.isUpperInclusive())
-                    .compare(o2.isUpperInclusive(), o1.isUpperInclusive())
+                    .compareFalseFirst(o2.isUpperInclusive(), o1.isUpperInclusive())
                     .result();
         }
     };

http://git-wip-us.apache.org/repos/asf/phoenix/blob/a8f0d769/phoenix-core/src/main/java/org/apache/phoenix/util/PhoenixStopWatch.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/PhoenixStopWatch.java b/phoenix-core/src/main/java/org/apache/phoenix/util/PhoenixStopWatch.java
new file mode 100644
index 0000000..9001b0a
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/util/PhoenixStopWatch.java
@@ -0,0 +1,81 @@
+/*
+ * 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.util;
+
+/**
+ * Bare-bones implementation of a stop watch that only measures time in milliseconds. If you want to be fancy then
+ * please use the guava Stopwatch. However, be warned that the Guava's Stopwatch is a beta class and is subject to
+ * incompatible changes and removal. So save the future upgrade pain and use this class instead.
+ */
+public class PhoenixStopWatch {
+    private boolean isRunning;
+    private long startTime;
+    private long elapsedTimeMs;
+
+    /**
+     * Creates a new stop watch without starting it.
+     */
+    public PhoenixStopWatch() {}
+
+    /**
+     * Starts the stopwatch.
+     * 
+     * @return this {@code PhoenixStopWatch} instance
+     * @throws IllegalStateException
+     *             if the stopwatch is already running.
+     */
+    public PhoenixStopWatch start() {
+        long currentTime = System.currentTimeMillis();
+        if (isRunning) { throw new IllegalStateException("Watch is already running"); }
+        startTime = currentTime;
+        isRunning = true;
+        return this;
+    }
+
+    /**
+     * Stops the stopwatch. Future calls to {@link #elapsedMillis()} will return the fixed duration that had elapsed up
+     * to this point.
+     * 
+     * @return this {@code PhoenixStopWatch} instance
+     * @throws IllegalStateException
+     *             if the stopwatch is already stopped.
+     */
+    public PhoenixStopWatch stop() {
+        long currentTime = System.currentTimeMillis();
+        if (!isRunning) { throw new IllegalStateException("Watch wasn't started"); }
+        elapsedTimeMs = currentTime - startTime;
+        startTime = 0;
+        isRunning = false;
+        return this;
+    }
+
+    /**
+     * Returns the current elapsed time shown on this stopwatch, expressed in milliseconds.
+     */
+    public long elapsedMillis() {
+        return elapsedTimeMs;
+    }
+
+    /**
+     * Returns {@code true} if {@link #start()} has been called on this stopwatch, and {@link #stop()} has not been
+     * called since the last call to {@code start()}.
+     */
+    public boolean isRunning() {
+        return isRunning;
+    }
+}


[47/50] [abbrv] phoenix git commit: PHOENIX-2125 ORDER BY on full PK on salted table does not work

Posted by ma...@apache.org.
PHOENIX-2125 ORDER BY on full PK on salted table does not work


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/b329e85b
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/b329e85b
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/b329e85b

Branch: refs/heads/calcite
Commit: b329e85b697575fcebcde9555c991038d14e4a3c
Parents: f006df5
Author: Samarth <sa...@salesforce.com>
Authored: Fri Jul 17 17:30:56 2015 -0700
Committer: Samarth <sa...@salesforce.com>
Committed: Fri Jul 17 17:30:56 2015 -0700

----------------------------------------------------------------------
 .../org/apache/phoenix/execute/ScanPlan.java    | 12 ++++++-
 .../java/org/apache/phoenix/util/ScanUtil.java  |  6 +++-
 .../phoenix/compile/QueryCompilerTest.java      | 36 ++++++++++++++------
 .../expression/ArrayToStringFunctionTest.java   |  4 ++-
 4 files changed, 44 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/b329e85b/phoenix-core/src/main/java/org/apache/phoenix/execute/ScanPlan.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/execute/ScanPlan.java b/phoenix-core/src/main/java/org/apache/phoenix/execute/ScanPlan.java
index fd9f8ad..e9b8a3a 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/execute/ScanPlan.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/execute/ScanPlan.java
@@ -185,9 +185,19 @@ public class ScanPlan extends BaseQueryPlan {
         if (isOrdered) {
             scanner = new MergeSortTopNResultIterator(iterators, limit, orderBy.getOrderByExpressions());
         } else {
-            if ((isSalted || table.getIndexType() == IndexType.LOCAL) && ScanUtil.forceRowKeyOrder(context)) { 
+            if ((isSalted || table.getIndexType() == IndexType.LOCAL) && ScanUtil.shouldRowsBeInRowKeyOrder(orderBy, context)) {
+                /*
+                 * For salted tables or local index, a merge sort is needed if: 
+                 * 1) The config phoenix.query.force.rowkeyorder is set to true 
+                 * 2) Or if the query has an order by that wants to sort
+                 * the results by the row key (forward or reverse ordering)
+                 */
                 scanner = new MergeSortRowKeyResultIterator(iterators, isSalted ? SaltingUtil.NUM_SALTING_BYTES : 0, orderBy == OrderBy.REV_ROW_KEY_ORDER_BY);
             } else if (useRoundRobinIterator()) {
+                /*
+                 * For any kind of tables, round robin is possible if there is
+                 * no ordering of rows needed.
+                 */
                 scanner = new RoundRobinResultIterator(iterators, this);
             } else {
                 scanner = new ConcatResultIterator(iterators);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b329e85b/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 d63edbb..9d104ca 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
@@ -677,11 +677,15 @@ public class ScanUtil {
          * not even row key order. Also no point doing round robin of scanners if fetch size
          * is 1.
          */
-        return fetchSize > 1 && !forceRowKeyOrder(context) && (orderBy.getOrderByExpressions().isEmpty() && orderBy != FWD_ROW_KEY_ORDER_BY && orderBy != REV_ROW_KEY_ORDER_BY);
+        return fetchSize > 1 && !shouldRowsBeInRowKeyOrder(orderBy, context) && orderBy.getOrderByExpressions().isEmpty();
     }
     
     public static boolean forceRowKeyOrder(StatementContext context) {
         return context.getConnection().getQueryServices().getProps()
                 .getBoolean(QueryServices.FORCE_ROW_KEY_ORDER_ATTRIB, QueryServicesOptions.DEFAULT_FORCE_ROW_KEY_ORDER);
     }
+    
+    public static boolean shouldRowsBeInRowKeyOrder(OrderBy orderBy, StatementContext context) {
+        return forceRowKeyOrder(context) || orderBy == FWD_ROW_KEY_ORDER_BY || orderBy == REV_ROW_KEY_ORDER_BY;
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b329e85b/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java b/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java
index 0f34582..98b130e 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java
@@ -1855,11 +1855,17 @@ public class QueryCompilerTest extends BaseConnectionlessQueryTest {
         Properties props = new Properties();
         props.setProperty(QueryServices.FORCE_ROW_KEY_ORDER_ATTRIB, Boolean.toString(true));
         Connection conn = DriverManager.getConnection(getUrl(), props);
-        conn.createStatement().execute("CREATE TABLE t (k1 char(2) not null, k2 varchar not null, k3 integer not null, v varchar, constraint pk primary key(k1,k2,k3))");
+        testForceRowKeyOrder(conn, false);
+        testForceRowKeyOrder(conn, true);
+    }
+
+    private void testForceRowKeyOrder(Connection conn, boolean isSalted) throws SQLException {
+        String tableName = "tablename" + (isSalted ? "_salt" : "");
+        conn.createStatement().execute("CREATE TABLE " + tableName + " (k1 char(2) not null, k2 varchar not null, k3 integer not null, v varchar, constraint pk primary key(k1,k2,k3))");
         String[] queries = {
-                "SELECT 1 FROM T ",
-                "SELECT 1 FROM T WHERE V = 'c'",
-                "SELECT 1 FROM T WHERE (k1, k2, k3) > ('a', 'ab', 1)",
+                "SELECT 1 FROM  " + tableName ,
+                "SELECT 1 FROM  " + tableName + "  WHERE V = 'c'",
+                "SELECT 1 FROM  " + tableName + "  WHERE (k1, k2, k3) > ('a', 'ab', 1)",
                 };
         String query;
         for (int i = 0; i < queries.length; i++) {
@@ -1874,13 +1880,21 @@ public class QueryCompilerTest extends BaseConnectionlessQueryTest {
         Properties props = new Properties();
         props.setProperty(QueryServices.FORCE_ROW_KEY_ORDER_ATTRIB, Boolean.toString(false));
         Connection conn = DriverManager.getConnection(getUrl(), props);
-        conn.createStatement().execute("CREATE TABLE t (k1 char(2) not null, k2 varchar not null, k3 integer not null, v varchar, constraint pk primary key(k1,k2,k3))");
+        testOrderByOrGroupByDoesntUseRoundRobin(conn, true);
+        testOrderByOrGroupByDoesntUseRoundRobin(conn, false);
+    }
+
+    private void testOrderByOrGroupByDoesntUseRoundRobin(Connection conn, boolean salted) throws SQLException {
+        String tableName = "orderbygroupbytable" + (salted ? "_salt" : ""); 
+        conn.createStatement().execute("CREATE TABLE " + tableName + " (k1 char(2) not null, k2 varchar not null, k3 integer not null, v varchar, constraint pk primary key(k1,k2,k3))");
         String[] queries = {
-                "SELECT 1 FROM T ORDER BY K1",
-                "SELECT 1 FROM T WHERE V = 'c' ORDER BY K1, K2",
-                "SELECT 1 FROM T WHERE (k1,k2, k3) > ('a', 'ab', 1) ORDER BY V",
-                "SELECT 1 FROM T GROUP BY V",
-                "SELECT 1 FROM T GROUP BY K1, V, K2 ORDER BY V",
+                "SELECT 1 FROM  " + tableName + "  ORDER BY K1",
+                "SELECT 1 FROM  " + tableName + "  WHERE V = 'c' ORDER BY K1, K2",
+                "SELECT 1 FROM  " + tableName + "  WHERE V = 'c' ORDER BY K1, K2, K3",
+                "SELECT 1 FROM  " + tableName + "  WHERE V = 'c' ORDER BY K3",
+                "SELECT 1 FROM  " + tableName + "  WHERE (k1,k2, k3) > ('a', 'ab', 1) ORDER BY V",
+                "SELECT 1 FROM  " + tableName + "  GROUP BY V",
+                "SELECT 1 FROM  " + tableName + "  GROUP BY K1, V, K2 ORDER BY V",
                 };
         String query;
         for (int i = 0; i < queries.length; i++) {
@@ -1889,7 +1903,7 @@ public class QueryCompilerTest extends BaseConnectionlessQueryTest {
             assertFalse("Expected plan to not use round robin iterator " + query, plan.useRoundRobinIterator());
         }
     }
-
+    
     @Test
     public void testSelectColumnsInOneFamily() throws Exception {
         Connection conn = DriverManager.getConnection(getUrl());

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b329e85b/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayToStringFunctionTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayToStringFunctionTest.java b/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayToStringFunctionTest.java
index 92eb6b5..d97f117 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayToStringFunctionTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayToStringFunctionTest.java
@@ -30,6 +30,7 @@ import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.phoenix.expression.function.ArrayToStringFunction;
 import org.apache.phoenix.schema.SortOrder;
 import org.apache.phoenix.schema.types.*;
+import org.junit.Ignore;
 import org.junit.Test;
 
 import com.google.common.collect.Lists;
@@ -132,8 +133,9 @@ public class ArrayToStringFunctionTest {
         String expected = "1.1, 2.2, 3.3, 4.4, 5.5";
         test(arr, type, null, null, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC);
     }
-
+    
     @Test
+    @Ignore
     public void testDate() throws SQLException {
         PDataType type = PDateArray.INSTANCE;
         PDataType base = PDate.INSTANCE;


[20/50] [abbrv] phoenix git commit: PHOENIX-2063 Addendum patch to fix connection usage

Posted by ma...@apache.org.
PHOENIX-2063 Addendum patch to fix connection usage


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/997de5e6
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/997de5e6
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/997de5e6

Branch: refs/heads/calcite
Commit: 997de5e6cab988018e723ed29a53da1bae61def4
Parents: 3b1bfa0
Author: James Taylor <jt...@salesforce.com>
Authored: Thu Jul 9 22:55:25 2015 -0700
Committer: James Taylor <jt...@salesforce.com>
Committed: Thu Jul 9 22:55:25 2015 -0700

----------------------------------------------------------------------
 .../phoenix/end2end/RowValueConstructorIT.java  | 28 +++++++++++++++-----
 1 file changed, 21 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/997de5e6/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java
index 0cf5455..b9e50a4 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java
@@ -1426,10 +1426,11 @@ public class RowValueConstructorIT extends BaseClientManagedTimeIT {
 
     @Test
     public void testCountDistinct1() throws Exception {
-        Connection conn = DriverManager.getConnection(getUrl());
+        Connection conn = nextConnection(getUrl());
         String ddl = "CREATE TABLE regions1 (region_name VARCHAR PRIMARY KEY, a INTEGER, b INTEGER)";
         conn.createStatement().execute(ddl);
-        conn.commit();
+        
+        conn = nextConnection(getUrl());
         PreparedStatement stmt = conn.prepareStatement("UPSERT INTO regions1(region_name, a, b) VALUES('a', 6,3)");
         stmt.execute();
         stmt = conn.prepareStatement("UPSERT INTO regions1(region_name, a, b) VALUES('b', 2,4)");
@@ -1437,18 +1438,23 @@ public class RowValueConstructorIT extends BaseClientManagedTimeIT {
         stmt = conn.prepareStatement("UPSERT INTO regions1(region_name, a, b) VALUES('c', 6,3)");
         stmt.execute();
         conn.commit();
+
+        conn = nextConnection(getUrl());
         ResultSet rs;
         rs = conn.createStatement().executeQuery("SELECT COUNT(DISTINCT (a,b)) from regions1");
         assertTrue(rs.next());
         assertEquals(2, rs.getInt(1));
+        conn.close();
     }
 
     @Test
     public void testCountDistinct2() throws Exception {
-        Connection conn = DriverManager.getConnection(getUrl());
+        Connection conn = nextConnection(getUrl());
         String ddl = "CREATE TABLE regions2 (region_name VARCHAR PRIMARY KEY, a VARCHAR, b VARCHAR)";
         conn.createStatement().execute(ddl);
         conn.commit();
+        
+        conn = nextConnection(getUrl());
         PreparedStatement stmt = conn.prepareStatement("UPSERT INTO regions2(region_name, a, b) VALUES('a', 'fooo','abc')");
         stmt.execute();
         stmt = conn.prepareStatement("UPSERT INTO regions2(region_name, a, b) VALUES('b', 'off','bac')");
@@ -1456,6 +1462,8 @@ public class RowValueConstructorIT extends BaseClientManagedTimeIT {
         stmt = conn.prepareStatement("UPSERT INTO regions2(region_name, a, b) VALUES('c', 'fooo', 'abc')");
         stmt.execute();
         conn.commit();
+
+        conn = nextConnection(getUrl());
         ResultSet rs;
         rs = conn.createStatement().executeQuery("SELECT COUNT(DISTINCT (a,b)) from regions2");
         assertTrue(rs.next());
@@ -1464,10 +1472,11 @@ public class RowValueConstructorIT extends BaseClientManagedTimeIT {
 
     @Test
     public void testCountDistinct3() throws Exception {
-        Connection conn = DriverManager.getConnection(getUrl());
+        Connection conn = nextConnection(getUrl());
         String ddl = "CREATE TABLE regions3 (region_name VARCHAR PRIMARY KEY, a Boolean, b Boolean)";
         conn.createStatement().execute(ddl);
-        conn.commit();
+        
+        conn = nextConnection(getUrl());
         PreparedStatement stmt = conn.prepareStatement("UPSERT INTO regions3(region_name, a, b) VALUES('a', true, true)");
         stmt.execute();
         stmt = conn.prepareStatement("UPSERT INTO regions3(region_name, a, b) VALUES('b', true, False)");
@@ -1477,6 +1486,8 @@ public class RowValueConstructorIT extends BaseClientManagedTimeIT {
         stmt = conn.prepareStatement("UPSERT INTO regions3(region_name, a, b) VALUES('d', true, false)");
         stmt.execute();
         conn.commit();
+
+        conn = nextConnection(getUrl());
         ResultSet rs;
         rs = conn.createStatement().executeQuery("SELECT COUNT(DISTINCT (a,b)) from regions3");
         assertTrue(rs.next());
@@ -1485,10 +1496,11 @@ public class RowValueConstructorIT extends BaseClientManagedTimeIT {
 
     @Test
     public void testCountDistinct4() throws Exception {
-        Connection conn = DriverManager.getConnection(getUrl());
+        Connection conn = nextConnection(getUrl());
         String ddl = "CREATE TABLE regions4 (region_name VARCHAR PRIMARY KEY, a VARCHAR, b VARCHAR)";
         conn.createStatement().execute(ddl);
-        conn.commit();
+        
+        conn = nextConnection(getUrl());
         PreparedStatement stmt = conn.prepareStatement("UPSERT INTO regions4(region_name, a, b) VALUES('a', 'fooo','abc')");
         stmt.execute();
         stmt = conn.prepareStatement("UPSERT INTO regions4(region_name, a, b) VALUES('b', 'off','bac')");
@@ -1496,6 +1508,8 @@ public class RowValueConstructorIT extends BaseClientManagedTimeIT {
         stmt = conn.prepareStatement("UPSERT INTO regions4(region_name, a, b) VALUES('c', 'foo', 'abc')");
         stmt.execute();
         conn.commit();
+        
+        conn = nextConnection(getUrl());
         ResultSet rs;
         rs = conn.createStatement().executeQuery("SELECT COUNT(DISTINCT (a,b)) from regions4");
         assertTrue(rs.next());


[22/50] [abbrv] phoenix git commit: PHOENIX-2107 Update JDBC version to 4.5.0

Posted by ma...@apache.org.
PHOENIX-2107 Update JDBC version to 4.5.0


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/81b3cb35
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/81b3cb35
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/81b3cb35

Branch: refs/heads/calcite
Commit: 81b3cb35679adf7772266f2aed9456273a8b0012
Parents: 39afa9f
Author: Samarth <sa...@salesforce.com>
Authored: Fri Jul 10 11:15:16 2015 -0700
Committer: Samarth <sa...@salesforce.com>
Committed: Fri Jul 10 11:15:16 2015 -0700

----------------------------------------------------------------------
 .../main/java/org/apache/phoenix/coprocessor/MetaDataProtocol.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/81b3cb35/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataProtocol.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataProtocol.java b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataProtocol.java
index 9009e7c..013f7a6 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataProtocol.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataProtocol.java
@@ -55,7 +55,7 @@ import com.google.protobuf.ByteString;
  */
 public abstract class MetaDataProtocol extends MetaDataService {
     public static final int PHOENIX_MAJOR_VERSION = 4;
-    public static final int PHOENIX_MINOR_VERSION = 4;
+    public static final int PHOENIX_MINOR_VERSION = 5;
     public static final int PHOENIX_PATCH_NUMBER = 0;
     public static final int PHOENIX_VERSION =
             VersionUtil.encodeVersion(PHOENIX_MAJOR_VERSION, PHOENIX_MINOR_VERSION, PHOENIX_PATCH_NUMBER);


[15/50] [abbrv] phoenix git commit: PHOENIX-2063 Row value constructor doesn't work when used in COUNT DISTINCT (Dumindu Buddhika)

Posted by ma...@apache.org.
PHOENIX-2063 Row value constructor doesn't work when used in COUNT
DISTINCT (Dumindu Buddhika)


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/fa2d79a0
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/fa2d79a0
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/fa2d79a0

Branch: refs/heads/calcite
Commit: fa2d79a0162497761dd4568e3f8f343063b6afaa
Parents: aa4c900
Author: ramkrishna <ra...@gmail.com>
Authored: Thu Jul 9 10:18:10 2015 +0530
Committer: ramkrishna <ra...@gmail.com>
Committed: Thu Jul 9 10:18:10 2015 +0530

----------------------------------------------------------------------
 .../phoenix/end2end/RowValueConstructorIT.java  | 78 ++++++++++++++++++++
 .../aggregator/ServerAggregators.java           |  1 +
 2 files changed, 79 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/fa2d79a0/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java
index e227eb0..0cf5455 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java
@@ -1423,4 +1423,82 @@ public class RowValueConstructorIT extends BaseClientManagedTimeIT {
         assertFalse(rs.next());
         conn.close();
     }
+
+    @Test
+    public void testCountDistinct1() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        String ddl = "CREATE TABLE regions1 (region_name VARCHAR PRIMARY KEY, a INTEGER, b INTEGER)";
+        conn.createStatement().execute(ddl);
+        conn.commit();
+        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO regions1(region_name, a, b) VALUES('a', 6,3)");
+        stmt.execute();
+        stmt = conn.prepareStatement("UPSERT INTO regions1(region_name, a, b) VALUES('b', 2,4)");
+        stmt.execute();
+        stmt = conn.prepareStatement("UPSERT INTO regions1(region_name, a, b) VALUES('c', 6,3)");
+        stmt.execute();
+        conn.commit();
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT COUNT(DISTINCT (a,b)) from regions1");
+        assertTrue(rs.next());
+        assertEquals(2, rs.getInt(1));
+    }
+
+    @Test
+    public void testCountDistinct2() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        String ddl = "CREATE TABLE regions2 (region_name VARCHAR PRIMARY KEY, a VARCHAR, b VARCHAR)";
+        conn.createStatement().execute(ddl);
+        conn.commit();
+        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO regions2(region_name, a, b) VALUES('a', 'fooo','abc')");
+        stmt.execute();
+        stmt = conn.prepareStatement("UPSERT INTO regions2(region_name, a, b) VALUES('b', 'off','bac')");
+        stmt.execute();
+        stmt = conn.prepareStatement("UPSERT INTO regions2(region_name, a, b) VALUES('c', 'fooo', 'abc')");
+        stmt.execute();
+        conn.commit();
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT COUNT(DISTINCT (a,b)) from regions2");
+        assertTrue(rs.next());
+        assertEquals(2, rs.getInt(1));
+    }
+
+    @Test
+    public void testCountDistinct3() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        String ddl = "CREATE TABLE regions3 (region_name VARCHAR PRIMARY KEY, a Boolean, b Boolean)";
+        conn.createStatement().execute(ddl);
+        conn.commit();
+        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO regions3(region_name, a, b) VALUES('a', true, true)");
+        stmt.execute();
+        stmt = conn.prepareStatement("UPSERT INTO regions3(region_name, a, b) VALUES('b', true, False)");
+        stmt.execute();
+        stmt = conn.prepareStatement("UPSERT INTO regions3(region_name, a, b) VALUES('c', true, true)");
+        stmt.execute();
+        stmt = conn.prepareStatement("UPSERT INTO regions3(region_name, a, b) VALUES('d', true, false)");
+        stmt.execute();
+        conn.commit();
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT COUNT(DISTINCT (a,b)) from regions3");
+        assertTrue(rs.next());
+        assertEquals(2, rs.getInt(1));
+    }
+
+    @Test
+    public void testCountDistinct4() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        String ddl = "CREATE TABLE regions4 (region_name VARCHAR PRIMARY KEY, a VARCHAR, b VARCHAR)";
+        conn.createStatement().execute(ddl);
+        conn.commit();
+        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO regions4(region_name, a, b) VALUES('a', 'fooo','abc')");
+        stmt.execute();
+        stmt = conn.prepareStatement("UPSERT INTO regions4(region_name, a, b) VALUES('b', 'off','bac')");
+        stmt.execute();
+        stmt = conn.prepareStatement("UPSERT INTO regions4(region_name, a, b) VALUES('c', 'foo', 'abc')");
+        stmt.execute();
+        conn.commit();
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT COUNT(DISTINCT (a,b)) from regions4");
+        assertTrue(rs.next());
+        assertEquals(3, rs.getInt(1));
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/fa2d79a0/phoenix-core/src/main/java/org/apache/phoenix/expression/aggregator/ServerAggregators.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/aggregator/ServerAggregators.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/aggregator/ServerAggregators.java
index 8792c91..01ca733 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/expression/aggregator/ServerAggregators.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/aggregator/ServerAggregators.java
@@ -59,6 +59,7 @@ public class ServerAggregators extends Aggregators {
             if (expressions[i].evaluate(result, ptr) && ptr.getLength() != 0) {
                 aggregators[i].aggregate(result, ptr);
             }
+            expressions[i].reset();
         }
     }
     


[38/50] [abbrv] phoenix git commit: PHOENIX-2111 Race condition on creation of new view and adding of column to base table

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/phoenix/blob/9f09f1a5/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/MetaDataProtos.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/MetaDataProtos.java b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/MetaDataProtos.java
index acb32d2..a121d28 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/MetaDataProtos.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/MetaDataProtos.java
@@ -1811,6 +1811,16 @@ public final class MetaDataProtos {
      * <code>required int64 clientTimestamp = 5;</code>
      */
     long getClientTimestamp();
+
+    // optional int32 clientVersion = 6;
+    /**
+     * <code>optional int32 clientVersion = 6;</code>
+     */
+    boolean hasClientVersion();
+    /**
+     * <code>optional int32 clientVersion = 6;</code>
+     */
+    int getClientVersion();
   }
   /**
    * Protobuf type {@code GetTableRequest}
@@ -1888,6 +1898,11 @@ public final class MetaDataProtos {
               clientTimestamp_ = input.readInt64();
               break;
             }
+            case 48: {
+              bitField0_ |= 0x00000020;
+              clientVersion_ = input.readInt32();
+              break;
+            }
           }
         }
       } catch (com.google.protobuf.InvalidProtocolBufferException e) {
@@ -2008,12 +2023,29 @@ public final class MetaDataProtos {
       return clientTimestamp_;
     }
 
+    // optional int32 clientVersion = 6;
+    public static final int CLIENTVERSION_FIELD_NUMBER = 6;
+    private int clientVersion_;
+    /**
+     * <code>optional int32 clientVersion = 6;</code>
+     */
+    public boolean hasClientVersion() {
+      return ((bitField0_ & 0x00000020) == 0x00000020);
+    }
+    /**
+     * <code>optional int32 clientVersion = 6;</code>
+     */
+    public int getClientVersion() {
+      return clientVersion_;
+    }
+
     private void initFields() {
       tenantId_ = com.google.protobuf.ByteString.EMPTY;
       schemaName_ = com.google.protobuf.ByteString.EMPTY;
       tableName_ = com.google.protobuf.ByteString.EMPTY;
       tableTimestamp_ = 0L;
       clientTimestamp_ = 0L;
+      clientVersion_ = 0;
     }
     private byte memoizedIsInitialized = -1;
     public final boolean isInitialized() {
@@ -2062,6 +2094,9 @@ public final class MetaDataProtos {
       if (((bitField0_ & 0x00000010) == 0x00000010)) {
         output.writeInt64(5, clientTimestamp_);
       }
+      if (((bitField0_ & 0x00000020) == 0x00000020)) {
+        output.writeInt32(6, clientVersion_);
+      }
       getUnknownFields().writeTo(output);
     }
 
@@ -2091,6 +2126,10 @@ public final class MetaDataProtos {
         size += com.google.protobuf.CodedOutputStream
           .computeInt64Size(5, clientTimestamp_);
       }
+      if (((bitField0_ & 0x00000020) == 0x00000020)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(6, clientVersion_);
+      }
       size += getUnknownFields().getSerializedSize();
       memoizedSerializedSize = size;
       return size;
@@ -2139,6 +2178,11 @@ public final class MetaDataProtos {
         result = result && (getClientTimestamp()
             == other.getClientTimestamp());
       }
+      result = result && (hasClientVersion() == other.hasClientVersion());
+      if (hasClientVersion()) {
+        result = result && (getClientVersion()
+            == other.getClientVersion());
+      }
       result = result &&
           getUnknownFields().equals(other.getUnknownFields());
       return result;
@@ -2172,6 +2216,10 @@ public final class MetaDataProtos {
         hash = (37 * hash) + CLIENTTIMESTAMP_FIELD_NUMBER;
         hash = (53 * hash) + hashLong(getClientTimestamp());
       }
+      if (hasClientVersion()) {
+        hash = (37 * hash) + CLIENTVERSION_FIELD_NUMBER;
+        hash = (53 * hash) + getClientVersion();
+      }
       hash = (29 * hash) + getUnknownFields().hashCode();
       memoizedHashCode = hash;
       return hash;
@@ -2291,6 +2339,8 @@ public final class MetaDataProtos {
         bitField0_ = (bitField0_ & ~0x00000008);
         clientTimestamp_ = 0L;
         bitField0_ = (bitField0_ & ~0x00000010);
+        clientVersion_ = 0;
+        bitField0_ = (bitField0_ & ~0x00000020);
         return this;
       }
 
@@ -2339,6 +2389,10 @@ public final class MetaDataProtos {
           to_bitField0_ |= 0x00000010;
         }
         result.clientTimestamp_ = clientTimestamp_;
+        if (((from_bitField0_ & 0x00000020) == 0x00000020)) {
+          to_bitField0_ |= 0x00000020;
+        }
+        result.clientVersion_ = clientVersion_;
         result.bitField0_ = to_bitField0_;
         onBuilt();
         return result;
@@ -2370,6 +2424,9 @@ public final class MetaDataProtos {
         if (other.hasClientTimestamp()) {
           setClientTimestamp(other.getClientTimestamp());
         }
+        if (other.hasClientVersion()) {
+          setClientVersion(other.getClientVersion());
+        }
         this.mergeUnknownFields(other.getUnknownFields());
         return this;
       }
@@ -2591,6 +2648,39 @@ public final class MetaDataProtos {
         return this;
       }
 
+      // optional int32 clientVersion = 6;
+      private int clientVersion_ ;
+      /**
+       * <code>optional int32 clientVersion = 6;</code>
+       */
+      public boolean hasClientVersion() {
+        return ((bitField0_ & 0x00000020) == 0x00000020);
+      }
+      /**
+       * <code>optional int32 clientVersion = 6;</code>
+       */
+      public int getClientVersion() {
+        return clientVersion_;
+      }
+      /**
+       * <code>optional int32 clientVersion = 6;</code>
+       */
+      public Builder setClientVersion(int value) {
+        bitField0_ |= 0x00000020;
+        clientVersion_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>optional int32 clientVersion = 6;</code>
+       */
+      public Builder clearClientVersion() {
+        bitField0_ = (bitField0_ & ~0x00000020);
+        clientVersion_ = 0;
+        onChanged();
+        return this;
+      }
+
       // @@protoc_insertion_point(builder_scope:GetTableRequest)
     }
 
@@ -2652,6 +2742,16 @@ public final class MetaDataProtos {
      * <code>required int64 clientTimestamp = 4;</code>
      */
     long getClientTimestamp();
+
+    // optional int32 clientVersion = 5;
+    /**
+     * <code>optional int32 clientVersion = 5;</code>
+     */
+    boolean hasClientVersion();
+    /**
+     * <code>optional int32 clientVersion = 5;</code>
+     */
+    int getClientVersion();
   }
   /**
    * Protobuf type {@code GetFunctionsRequest}
@@ -2743,6 +2843,11 @@ public final class MetaDataProtos {
               clientTimestamp_ = input.readInt64();
               break;
             }
+            case 40: {
+              bitField0_ |= 0x00000004;
+              clientVersion_ = input.readInt32();
+              break;
+            }
           }
         }
       } catch (com.google.protobuf.InvalidProtocolBufferException e) {
@@ -2867,11 +2972,28 @@ public final class MetaDataProtos {
       return clientTimestamp_;
     }
 
+    // optional int32 clientVersion = 5;
+    public static final int CLIENTVERSION_FIELD_NUMBER = 5;
+    private int clientVersion_;
+    /**
+     * <code>optional int32 clientVersion = 5;</code>
+     */
+    public boolean hasClientVersion() {
+      return ((bitField0_ & 0x00000004) == 0x00000004);
+    }
+    /**
+     * <code>optional int32 clientVersion = 5;</code>
+     */
+    public int getClientVersion() {
+      return clientVersion_;
+    }
+
     private void initFields() {
       tenantId_ = com.google.protobuf.ByteString.EMPTY;
       functionNames_ = java.util.Collections.emptyList();
       functionTimestamps_ = java.util.Collections.emptyList();
       clientTimestamp_ = 0L;
+      clientVersion_ = 0;
     }
     private byte memoizedIsInitialized = -1;
     public final boolean isInitialized() {
@@ -2905,6 +3027,9 @@ public final class MetaDataProtos {
       if (((bitField0_ & 0x00000002) == 0x00000002)) {
         output.writeInt64(4, clientTimestamp_);
       }
+      if (((bitField0_ & 0x00000004) == 0x00000004)) {
+        output.writeInt32(5, clientVersion_);
+      }
       getUnknownFields().writeTo(output);
     }
 
@@ -2940,6 +3065,10 @@ public final class MetaDataProtos {
         size += com.google.protobuf.CodedOutputStream
           .computeInt64Size(4, clientTimestamp_);
       }
+      if (((bitField0_ & 0x00000004) == 0x00000004)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(5, clientVersion_);
+      }
       size += getUnknownFields().getSerializedSize();
       memoizedSerializedSize = size;
       return size;
@@ -2977,6 +3106,11 @@ public final class MetaDataProtos {
         result = result && (getClientTimestamp()
             == other.getClientTimestamp());
       }
+      result = result && (hasClientVersion() == other.hasClientVersion());
+      if (hasClientVersion()) {
+        result = result && (getClientVersion()
+            == other.getClientVersion());
+      }
       result = result &&
           getUnknownFields().equals(other.getUnknownFields());
       return result;
@@ -3006,6 +3140,10 @@ public final class MetaDataProtos {
         hash = (37 * hash) + CLIENTTIMESTAMP_FIELD_NUMBER;
         hash = (53 * hash) + hashLong(getClientTimestamp());
       }
+      if (hasClientVersion()) {
+        hash = (37 * hash) + CLIENTVERSION_FIELD_NUMBER;
+        hash = (53 * hash) + getClientVersion();
+      }
       hash = (29 * hash) + getUnknownFields().hashCode();
       memoizedHashCode = hash;
       return hash;
@@ -3123,6 +3261,8 @@ public final class MetaDataProtos {
         bitField0_ = (bitField0_ & ~0x00000004);
         clientTimestamp_ = 0L;
         bitField0_ = (bitField0_ & ~0x00000008);
+        clientVersion_ = 0;
+        bitField0_ = (bitField0_ & ~0x00000010);
         return this;
       }
 
@@ -3169,6 +3309,10 @@ public final class MetaDataProtos {
           to_bitField0_ |= 0x00000002;
         }
         result.clientTimestamp_ = clientTimestamp_;
+        if (((from_bitField0_ & 0x00000010) == 0x00000010)) {
+          to_bitField0_ |= 0x00000004;
+        }
+        result.clientVersion_ = clientVersion_;
         result.bitField0_ = to_bitField0_;
         onBuilt();
         return result;
@@ -3211,6 +3355,9 @@ public final class MetaDataProtos {
         if (other.hasClientTimestamp()) {
           setClientTimestamp(other.getClientTimestamp());
         }
+        if (other.hasClientVersion()) {
+          setClientVersion(other.getClientVersion());
+        }
         this.mergeUnknownFields(other.getUnknownFields());
         return this;
       }
@@ -3453,6 +3600,39 @@ public final class MetaDataProtos {
         return this;
       }
 
+      // optional int32 clientVersion = 5;
+      private int clientVersion_ ;
+      /**
+       * <code>optional int32 clientVersion = 5;</code>
+       */
+      public boolean hasClientVersion() {
+        return ((bitField0_ & 0x00000010) == 0x00000010);
+      }
+      /**
+       * <code>optional int32 clientVersion = 5;</code>
+       */
+      public int getClientVersion() {
+        return clientVersion_;
+      }
+      /**
+       * <code>optional int32 clientVersion = 5;</code>
+       */
+      public Builder setClientVersion(int value) {
+        bitField0_ |= 0x00000010;
+        clientVersion_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>optional int32 clientVersion = 5;</code>
+       */
+      public Builder clearClientVersion() {
+        bitField0_ = (bitField0_ & ~0x00000010);
+        clientVersion_ = 0;
+        onChanged();
+        return this;
+      }
+
       // @@protoc_insertion_point(builder_scope:GetFunctionsRequest)
     }
 
@@ -3480,6 +3660,16 @@ public final class MetaDataProtos {
      * <code>repeated bytes tableMetadataMutations = 1;</code>
      */
     com.google.protobuf.ByteString getTableMetadataMutations(int index);
+
+    // optional int32 clientVersion = 2;
+    /**
+     * <code>optional int32 clientVersion = 2;</code>
+     */
+    boolean hasClientVersion();
+    /**
+     * <code>optional int32 clientVersion = 2;</code>
+     */
+    int getClientVersion();
   }
   /**
    * Protobuf type {@code CreateTableRequest}
@@ -3544,6 +3734,11 @@ public final class MetaDataProtos {
               tableMetadataMutations_.add(input.readBytes());
               break;
             }
+            case 16: {
+              bitField0_ |= 0x00000001;
+              clientVersion_ = input.readInt32();
+              break;
+            }
           }
         }
       } catch (com.google.protobuf.InvalidProtocolBufferException e) {
@@ -3586,6 +3781,7 @@ public final class MetaDataProtos {
       return PARSER;
     }
 
+    private int bitField0_;
     // repeated bytes tableMetadataMutations = 1;
     public static final int TABLEMETADATAMUTATIONS_FIELD_NUMBER = 1;
     private java.util.List<com.google.protobuf.ByteString> tableMetadataMutations_;
@@ -3609,8 +3805,25 @@ public final class MetaDataProtos {
       return tableMetadataMutations_.get(index);
     }
 
+    // optional int32 clientVersion = 2;
+    public static final int CLIENTVERSION_FIELD_NUMBER = 2;
+    private int clientVersion_;
+    /**
+     * <code>optional int32 clientVersion = 2;</code>
+     */
+    public boolean hasClientVersion() {
+      return ((bitField0_ & 0x00000001) == 0x00000001);
+    }
+    /**
+     * <code>optional int32 clientVersion = 2;</code>
+     */
+    public int getClientVersion() {
+      return clientVersion_;
+    }
+
     private void initFields() {
       tableMetadataMutations_ = java.util.Collections.emptyList();
+      clientVersion_ = 0;
     }
     private byte memoizedIsInitialized = -1;
     public final boolean isInitialized() {
@@ -3627,6 +3840,9 @@ public final class MetaDataProtos {
       for (int i = 0; i < tableMetadataMutations_.size(); i++) {
         output.writeBytes(1, tableMetadataMutations_.get(i));
       }
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        output.writeInt32(2, clientVersion_);
+      }
       getUnknownFields().writeTo(output);
     }
 
@@ -3645,6 +3861,10 @@ public final class MetaDataProtos {
         size += dataSize;
         size += 1 * getTableMetadataMutationsList().size();
       }
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(2, clientVersion_);
+      }
       size += getUnknownFields().getSerializedSize();
       memoizedSerializedSize = size;
       return size;
@@ -3670,6 +3890,11 @@ public final class MetaDataProtos {
       boolean result = true;
       result = result && getTableMetadataMutationsList()
           .equals(other.getTableMetadataMutationsList());
+      result = result && (hasClientVersion() == other.hasClientVersion());
+      if (hasClientVersion()) {
+        result = result && (getClientVersion()
+            == other.getClientVersion());
+      }
       result = result &&
           getUnknownFields().equals(other.getUnknownFields());
       return result;
@@ -3687,6 +3912,10 @@ public final class MetaDataProtos {
         hash = (37 * hash) + TABLEMETADATAMUTATIONS_FIELD_NUMBER;
         hash = (53 * hash) + getTableMetadataMutationsList().hashCode();
       }
+      if (hasClientVersion()) {
+        hash = (37 * hash) + CLIENTVERSION_FIELD_NUMBER;
+        hash = (53 * hash) + getClientVersion();
+      }
       hash = (29 * hash) + getUnknownFields().hashCode();
       memoizedHashCode = hash;
       return hash;
@@ -3802,6 +4031,8 @@ public final class MetaDataProtos {
         super.clear();
         tableMetadataMutations_ = java.util.Collections.emptyList();
         bitField0_ = (bitField0_ & ~0x00000001);
+        clientVersion_ = 0;
+        bitField0_ = (bitField0_ & ~0x00000002);
         return this;
       }
 
@@ -3829,11 +4060,17 @@ public final class MetaDataProtos {
       public org.apache.phoenix.coprocessor.generated.MetaDataProtos.CreateTableRequest buildPartial() {
         org.apache.phoenix.coprocessor.generated.MetaDataProtos.CreateTableRequest result = new org.apache.phoenix.coprocessor.generated.MetaDataProtos.CreateTableRequest(this);
         int from_bitField0_ = bitField0_;
+        int to_bitField0_ = 0;
         if (((bitField0_ & 0x00000001) == 0x00000001)) {
           tableMetadataMutations_ = java.util.Collections.unmodifiableList(tableMetadataMutations_);
           bitField0_ = (bitField0_ & ~0x00000001);
         }
         result.tableMetadataMutations_ = tableMetadataMutations_;
+        if (((from_bitField0_ & 0x00000002) == 0x00000002)) {
+          to_bitField0_ |= 0x00000001;
+        }
+        result.clientVersion_ = clientVersion_;
+        result.bitField0_ = to_bitField0_;
         onBuilt();
         return result;
       }
@@ -3859,6 +4096,9 @@ public final class MetaDataProtos {
           }
           onChanged();
         }
+        if (other.hasClientVersion()) {
+          setClientVersion(other.getClientVersion());
+        }
         this.mergeUnknownFields(other.getUnknownFields());
         return this;
       }
@@ -3958,6 +4198,39 @@ public final class MetaDataProtos {
         return this;
       }
 
+      // optional int32 clientVersion = 2;
+      private int clientVersion_ ;
+      /**
+       * <code>optional int32 clientVersion = 2;</code>
+       */
+      public boolean hasClientVersion() {
+        return ((bitField0_ & 0x00000002) == 0x00000002);
+      }
+      /**
+       * <code>optional int32 clientVersion = 2;</code>
+       */
+      public int getClientVersion() {
+        return clientVersion_;
+      }
+      /**
+       * <code>optional int32 clientVersion = 2;</code>
+       */
+      public Builder setClientVersion(int value) {
+        bitField0_ |= 0x00000002;
+        clientVersion_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>optional int32 clientVersion = 2;</code>
+       */
+      public Builder clearClientVersion() {
+        bitField0_ = (bitField0_ & ~0x00000002);
+        clientVersion_ = 0;
+        onChanged();
+        return this;
+      }
+
       // @@protoc_insertion_point(builder_scope:CreateTableRequest)
     }
 
@@ -4005,6 +4278,16 @@ public final class MetaDataProtos {
      * <code>optional bool replace = 3;</code>
      */
     boolean getReplace();
+
+    // optional int32 clientVersion = 4;
+    /**
+     * <code>optional int32 clientVersion = 4;</code>
+     */
+    boolean hasClientVersion();
+    /**
+     * <code>optional int32 clientVersion = 4;</code>
+     */
+    int getClientVersion();
   }
   /**
    * Protobuf type {@code CreateFunctionRequest}
@@ -4079,6 +4362,11 @@ public final class MetaDataProtos {
               replace_ = input.readBool();
               break;
             }
+            case 32: {
+              bitField0_ |= 0x00000004;
+              clientVersion_ = input.readInt32();
+              break;
+            }
           }
         }
       } catch (com.google.protobuf.InvalidProtocolBufferException e) {
@@ -4177,10 +4465,27 @@ public final class MetaDataProtos {
       return replace_;
     }
 
+    // optional int32 clientVersion = 4;
+    public static final int CLIENTVERSION_FIELD_NUMBER = 4;
+    private int clientVersion_;
+    /**
+     * <code>optional int32 clientVersion = 4;</code>
+     */
+    public boolean hasClientVersion() {
+      return ((bitField0_ & 0x00000004) == 0x00000004);
+    }
+    /**
+     * <code>optional int32 clientVersion = 4;</code>
+     */
+    public int getClientVersion() {
+      return clientVersion_;
+    }
+
     private void initFields() {
       tableMetadataMutations_ = java.util.Collections.emptyList();
       temporary_ = false;
       replace_ = false;
+      clientVersion_ = 0;
     }
     private byte memoizedIsInitialized = -1;
     public final boolean isInitialized() {
@@ -4207,6 +4512,9 @@ public final class MetaDataProtos {
       if (((bitField0_ & 0x00000002) == 0x00000002)) {
         output.writeBool(3, replace_);
       }
+      if (((bitField0_ & 0x00000004) == 0x00000004)) {
+        output.writeInt32(4, clientVersion_);
+      }
       getUnknownFields().writeTo(output);
     }
 
@@ -4233,6 +4541,10 @@ public final class MetaDataProtos {
         size += com.google.protobuf.CodedOutputStream
           .computeBoolSize(3, replace_);
       }
+      if (((bitField0_ & 0x00000004) == 0x00000004)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(4, clientVersion_);
+      }
       size += getUnknownFields().getSerializedSize();
       memoizedSerializedSize = size;
       return size;
@@ -4268,6 +4580,11 @@ public final class MetaDataProtos {
         result = result && (getReplace()
             == other.getReplace());
       }
+      result = result && (hasClientVersion() == other.hasClientVersion());
+      if (hasClientVersion()) {
+        result = result && (getClientVersion()
+            == other.getClientVersion());
+      }
       result = result &&
           getUnknownFields().equals(other.getUnknownFields());
       return result;
@@ -4293,6 +4610,10 @@ public final class MetaDataProtos {
         hash = (37 * hash) + REPLACE_FIELD_NUMBER;
         hash = (53 * hash) + hashBoolean(getReplace());
       }
+      if (hasClientVersion()) {
+        hash = (37 * hash) + CLIENTVERSION_FIELD_NUMBER;
+        hash = (53 * hash) + getClientVersion();
+      }
       hash = (29 * hash) + getUnknownFields().hashCode();
       memoizedHashCode = hash;
       return hash;
@@ -4412,6 +4733,8 @@ public final class MetaDataProtos {
         bitField0_ = (bitField0_ & ~0x00000002);
         replace_ = false;
         bitField0_ = (bitField0_ & ~0x00000004);
+        clientVersion_ = 0;
+        bitField0_ = (bitField0_ & ~0x00000008);
         return this;
       }
 
@@ -4453,6 +4776,10 @@ public final class MetaDataProtos {
           to_bitField0_ |= 0x00000002;
         }
         result.replace_ = replace_;
+        if (((from_bitField0_ & 0x00000008) == 0x00000008)) {
+          to_bitField0_ |= 0x00000004;
+        }
+        result.clientVersion_ = clientVersion_;
         result.bitField0_ = to_bitField0_;
         onBuilt();
         return result;
@@ -4485,6 +4812,9 @@ public final class MetaDataProtos {
         if (other.hasReplace()) {
           setReplace(other.getReplace());
         }
+        if (other.hasClientVersion()) {
+          setClientVersion(other.getClientVersion());
+        }
         this.mergeUnknownFields(other.getUnknownFields());
         return this;
       }
@@ -4654,6 +4984,39 @@ public final class MetaDataProtos {
         return this;
       }
 
+      // optional int32 clientVersion = 4;
+      private int clientVersion_ ;
+      /**
+       * <code>optional int32 clientVersion = 4;</code>
+       */
+      public boolean hasClientVersion() {
+        return ((bitField0_ & 0x00000008) == 0x00000008);
+      }
+      /**
+       * <code>optional int32 clientVersion = 4;</code>
+       */
+      public int getClientVersion() {
+        return clientVersion_;
+      }
+      /**
+       * <code>optional int32 clientVersion = 4;</code>
+       */
+      public Builder setClientVersion(int value) {
+        bitField0_ |= 0x00000008;
+        clientVersion_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>optional int32 clientVersion = 4;</code>
+       */
+      public Builder clearClientVersion() {
+        bitField0_ = (bitField0_ & ~0x00000008);
+        clientVersion_ = 0;
+        onChanged();
+        return this;
+      }
+
       // @@protoc_insertion_point(builder_scope:CreateFunctionRequest)
     }
 
@@ -4706,6 +5069,16 @@ public final class MetaDataProtos {
      * <code>optional bool cascade = 3;</code>
      */
     boolean getCascade();
+
+    // optional int32 clientVersion = 4;
+    /**
+     * <code>optional int32 clientVersion = 4;</code>
+     */
+    boolean hasClientVersion();
+    /**
+     * <code>optional int32 clientVersion = 4;</code>
+     */
+    int getClientVersion();
   }
   /**
    * Protobuf type {@code DropTableRequest}
@@ -4776,6 +5149,11 @@ public final class MetaDataProtos {
               cascade_ = input.readBool();
               break;
             }
+            case 32: {
+              bitField0_ |= 0x00000004;
+              clientVersion_ = input.readInt32();
+              break;
+            }
           }
         }
       } catch (com.google.protobuf.InvalidProtocolBufferException e) {
@@ -4901,10 +5279,27 @@ public final class MetaDataProtos {
       return cascade_;
     }
 
+    // optional int32 clientVersion = 4;
+    public static final int CLIENTVERSION_FIELD_NUMBER = 4;
+    private int clientVersion_;
+    /**
+     * <code>optional int32 clientVersion = 4;</code>
+     */
+    public boolean hasClientVersion() {
+      return ((bitField0_ & 0x00000004) == 0x00000004);
+    }
+    /**
+     * <code>optional int32 clientVersion = 4;</code>
+     */
+    public int getClientVersion() {
+      return clientVersion_;
+    }
+
     private void initFields() {
       tableMetadataMutations_ = java.util.Collections.emptyList();
       tableType_ = "";
       cascade_ = false;
+      clientVersion_ = 0;
     }
     private byte memoizedIsInitialized = -1;
     public final boolean isInitialized() {
@@ -4931,6 +5326,9 @@ public final class MetaDataProtos {
       if (((bitField0_ & 0x00000002) == 0x00000002)) {
         output.writeBool(3, cascade_);
       }
+      if (((bitField0_ & 0x00000004) == 0x00000004)) {
+        output.writeInt32(4, clientVersion_);
+      }
       getUnknownFields().writeTo(output);
     }
 
@@ -4957,6 +5355,10 @@ public final class MetaDataProtos {
         size += com.google.protobuf.CodedOutputStream
           .computeBoolSize(3, cascade_);
       }
+      if (((bitField0_ & 0x00000004) == 0x00000004)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(4, clientVersion_);
+      }
       size += getUnknownFields().getSerializedSize();
       memoizedSerializedSize = size;
       return size;
@@ -4992,6 +5394,11 @@ public final class MetaDataProtos {
         result = result && (getCascade()
             == other.getCascade());
       }
+      result = result && (hasClientVersion() == other.hasClientVersion());
+      if (hasClientVersion()) {
+        result = result && (getClientVersion()
+            == other.getClientVersion());
+      }
       result = result &&
           getUnknownFields().equals(other.getUnknownFields());
       return result;
@@ -5017,6 +5424,10 @@ public final class MetaDataProtos {
         hash = (37 * hash) + CASCADE_FIELD_NUMBER;
         hash = (53 * hash) + hashBoolean(getCascade());
       }
+      if (hasClientVersion()) {
+        hash = (37 * hash) + CLIENTVERSION_FIELD_NUMBER;
+        hash = (53 * hash) + getClientVersion();
+      }
       hash = (29 * hash) + getUnknownFields().hashCode();
       memoizedHashCode = hash;
       return hash;
@@ -5132,6 +5543,8 @@ public final class MetaDataProtos {
         bitField0_ = (bitField0_ & ~0x00000002);
         cascade_ = false;
         bitField0_ = (bitField0_ & ~0x00000004);
+        clientVersion_ = 0;
+        bitField0_ = (bitField0_ & ~0x00000008);
         return this;
       }
 
@@ -5173,6 +5586,10 @@ public final class MetaDataProtos {
           to_bitField0_ |= 0x00000002;
         }
         result.cascade_ = cascade_;
+        if (((from_bitField0_ & 0x00000008) == 0x00000008)) {
+          to_bitField0_ |= 0x00000004;
+        }
+        result.clientVersion_ = clientVersion_;
         result.bitField0_ = to_bitField0_;
         onBuilt();
         return result;
@@ -5207,6 +5624,9 @@ public final class MetaDataProtos {
         if (other.hasCascade()) {
           setCascade(other.getCascade());
         }
+        if (other.hasClientVersion()) {
+          setClientVersion(other.getClientVersion());
+        }
         this.mergeUnknownFields(other.getUnknownFields());
         return this;
       }
@@ -5417,6 +5837,39 @@ public final class MetaDataProtos {
         return this;
       }
 
+      // optional int32 clientVersion = 4;
+      private int clientVersion_ ;
+      /**
+       * <code>optional int32 clientVersion = 4;</code>
+       */
+      public boolean hasClientVersion() {
+        return ((bitField0_ & 0x00000008) == 0x00000008);
+      }
+      /**
+       * <code>optional int32 clientVersion = 4;</code>
+       */
+      public int getClientVersion() {
+        return clientVersion_;
+      }
+      /**
+       * <code>optional int32 clientVersion = 4;</code>
+       */
+      public Builder setClientVersion(int value) {
+        bitField0_ |= 0x00000008;
+        clientVersion_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>optional int32 clientVersion = 4;</code>
+       */
+      public Builder clearClientVersion() {
+        bitField0_ = (bitField0_ & ~0x00000008);
+        clientVersion_ = 0;
+        onChanged();
+        return this;
+      }
+
       // @@protoc_insertion_point(builder_scope:DropTableRequest)
     }
 
@@ -5444,6 +5897,16 @@ public final class MetaDataProtos {
      * <code>repeated bytes tableMetadataMutations = 1;</code>
      */
     com.google.protobuf.ByteString getTableMetadataMutations(int index);
+
+    // optional int32 clientVersion = 2;
+    /**
+     * <code>optional int32 clientVersion = 2;</code>
+     */
+    boolean hasClientVersion();
+    /**
+     * <code>optional int32 clientVersion = 2;</code>
+     */
+    int getClientVersion();
   }
   /**
    * Protobuf type {@code AddColumnRequest}
@@ -5504,6 +5967,11 @@ public final class MetaDataProtos {
               tableMetadataMutations_.add(input.readBytes());
               break;
             }
+            case 16: {
+              bitField0_ |= 0x00000001;
+              clientVersion_ = input.readInt32();
+              break;
+            }
           }
         }
       } catch (com.google.protobuf.InvalidProtocolBufferException e) {
@@ -5546,6 +6014,7 @@ public final class MetaDataProtos {
       return PARSER;
     }
 
+    private int bitField0_;
     // repeated bytes tableMetadataMutations = 1;
     public static final int TABLEMETADATAMUTATIONS_FIELD_NUMBER = 1;
     private java.util.List<com.google.protobuf.ByteString> tableMetadataMutations_;
@@ -5569,8 +6038,25 @@ public final class MetaDataProtos {
       return tableMetadataMutations_.get(index);
     }
 
+    // optional int32 clientVersion = 2;
+    public static final int CLIENTVERSION_FIELD_NUMBER = 2;
+    private int clientVersion_;
+    /**
+     * <code>optional int32 clientVersion = 2;</code>
+     */
+    public boolean hasClientVersion() {
+      return ((bitField0_ & 0x00000001) == 0x00000001);
+    }
+    /**
+     * <code>optional int32 clientVersion = 2;</code>
+     */
+    public int getClientVersion() {
+      return clientVersion_;
+    }
+
     private void initFields() {
       tableMetadataMutations_ = java.util.Collections.emptyList();
+      clientVersion_ = 0;
     }
     private byte memoizedIsInitialized = -1;
     public final boolean isInitialized() {
@@ -5587,6 +6073,9 @@ public final class MetaDataProtos {
       for (int i = 0; i < tableMetadataMutations_.size(); i++) {
         output.writeBytes(1, tableMetadataMutations_.get(i));
       }
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        output.writeInt32(2, clientVersion_);
+      }
       getUnknownFields().writeTo(output);
     }
 
@@ -5605,6 +6094,10 @@ public final class MetaDataProtos {
         size += dataSize;
         size += 1 * getTableMetadataMutationsList().size();
       }
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(2, clientVersion_);
+      }
       size += getUnknownFields().getSerializedSize();
       memoizedSerializedSize = size;
       return size;
@@ -5630,6 +6123,11 @@ public final class MetaDataProtos {
       boolean result = true;
       result = result && getTableMetadataMutationsList()
           .equals(other.getTableMetadataMutationsList());
+      result = result && (hasClientVersion() == other.hasClientVersion());
+      if (hasClientVersion()) {
+        result = result && (getClientVersion()
+            == other.getClientVersion());
+      }
       result = result &&
           getUnknownFields().equals(other.getUnknownFields());
       return result;
@@ -5647,6 +6145,10 @@ public final class MetaDataProtos {
         hash = (37 * hash) + TABLEMETADATAMUTATIONS_FIELD_NUMBER;
         hash = (53 * hash) + getTableMetadataMutationsList().hashCode();
       }
+      if (hasClientVersion()) {
+        hash = (37 * hash) + CLIENTVERSION_FIELD_NUMBER;
+        hash = (53 * hash) + getClientVersion();
+      }
       hash = (29 * hash) + getUnknownFields().hashCode();
       memoizedHashCode = hash;
       return hash;
@@ -5758,6 +6260,8 @@ public final class MetaDataProtos {
         super.clear();
         tableMetadataMutations_ = java.util.Collections.emptyList();
         bitField0_ = (bitField0_ & ~0x00000001);
+        clientVersion_ = 0;
+        bitField0_ = (bitField0_ & ~0x00000002);
         return this;
       }
 
@@ -5785,11 +6289,17 @@ public final class MetaDataProtos {
       public org.apache.phoenix.coprocessor.generated.MetaDataProtos.AddColumnRequest buildPartial() {
         org.apache.phoenix.coprocessor.generated.MetaDataProtos.AddColumnRequest result = new org.apache.phoenix.coprocessor.generated.MetaDataProtos.AddColumnRequest(this);
         int from_bitField0_ = bitField0_;
+        int to_bitField0_ = 0;
         if (((bitField0_ & 0x00000001) == 0x00000001)) {
           tableMetadataMutations_ = java.util.Collections.unmodifiableList(tableMetadataMutations_);
           bitField0_ = (bitField0_ & ~0x00000001);
         }
         result.tableMetadataMutations_ = tableMetadataMutations_;
+        if (((from_bitField0_ & 0x00000002) == 0x00000002)) {
+          to_bitField0_ |= 0x00000001;
+        }
+        result.clientVersion_ = clientVersion_;
+        result.bitField0_ = to_bitField0_;
         onBuilt();
         return result;
       }
@@ -5815,6 +6325,9 @@ public final class MetaDataProtos {
           }
           onChanged();
         }
+        if (other.hasClientVersion()) {
+          setClientVersion(other.getClientVersion());
+        }
         this.mergeUnknownFields(other.getUnknownFields());
         return this;
       }
@@ -5914,6 +6427,39 @@ public final class MetaDataProtos {
         return this;
       }
 
+      // optional int32 clientVersion = 2;
+      private int clientVersion_ ;
+      /**
+       * <code>optional int32 clientVersion = 2;</code>
+       */
+      public boolean hasClientVersion() {
+        return ((bitField0_ & 0x00000002) == 0x00000002);
+      }
+      /**
+       * <code>optional int32 clientVersion = 2;</code>
+       */
+      public int getClientVersion() {
+        return clientVersion_;
+      }
+      /**
+       * <code>optional int32 clientVersion = 2;</code>
+       */
+      public Builder setClientVersion(int value) {
+        bitField0_ |= 0x00000002;
+        clientVersion_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>optional int32 clientVersion = 2;</code>
+       */
+      public Builder clearClientVersion() {
+        bitField0_ = (bitField0_ & ~0x00000002);
+        clientVersion_ = 0;
+        onChanged();
+        return this;
+      }
+
       // @@protoc_insertion_point(builder_scope:AddColumnRequest)
     }
 
@@ -5941,6 +6487,16 @@ public final class MetaDataProtos {
      * <code>repeated bytes tableMetadataMutations = 1;</code>
      */
     com.google.protobuf.ByteString getTableMetadataMutations(int index);
+
+    // optional int32 clientVersion = 2;
+    /**
+     * <code>optional int32 clientVersion = 2;</code>
+     */
+    boolean hasClientVersion();
+    /**
+     * <code>optional int32 clientVersion = 2;</code>
+     */
+    int getClientVersion();
   }
   /**
    * Protobuf type {@code DropColumnRequest}
@@ -6001,6 +6557,11 @@ public final class MetaDataProtos {
               tableMetadataMutations_.add(input.readBytes());
               break;
             }
+            case 16: {
+              bitField0_ |= 0x00000001;
+              clientVersion_ = input.readInt32();
+              break;
+            }
           }
         }
       } catch (com.google.protobuf.InvalidProtocolBufferException e) {
@@ -6043,6 +6604,7 @@ public final class MetaDataProtos {
       return PARSER;
     }
 
+    private int bitField0_;
     // repeated bytes tableMetadataMutations = 1;
     public static final int TABLEMETADATAMUTATIONS_FIELD_NUMBER = 1;
     private java.util.List<com.google.protobuf.ByteString> tableMetadataMutations_;
@@ -6066,8 +6628,25 @@ public final class MetaDataProtos {
       return tableMetadataMutations_.get(index);
     }
 
+    // optional int32 clientVersion = 2;
+    public static final int CLIENTVERSION_FIELD_NUMBER = 2;
+    private int clientVersion_;
+    /**
+     * <code>optional int32 clientVersion = 2;</code>
+     */
+    public boolean hasClientVersion() {
+      return ((bitField0_ & 0x00000001) == 0x00000001);
+    }
+    /**
+     * <code>optional int32 clientVersion = 2;</code>
+     */
+    public int getClientVersion() {
+      return clientVersion_;
+    }
+
     private void initFields() {
       tableMetadataMutations_ = java.util.Collections.emptyList();
+      clientVersion_ = 0;
     }
     private byte memoizedIsInitialized = -1;
     public final boolean isInitialized() {
@@ -6084,6 +6663,9 @@ public final class MetaDataProtos {
       for (int i = 0; i < tableMetadataMutations_.size(); i++) {
         output.writeBytes(1, tableMetadataMutations_.get(i));
       }
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        output.writeInt32(2, clientVersion_);
+      }
       getUnknownFields().writeTo(output);
     }
 
@@ -6102,6 +6684,10 @@ public final class MetaDataProtos {
         size += dataSize;
         size += 1 * getTableMetadataMutationsList().size();
       }
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(2, clientVersion_);
+      }
       size += getUnknownFields().getSerializedSize();
       memoizedSerializedSize = size;
       return size;
@@ -6127,6 +6713,11 @@ public final class MetaDataProtos {
       boolean result = true;
       result = result && getTableMetadataMutationsList()
           .equals(other.getTableMetadataMutationsList());
+      result = result && (hasClientVersion() == other.hasClientVersion());
+      if (hasClientVersion()) {
+        result = result && (getClientVersion()
+            == other.getClientVersion());
+      }
       result = result &&
           getUnknownFields().equals(other.getUnknownFields());
       return result;
@@ -6144,6 +6735,10 @@ public final class MetaDataProtos {
         hash = (37 * hash) + TABLEMETADATAMUTATIONS_FIELD_NUMBER;
         hash = (53 * hash) + getTableMetadataMutationsList().hashCode();
       }
+      if (hasClientVersion()) {
+        hash = (37 * hash) + CLIENTVERSION_FIELD_NUMBER;
+        hash = (53 * hash) + getClientVersion();
+      }
       hash = (29 * hash) + getUnknownFields().hashCode();
       memoizedHashCode = hash;
       return hash;
@@ -6255,6 +6850,8 @@ public final class MetaDataProtos {
         super.clear();
         tableMetadataMutations_ = java.util.Collections.emptyList();
         bitField0_ = (bitField0_ & ~0x00000001);
+        clientVersion_ = 0;
+        bitField0_ = (bitField0_ & ~0x00000002);
         return this;
       }
 
@@ -6282,11 +6879,17 @@ public final class MetaDataProtos {
       public org.apache.phoenix.coprocessor.generated.MetaDataProtos.DropColumnRequest buildPartial() {
         org.apache.phoenix.coprocessor.generated.MetaDataProtos.DropColumnRequest result = new org.apache.phoenix.coprocessor.generated.MetaDataProtos.DropColumnRequest(this);
         int from_bitField0_ = bitField0_;
+        int to_bitField0_ = 0;
         if (((bitField0_ & 0x00000001) == 0x00000001)) {
           tableMetadataMutations_ = java.util.Collections.unmodifiableList(tableMetadataMutations_);
           bitField0_ = (bitField0_ & ~0x00000001);
         }
         result.tableMetadataMutations_ = tableMetadataMutations_;
+        if (((from_bitField0_ & 0x00000002) == 0x00000002)) {
+          to_bitField0_ |= 0x00000001;
+        }
+        result.clientVersion_ = clientVersion_;
+        result.bitField0_ = to_bitField0_;
         onBuilt();
         return result;
       }
@@ -6312,6 +6915,9 @@ public final class MetaDataProtos {
           }
           onChanged();
         }
+        if (other.hasClientVersion()) {
+          setClientVersion(other.getClientVersion());
+        }
         this.mergeUnknownFields(other.getUnknownFields());
         return this;
       }
@@ -6411,6 +7017,39 @@ public final class MetaDataProtos {
         return this;
       }
 
+      // optional int32 clientVersion = 2;
+      private int clientVersion_ ;
+      /**
+       * <code>optional int32 clientVersion = 2;</code>
+       */
+      public boolean hasClientVersion() {
+        return ((bitField0_ & 0x00000002) == 0x00000002);
+      }
+      /**
+       * <code>optional int32 clientVersion = 2;</code>
+       */
+      public int getClientVersion() {
+        return clientVersion_;
+      }
+      /**
+       * <code>optional int32 clientVersion = 2;</code>
+       */
+      public Builder setClientVersion(int value) {
+        bitField0_ |= 0x00000002;
+        clientVersion_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>optional int32 clientVersion = 2;</code>
+       */
+      public Builder clearClientVersion() {
+        bitField0_ = (bitField0_ & ~0x00000002);
+        clientVersion_ = 0;
+        onChanged();
+        return this;
+      }
+
       // @@protoc_insertion_point(builder_scope:DropColumnRequest)
     }
 
@@ -6448,6 +7087,16 @@ public final class MetaDataProtos {
      * <code>optional bool ifExists = 2;</code>
      */
     boolean getIfExists();
+
+    // optional int32 clientVersion = 3;
+    /**
+     * <code>optional int32 clientVersion = 3;</code>
+     */
+    boolean hasClientVersion();
+    /**
+     * <code>optional int32 clientVersion = 3;</code>
+     */
+    int getClientVersion();
   }
   /**
    * Protobuf type {@code DropFunctionRequest}
@@ -6513,6 +7162,11 @@ public final class MetaDataProtos {
               ifExists_ = input.readBool();
               break;
             }
+            case 24: {
+              bitField0_ |= 0x00000002;
+              clientVersion_ = input.readInt32();
+              break;
+            }
           }
         }
       } catch (com.google.protobuf.InvalidProtocolBufferException e) {
@@ -6595,9 +7249,26 @@ public final class MetaDataProtos {
       return ifExists_;
     }
 
+    // optional int32 clientVersion = 3;
+    public static final int CLIENTVERSION_FIELD_NUMBER = 3;
+    private int clientVersion_;
+    /**
+     * <code>optional int32 clientVersion = 3;</code>
+     */
+    public boolean hasClientVersion() {
+      return ((bitField0_ & 0x00000002) == 0x00000002);
+    }
+    /**
+     * <code>optional int32 clientVersion = 3;</code>
+     */
+    public int getClientVersion() {
+      return clientVersion_;
+    }
+
     private void initFields() {
       tableMetadataMutations_ = java.util.Collections.emptyList();
       ifExists_ = false;
+      clientVersion_ = 0;
     }
     private byte memoizedIsInitialized = -1;
     public final boolean isInitialized() {
@@ -6617,6 +7288,9 @@ public final class MetaDataProtos {
       if (((bitField0_ & 0x00000001) == 0x00000001)) {
         output.writeBool(2, ifExists_);
       }
+      if (((bitField0_ & 0x00000002) == 0x00000002)) {
+        output.writeInt32(3, clientVersion_);
+      }
       getUnknownFields().writeTo(output);
     }
 
@@ -6639,6 +7313,10 @@ public final class MetaDataProtos {
         size += com.google.protobuf.CodedOutputStream
           .computeBoolSize(2, ifExists_);
       }
+      if (((bitField0_ & 0x00000002) == 0x00000002)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(3, clientVersion_);
+      }
       size += getUnknownFields().getSerializedSize();
       memoizedSerializedSize = size;
       return size;
@@ -6669,6 +7347,11 @@ public final class MetaDataProtos {
         result = result && (getIfExists()
             == other.getIfExists());
       }
+      result = result && (hasClientVersion() == other.hasClientVersion());
+      if (hasClientVersion()) {
+        result = result && (getClientVersion()
+            == other.getClientVersion());
+      }
       result = result &&
           getUnknownFields().equals(other.getUnknownFields());
       return result;
@@ -6690,6 +7373,10 @@ public final class MetaDataProtos {
         hash = (37 * hash) + IFEXISTS_FIELD_NUMBER;
         hash = (53 * hash) + hashBoolean(getIfExists());
       }
+      if (hasClientVersion()) {
+        hash = (37 * hash) + CLIENTVERSION_FIELD_NUMBER;
+        hash = (53 * hash) + getClientVersion();
+      }
       hash = (29 * hash) + getUnknownFields().hashCode();
       memoizedHashCode = hash;
       return hash;
@@ -6803,6 +7490,8 @@ public final class MetaDataProtos {
         bitField0_ = (bitField0_ & ~0x00000001);
         ifExists_ = false;
         bitField0_ = (bitField0_ & ~0x00000002);
+        clientVersion_ = 0;
+        bitField0_ = (bitField0_ & ~0x00000004);
         return this;
       }
 
@@ -6840,6 +7529,10 @@ public final class MetaDataProtos {
           to_bitField0_ |= 0x00000001;
         }
         result.ifExists_ = ifExists_;
+        if (((from_bitField0_ & 0x00000004) == 0x00000004)) {
+          to_bitField0_ |= 0x00000002;
+        }
+        result.clientVersion_ = clientVersion_;
         result.bitField0_ = to_bitField0_;
         onBuilt();
         return result;
@@ -6869,6 +7562,9 @@ public final class MetaDataProtos {
         if (other.hasIfExists()) {
           setIfExists(other.getIfExists());
         }
+        if (other.hasClientVersion()) {
+          setClientVersion(other.getClientVersion());
+        }
         this.mergeUnknownFields(other.getUnknownFields());
         return this;
       }
@@ -7001,6 +7697,39 @@ public final class MetaDataProtos {
         return this;
       }
 
+      // optional int32 clientVersion = 3;
+      private int clientVersion_ ;
+      /**
+       * <code>optional int32 clientVersion = 3;</code>
+       */
+      public boolean hasClientVersion() {
+        return ((bitField0_ & 0x00000004) == 0x00000004);
+      }
+      /**
+       * <code>optional int32 clientVersion = 3;</code>
+       */
+      public int getClientVersion() {
+        return clientVersion_;
+      }
+      /**
+       * <code>optional int32 clientVersion = 3;</code>
+       */
+      public Builder setClientVersion(int value) {
+        bitField0_ |= 0x00000004;
+        clientVersion_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>optional int32 clientVersion = 3;</code>
+       */
+      public Builder clearClientVersion() {
+        bitField0_ = (bitField0_ & ~0x00000004);
+        clientVersion_ = 0;
+        onChanged();
+        return this;
+      }
+
       // @@protoc_insertion_point(builder_scope:DropFunctionRequest)
     }
 
@@ -7028,6 +7757,16 @@ public final class MetaDataProtos {
      * <code>repeated bytes tableMetadataMutations = 1;</code>
      */
     com.google.protobuf.ByteString getTableMetadataMutations(int index);
+
+    // optional int32 clientVersion = 2;
+    /**
+     * <code>optional int32 clientVersion = 2;</code>
+     */
+    boolean hasClientVersion();
+    /**
+     * <code>optional int32 clientVersion = 2;</code>
+     */
+    int getClientVersion();
   }
   /**
    * Protobuf type {@code UpdateIndexStateRequest}
@@ -7088,6 +7827,11 @@ public final class MetaDataProtos {
               tableMetadataMutations_.add(input.readBytes());
               break;
             }
+            case 16: {
+              bitField0_ |= 0x00000001;
+              clientVersion_ = input.readInt32();
+              break;
+            }
           }
         }
       } catch (com.google.protobuf.InvalidProtocolBufferException e) {
@@ -7130,6 +7874,7 @@ public final class MetaDataProtos {
       return PARSER;
     }
 
+    private int bitField0_;
     // repeated bytes tableMetadataMutations = 1;
     public static final int TABLEMETADATAMUTATIONS_FIELD_NUMBER = 1;
     private java.util.List<com.google.protobuf.ByteString> tableMetadataMutations_;
@@ -7153,8 +7898,25 @@ public final class MetaDataProtos {
       return tableMetadataMutations_.get(index);
     }
 
+    // optional int32 clientVersion = 2;
+    public static final int CLIENTVERSION_FIELD_NUMBER = 2;
+    private int clientVersion_;
+    /**
+     * <code>optional int32 clientVersion = 2;</code>
+     */
+    public boolean hasClientVersion() {
+      return ((bitField0_ & 0x00000001) == 0x00000001);
+    }
+    /**
+     * <code>optional int32 clientVersion = 2;</code>
+     */
+    public int getClientVersion() {
+      return clientVersion_;
+    }
+
     private void initFields() {
       tableMetadataMutations_ = java.util.Collections.emptyList();
+      clientVersion_ = 0;
     }
     private byte memoizedIsInitialized = -1;
     public final boolean isInitialized() {
@@ -7171,6 +7933,9 @@ public final class MetaDataProtos {
       for (int i = 0; i < tableMetadataMutations_.size(); i++) {
         output.writeBytes(1, tableMetadataMutations_.get(i));
       }
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        output.writeInt32(2, clientVersion_);
+      }
       getUnknownFields().writeTo(output);
     }
 
@@ -7189,6 +7954,10 @@ public final class MetaDataProtos {
         size += dataSize;
         size += 1 * getTableMetadataMutationsList().size();
       }
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(2, clientVersion_);
+      }
       size += getUnknownFields().getSerializedSize();
       memoizedSerializedSize = size;
       return size;
@@ -7214,6 +7983,11 @@ public final class MetaDataProtos {
       boolean result = true;
       result = result && getTableMetadataMutationsList()
           .equals(other.getTableMetadataMutationsList());
+      result = result && (hasClientVersion() == other.hasClientVersion());
+      if (hasClientVersion()) {
+        result = result && (getClientVersion()
+            == other.getClientVersion());
+      }
       result = result &&
           getUnknownFields().equals(other.getUnknownFields());
       return result;
@@ -7231,6 +8005,10 @@ public final class MetaDataProtos {
         hash = (37 * hash) + TABLEMETADATAMUTATIONS_FIELD_NUMBER;
         hash = (53 * hash) + getTableMetadataMutationsList().hashCode();
       }
+      if (hasClientVersion()) {
+        hash = (37 * hash) + CLIENTVERSION_FIELD_NUMBER;
+        hash = (53 * hash) + getClientVersion();
+      }
       hash = (29 * hash) + getUnknownFields().hashCode();
       memoizedHashCode = hash;
       return hash;
@@ -7342,6 +8120,8 @@ public final class MetaDataProtos {
         super.clear();
         tableMetadataMutations_ = java.util.Collections.emptyList();
         bitField0_ = (bitField0_ & ~0x00000001);
+        clientVersion_ = 0;
+        bitField0_ = (bitField0_ & ~0x00000002);
         return this;
       }
 
@@ -7369,11 +8149,17 @@ public final class MetaDataProtos {
       public org.apache.phoenix.coprocessor.generated.MetaDataProtos.UpdateIndexStateRequest buildPartial() {
         org.apache.phoenix.coprocessor.generated.MetaDataProtos.UpdateIndexStateRequest result = new org.apache.phoenix.coprocessor.generated.MetaDataProtos.UpdateIndexStateRequest(this);
         int from_bitField0_ = bitField0_;
+        int to_bitField0_ = 0;
         if (((bitField0_ & 0x00000001) == 0x00000001)) {
           tableMetadataMutations_ = java.util.Collections.unmodifiableList(tableMetadataMutations_);
           bitField0_ = (bitField0_ & ~0x00000001);
         }
         result.tableMetadataMutations_ = tableMetadataMutations_;
+        if (((from_bitField0_ & 0x00000002) == 0x00000002)) {
+          to_bitField0_ |= 0x00000001;
+        }
+        result.clientVersion_ = clientVersion_;
+        result.bitField0_ = to_bitField0_;
         onBuilt();
         return result;
       }
@@ -7399,6 +8185,9 @@ public final class MetaDataProtos {
           }
           onChanged();
         }
+        if (other.hasClientVersion()) {
+          setClientVersion(other.getClientVersion());
+        }
         this.mergeUnknownFields(other.getUnknownFields());
         return this;
       }
@@ -7498,6 +8287,39 @@ public final class MetaDataProtos {
         return this;
       }
 
+      // optional int32 clientVersion = 2;
+      private int clientVersion_ ;
+      /**
+       * <code>optional int32 clientVersion = 2;</code>
+       */
+      public boolean hasClientVersion() {
+        return ((bitField0_ & 0x00000002) == 0x00000002);
+      }
+      /**
+       * <code>optional int32 clientVersion = 2;</code>
+       */
+      public int getClientVersion() {
+        return clientVersion_;
+      }
+      /**
+       * <code>optional int32 clientVersion = 2;</code>
+       */
+      public Builder setClientVersion(int value) {
+        bitField0_ |= 0x00000002;
+        clientVersion_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>optional int32 clientVersion = 2;</code>
+       */
+      public Builder clearClientVersion() {
+        bitField0_ = (bitField0_ & ~0x00000002);
+        clientVersion_ = 0;
+        onChanged();
+        return this;
+      }
+
       // @@protoc_insertion_point(builder_scope:UpdateIndexStateRequest)
     }
 
@@ -7511,6 +8333,16 @@ public final class MetaDataProtos {
 
   public interface ClearCacheRequestOrBuilder
       extends com.google.protobuf.MessageOrBuilder {
+
+    // optional int32 clientVersion = 1;
+    /**
+     * <code>optional int32 clientVersion = 1;</code>
+     */
+    boolean hasClientVersion();
+    /**
+     * <code>optional int32 clientVersion = 1;</code>
+     */
+    int getClientVersion();
   }
   /**
    * Protobuf type {@code ClearCacheRequest}
@@ -7545,6 +8377,7 @@ public final class MetaDataProtos {
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       initFields();
+      int mutable_bitField0_ = 0;
       com.google.protobuf.UnknownFieldSet.Builder unknownFields =
           com.google.protobuf.UnknownFieldSet.newBuilder();
       try {
@@ -7562,6 +8395,11 @@ public final class MetaDataProtos {
               }
               break;
             }
+            case 8: {
+              bitField0_ |= 0x00000001;
+              clientVersion_ = input.readInt32();
+              break;
+            }
           }
         }
       } catch (com.google.protobuf.InvalidProtocolBufferException e) {
@@ -7601,7 +8439,25 @@ public final class MetaDataProtos {
       return PARSER;
     }
 
+    private int bitField0_;
+    // optional int32 clientVersion = 1;
+    public static final int CLIENTVERSION_FIELD_NUMBER = 1;
+    private int clientVersion_;
+    /**
+     * <code>optional int32 clientVersion = 1;</code>
+     */
+    public boolean hasClientVersion() {
+      return ((bitField0_ & 0x00000001) == 0x00000001);
+    }
+    /**
+     * <code>optional int32 clientVersion = 1;</code>
+     */
+    public int getClientVersion() {
+      return clientVersion_;
+    }
+
     private void initFields() {
+      clientVersion_ = 0;
     }
     private byte memoizedIsInitialized = -1;
     public final boolean isInitialized() {
@@ -7615,6 +8471,9 @@ public final class MetaDataProtos {
     public void writeTo(com.google.protobuf.CodedOutputStream output)
                         throws java.io.IOException {
       getSerializedSize();
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        output.writeInt32(1, clientVersion_);
+      }
       getUnknownFields().writeTo(output);
     }
 
@@ -7624,6 +8483,10 @@ public final class MetaDataProtos {
       if (size != -1) return size;
 
       size = 0;
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(1, clientVersion_);
+      }
       size += getUnknownFields().getSerializedSize();
       memoizedSerializedSize = size;
       return size;
@@ -7647,6 +8510,11 @@ public final class MetaDataProtos {
       org.apache.phoenix.coprocessor.generated.MetaDataProtos.ClearCacheRequest other = (org.apache.phoenix.coprocessor.generated.MetaDataProtos.ClearCacheRequest) obj;
 
       boolean result = true;
+      result = result && (hasClientVersion() == other.hasClientVersion());
+      if (hasClientVersion()) {
+        result = result && (getClientVersion()
+            == other.getClientVersion());
+      }
       result = result &&
           getUnknownFields().equals(other.getUnknownFields());
       return result;
@@ -7660,6 +8528,10 @@ public final class MetaDataProtos {
       }
       int hash = 41;
       hash = (19 * hash) + getDescriptorForType().hashCode();
+      if (hasClientVersion()) {
+        hash = (37 * hash) + CLIENTVERSION_FIELD_NUMBER;
+        hash = (53 * hash) + getClientVersion();
+      }
       hash = (29 * hash) + getUnknownFields().hashCode();
       memoizedHashCode = hash;
       return hash;
@@ -7769,6 +8641,8 @@ public final class MetaDataProtos {
 
       public Builder clear() {
         super.clear();
+        clientVersion_ = 0;
+        bitField0_ = (bitField0_ & ~0x00000001);
         return this;
       }
 
@@ -7795,6 +8669,13 @@ public final class MetaDataProtos {
 
       public org.apache.phoenix.coprocessor.generated.MetaDataProtos.ClearCacheRequest buildPartial() {
         org.apache.phoenix.coprocessor.generated.MetaDataProtos.ClearCacheRequest result = new org.apache.phoenix.coprocessor.generated.MetaDataProtos.ClearCacheRequest(this);
+        int from_bitField0_ = bitField0_;
+        int to_bitField0_ = 0;
+        if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
+          to_bitField0_ |= 0x00000001;
+        }
+        result.clientVersion_ = clientVersion_;
+        result.bitField0_ = to_bitField0_;
         onBuilt();
         return result;
       }
@@ -7810,6 +8691,9 @@ public final class MetaDataProtos {
 
       public Builder mergeFrom(org.apache.phoenix.coprocessor.generated.MetaDataProtos.ClearCacheRequest other) {
         if (other == org.apache.phoenix.coprocessor.generated.MetaDataProtos.ClearCacheRequest.getDefaultInstance()) return this;
+        if (other.hasClientVersion()) {
+          setClientVersion(other.getClientVersion());
+        }
         this.mergeUnknownFields(other.getUnknownFields());
         return this;
       }
@@ -7835,6 +8719,40 @@ public final class MetaDataProtos {
         }
         return this;
       }
+      private int bitField0_;
+
+      // optional int32 clientVersion = 1;
+      private int clientVersion_ ;
+      /**
+       * <code>optional int32 clientVersion = 1;</code>
+       */
+      public boolean hasClientVersion() {
+        return ((bitField0_ & 0x00000001) == 0x00000001);
+      }
+      /**
+       * <code>optional int32 clientVersion = 1;</code>
+       */
+      public int getClientVersion() {
+        return clientVersion_;
+      }
+      /**
+       * <code>optional int32 clientVersion = 1;</code>
+       */
+      public Builder setClientVersion(int value) {
+        bitField0_ |= 0x00000001;
+        clientVersion_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>optional int32 clientVersion = 1;</code>
+       */
+      public Builder clearClientVersion() {
+        bitField0_ = (bitField0_ & ~0x00000001);
+        clientVersion_ = 0;
+        onChanged();
+        return this;
+      }
 
       // @@protoc_insertion_point(builder_scope:ClearCacheRequest)
     }
@@ -8187,6 +9105,16 @@ public final class MetaDataProtos {
 
   public interface GetVersionRequestOrBuilder
       extends com.google.protobuf.MessageOrBuilder {
+
+    // optional int32 clientVersion = 1;
+    /**
+     * <code>optional int32 clientVersion = 1;</code>
+     */
+    boolean hasClientVersion();
+    /**
+     * <code>optional int32 clientVersion = 1;</code>
+     */
+    int getClientVersion();
   }
   /**
    * Protobuf type {@code GetVersionRequest}
@@ -8221,6 +9149,7 @@ public final class MetaDataProtos {
         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
         throws com.google.protobuf.InvalidProtocolBufferException {
       initFields();
+      int mutable_bitField0_ = 0;
       com.google.protobuf.UnknownFieldSet.Builder unknownFields =
           com.google.protobuf.UnknownFieldSet.newBuilder();
       try {
@@ -8238,6 +9167,11 @@ public final class MetaDataProtos {
               }
               break;
             }
+            case 8: {
+              bitField0_ |= 0x00000001;
+              clientVersion_ = input.readInt32();
+              break;
+            }
           }
         }
       } catch (com.google.protobuf.InvalidProtocolBufferException e) {
@@ -8277,7 +9211,25 @@ public final class MetaDataProtos {
       return PARSER;
     }
 
+    private int bitField0_;
+    // optional int32 clientVersion = 1;
+    public static final int CLIENTVERSION_FIELD_NUMBER = 1;
+    private int clientVersion_;
+    /**
+     * <code>optional int32 clientVersion = 1;</code>
+     */
+    public boolean hasClientVersion() {
+      return ((bitField0_ & 0x00000001) == 0x00000001);
+    }
+    /**
+     * <code>optional int32 clientVersion = 1;</code>
+     */
+    public int getClientVersion() {
+      return clientVersion_;
+    }
+
     private void initFields() {
+      clientVersion_ = 0;
     }
     private byte memoizedIsInitialized = -1;
     public final boolean isInitialized() {
@@ -8291,6 +9243,9 @@ public final class MetaDataProtos {
     public void writeTo(com.google.protobuf.CodedOutputStream output)
                         throws java.io.IOException {
       getSerializedSize();
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        output.writeInt32(1, clientVersion_);
+      }
       getUnknownFields().writeTo(output);
     }
 
@@ -8300,6 +9255,10 @@ public final class MetaDataProtos {
       if (size != -1) return size;
 
       size = 0;
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(1, clientVersion_);
+      }
       size += getUnknownFields().getSerializedSize();
       memoizedSerializedSize = size;
       return size;
@@ -8323,6 +9282,11 @@ public final class MetaDataProtos {
       org.apache.phoenix.coprocessor.generated.MetaDataProtos.GetVersionRequest other = (org.apache.phoenix.coprocessor.generated.MetaDataProtos.GetVersionRequest) obj;
 
       boolean result = true;
+      result = result && (hasClientVersion() == other.hasClientVersion());
+      if (hasClientVersion()) {
+        result = result && (getClientVersion()
+            == other.getClientVersion());
+      }
       result = result &&
           getUnknownFields().equals(other.getUnknownFields());
       return result;
@@ -8336,6 +9300,10 @@ public final class MetaDataProtos {
       }
       int hash = 41;
       hash = (19 * hash) + getDescriptorForType().hashCode();
+      if (hasClientVersion()) {
+        hash = (37 * hash) + CLIENTVERSION_FIELD_NUMBER;
+        hash = (53 * hash) + getClientVersion();
+      }
       hash = (29 * hash) + getUnknownFields().hashCode();
       memoizedHashCode = hash;
       return hash;
@@ -8445,6 +9413,8 @@ public final class MetaDataProtos {
 
       public Builder clear() {
         super.clear();
+        clientVersion_ = 0;
+        bitField0_ = (bitField0_ & ~0x00000001);
         return this;
       }
 
@@ -8471,6 +9441,13 @@ public final class MetaDataProtos {
 
       public org.apache.phoenix.coprocessor.generated.MetaDataProtos.GetVersionRequest buildPartial() {
         org.apache.phoenix.coprocessor.generated.MetaDataProtos.GetVersionRequest result = new org.apache.phoenix.coprocessor.generated.MetaDataProtos.GetVersionRequest(this);
+        int from_bitField0_ = bitField0_;
+        int to_bitField0_ = 0;
+        if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
+          to_bitField0_ |= 0x00000001;
+        }
+        result.clientVersion_ = clientVersion_;
+        result.bitField0_ = to_bitField0_;
         onBuilt();
         return result;
       }
@@ -8486,6 +9463,9 @@ public final class MetaDataProtos {
 
       public Builder mergeFrom(org.apache.phoenix.coprocessor.generated.MetaDataProtos.GetVersionRequest other) {
         if (other == org.apache.phoenix.coprocessor.generated.MetaDataProtos.GetVersionRequest.getDefaultInstance()) return this;
+        if (other.hasClientVersion()) {
+          setClientVersion(other.getClientVersion());
+        }
         this.mergeUnknownFields(other.getUnknownFields());
         return this;
       }
@@ -8511,6 +9491,40 @@ public final class MetaDataProtos {
         }
         return this;
       }
+      private int bitField0_;
+
+      // optional int32 clientVersion = 1;
+      private int clientVersion_ ;
+      /**
+       * <code>optional int32 clientVersion = 1;</code>
+       */
+      public boolean hasClientVersion() {
+        return ((bitField0_ & 0x00000001) == 0x00000001);
+      }
+      /**
+       * <code>optional int32 clientVersion = 1;</code>
+       */
+      public int getClientVersion() {
+        return clientVersion_;
+      }
+      /**
+       * <code>optional int32 clientVersion = 1;</code>
+       */
+      public Builder setClientVersion(int value) {
+        bitField0_ |= 0x00000001;
+        clientVersion_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>optional int32 clientVersion = 1;</code>
+       */
+      public Builder clearClientVersion() {
+        bitField0_ = (bitField0_ & ~0x00000001);
+        clientVersion_ = 0;
+        onChanged();
+        return this;
+      }
 
       // @@protoc_insertion_point(builder_scope:GetVersionRequest)
     }
@@ -9007,6 +10021,16 @@ public final class MetaDataProtos {
      * <code>required int64 clientTimestamp = 4;</code>
      */
     long getClientTimestamp();
+
+    // optional int32 clientVersion = 5;
+    /**
+     * <code>optional int32 clientVersion = 5;</code>
+     */
+    boolean hasClientVersion();
+    /**
+     * <code>optional int32 clientVersion = 5;</code>
+     */
+    int getClientVersion();
   }
   /**
    * Protobuf type {@code ClearTableFromCacheRequest}
@@ -9079,6 +10103,11 @@ public final class MetaDataProtos {
               clientTimestamp_ = input.readInt64();
               break;
             }
+            case 40: {
+              bitField0_ |= 0x00000010;
+              clientVersion_ = input.readInt32();
+              break;
+            }
           }
         }
       } catch (com.google.protobuf.InvalidProtocolBufferException e) {
@@ -9183,11 +10212,28 @@ public final class MetaDataProtos {
       return clientTimestamp_;
     }
 
+    // optional int32 clientVersion = 5;
+    public static final int CLIENTVERSION_FIELD_NUMBER = 5;
+    private int clientVersion_;
+    /**
+     * <code>optional int32 clientVersion = 5;</code>
+     */
+    public boolean hasClientVersion() {
+      return ((bitField0_ & 0x00000010) == 0x00000010);
+    }
+    /**
+     * <code>optional int32 clientVersion = 5;</code>
+     */
+    public int getClientVersion() {
+      return clientVersion_;
+    }
+
     private void initFields() {
       tenantId_ = com.google.protobuf.ByteString.EMPTY;
       schemaName_ = com.google.protobuf.ByteString.EMPTY;
       tableName_ = com.google.protobuf.ByteString.EMPTY;
       clientTimestamp_ = 0L;
+      clientVersion_ = 0;
     }
     private byte memoizedIsInitialized = -1;
     public final boolean isInitialized() {
@@ -9229,6 +10275,9 @@ public final class MetaDataProtos {
       if (((bitField0_ & 0x00000008) == 0x00000008)) {
         output.writeInt64(4, clientTimestamp_);
       }
+      if (((bitField0_ & 0x00000010) == 0x00000010)) {
+        output.writeInt32(5, clientVersion_);
+      }
       getUnknownFields().writeTo(output);
     }
 
@@ -9254,6 +10303,10 @@ public final class MetaDataProtos {
         size += com.google.protobuf.CodedOutputStream
           .computeInt64Size(4, clientTimestamp_);
       }
+      if (((bitField0_ & 0x00000010) == 0x00000010)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(5, clientVersion_);
+      }
       size += getUnknownFields().getSerializedSize();
       memoizedSerializedSize = size;
       return size;
@@ -9297,6 +10350,11 @@ public final class MetaDataProtos {
         result = result && (getClientTimestamp()
             == other.getClientTimestamp());
       }
+      result = result && (hasClientVersion() == other.hasClientVersion());
+      if (hasClientVersion()) {
+        result = result && (getClientVersion()
+            == other.getClientVersion());
+      }
       result = result &&
           getUnknownFields().equals(other.getUnknownFields());
       return result;
@@ -9326,6 +10384,10 @@ public final class MetaDataProtos {
         hash = (37 * hash) + CLIENTTIMESTAMP_FIELD_NUMBER;
         hash = (53 * hash) + hashLong(getClientTimestamp());
       }
+      if (hasClientVersion()) {
+        hash = (37 * hash) + CLIENTVERSION_FIELD_NUMBER;
+        hash = (53 * hash) + getClientVersion();
+      }
       hash = (29 * hash) + getUnknownFields().hashCode();
       memoizedHashCode = hash;
       return hash;
@@ -9443,6 +10505,8 @@ public final class MetaDataProtos {
         bitField0_ = (bitField0_ & ~0x00000004);
         clientTimestamp_ = 0L;
         bitField0_ = (bitField0_ & ~0x00000008);
+        clientVersion_ = 0;
+        bitField0_ = (bitField0_ & ~0x00000010);
         return this;
       }
 
@@ -9487,6 +10551,10 @@ public final class MetaDataProtos {
           to_bitField0_ |= 0x00000008;
         }
         result.clientTimestamp_ = clientTimestamp_;
+        if (((from_bitField0_ & 0x00000010) == 0x00000010)) {
+          to_bitField0_ |= 0x00000010;
+        }
+        result.clientVersion_ = clientVersion_;
         result.bitField0_ = to_bitField0_;
         onBuilt();
         return result;
@@ -9515,6 +10583,9 @@ public final class MetaDataProtos {
         if (other.hasClientTimestamp()) {
           setClientTimestamp(other.getClientTimestamp());
         }
+        if (other.hasClientVersion()) {
+          setClientVersion(other.getClientVersion());
+        }
         this.mergeUnknownFields(other.getUnknownFields());
         return this;
       }
@@ -9699,6 +10770,39 @@ public final class MetaDataProtos {
         return this;
       }
 
+      // optional int32 clientVersion = 5;
+      private int clientVersion_ ;
+      /**
+       * <code>optional int32 clientVersion = 5;</code>
+       */
+      public boolean hasClientVersion() {
+        return ((bitField0_ & 0x00000010) == 0x00000010);
+      }
+      /**
+       * <code>optional int32 clientVersion = 5;</code>
+       */
+      public int getClientVersion() {
+        return clientVersion_;
+      }
+      /**
+       * <code>optional int32 clientVersion = 5;</code>
+       */
+      public Builder setClientVersion(int value) {
+        bitField0_ |= 0x00000010;
+        clientVersion_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>optional int32 clientVersion = 5;</code>
+       */
+      public Builder clearClientVersion() {
+        bitField0_ = (bitField0_ & ~0x00000010);
+        clientVersion_ = 0;
+        onChanged();
+        return this;
+      }
+
       // @@protoc_insertion_point(builder_scope:ClearTableFromCacheRequest)
     }
 
@@ -11156,60 +12260,67 @@ public final class MetaDataProtos {
       "onTime\030\002 \001(\003\022\026\n\005table\030\003 \001(\0132\007.PTable\022\026\n\016" +
       "tablesToDelete\030\004 \003(\014\022\022\n\ncolumnName\030\005 \001(\014" +
       "\022\022\n\nfamilyName\030\006 \001(\014\022\024\n\014functionName\030\007 \001" +
-      "(\014\022\034\n\010function\030\010 \003(\0132\n.PFunction\"{\n\017GetT" +
-      "ableRequest\022\020\n\010tenantId\030\001 \002(\014\022\022\n\nschemaN" +
-      "ame\030\002 \002(\014\022\021\n\ttableName\030\003 \002(\014\022\026\n\016tableTim" +
-      "estamp\030\004 \002(\003\022\027\n\017clientTimestamp\030\005 \002(\003\"s\n",
-      "\023GetFunctionsRequest\022\020\n\010tenantId\030\001 \002(\014\022\025" +
-      "\n\rfunctionNames\030\002 \003(\014\022\032\n\022functionTimesta" +
-      "mps\030\003 \003(\003\022\027\n\017clientTimestamp\030\004 \002(\003\"4\n\022Cr" +
-      "eateTableRequest\022\036\n\026tableMetadataMutatio" +
-      "ns\030\001 \003(\014\"[\n\025CreateFunctionRequest\022\036\n\026tab" +
-      "leMetadataMutations\030\001 \003(\014\022\021\n\ttemporary\030\002" +
-      " \002(\010\022\017\n\007replace\030\003 \001(\010\"V\n\020DropTableReques" +
-      "t\022\036\n\026tableMetadataMutations\030\001 \003(\014\022\021\n\ttab" +
-      "leType\030\002 \002(\t\022\017\n\007cascade\030\003 \001(\010\"2\n\020AddColu" +
-      "mnRequest\022\036\n\026tableMetadataMutations\030\001 \003(",
-      "\014\"3\n\021DropColumnRequest\022\036\n\026tableMetadataM" +
-      "utations\030\001 \003(\014\"G\n\023DropFunctionRequest\022\036\n" +
-      "\026tableMetadataMutations\030\001 \003(\014\022\020\n\010ifExist" +
-      "s\030\002 \001(\010\"9\n\027UpdateIndexStateRequest\022\036\n\026ta" +
-      "bleMetadataMutations\030\001 \003(\014\"\023\n\021ClearCache" +
-      "Request\"\024\n\022ClearCacheResponse\"\023\n\021GetVers" +
-      "ionRequest\"%\n\022GetVersionResponse\022\017\n\007vers" +
-      "ion\030\001 \002(\003\"n\n\032ClearTableFromCacheRequest\022" +
-      "\020\n\010tenantId\030\001 \002(\014\022\022\n\nschemaName\030\002 \002(\014\022\021\n" +
-      "\ttableName\030\003 \002(\014\022\027\n\017clientTimestamp\030\004 \002(",
-      "\003\"\035\n\033ClearTableFromCacheResponse*\365\002\n\014Mut" +
-      "ationCode\022\030\n\024TABLE_ALREADY_EXISTS\020\000\022\023\n\017T" +
-      "ABLE_NOT_FOUND\020\001\022\024\n\020COLUMN_NOT_FOUND\020\002\022\031" +
-      "\n\025COLUMN_ALREADY_EXISTS\020\003\022\035\n\031CONCURRENT_" +
-      "TABLE_MUTATION\020\004\022\027\n\023TABLE_NOT_IN_REGION\020" +
-      "\005\022\025\n\021NEWER_TABLE_FOUND\020\006\022\034\n\030UNALLOWED_TA" +
-      "BLE_MUTATION\020\007\022\021\n\rNO_PK_COLUMNS\020\010\022\032\n\026PAR" +
-      "ENT_TABLE_NOT_FOUND\020\t\022\033\n\027FUNCTION_ALREAD" +
-      "Y_EXISTS\020\n\022\026\n\022FUNCTION_NOT_FOUND\020\013\022\030\n\024NE" +
-      "WER_FUNCTION_FOUND\020\014\022\032\n\026FUNCTION_NOT_IN_",
-      "REGION\020\r2\304\005\n\017MetaDataService\022/\n\010getTable" +
-      "\022\020.GetTableRequest\032\021.MetaDataResponse\0227\n" +
-      "\014getFunctions\022\024.GetFunctionsRequest\032\021.Me" +
-      "taDataResponse\0225\n\013createTable\022\023.CreateTa" +
-      "bleRequest\032\021.MetaDataResponse\022;\n\016createF" +
-      "unction\022\026.CreateFunctionRequest\032\021.MetaDa" +
-      "taResponse\0221\n\tdropTable\022\021.DropTableReque" +
-      "st\032\021.MetaDataResponse\0227\n\014dropFunction\022\024." +
-      "DropFunctionRequest\032\021.MetaDataResponse\0221" +
-      "\n\taddColumn\022\021.AddColumnRequest\032\021.MetaDat",
-      "aResponse\0223\n\ndropColumn\022\022.DropColumnRequ" +
-      "est\032\021.MetaDataResponse\022?\n\020updateIndexSta" +
-      "te\022\030.UpdateIndexStateRequest\032\021.MetaDataR" +
-      "esponse\0225\n\nclearCache\022\022.ClearCacheReques" +
-      "t\032\023.ClearCacheResponse\0225\n\ngetVersion\022\022.G" +
-      "etVersionRequest\032\023.GetVersionResponse\022P\n" +
-      "\023clearTableFromCache\022\033.ClearTableFromCac" +
-      "heRequest\032\034.ClearTableFromCacheResponseB" +
-      "B\n(org.apache.phoenix.coprocessor.genera" +
-      "tedB\016MetaDataProtosH\001\210\001\001\240\001\001"
+      "(\014\022\034\n\010function\030\010 \003(\0132\n.PFunction\"\222\001\n\017Get" +
+      "TableRequest\022\020\n\010tenantId\030\001 \002(\014\022\022\n\nschema" +
+      "Name\030\002 \002(\014\022\021\n\ttableName\030\003 \002(\014\022\026\n\016tableTi" +
+      "mestamp\030\004 \002(\003\022\027\n\017clientTimestamp\030\005 \002(\003\022\025",
+      "\n\rclientVersion\030\006 \001(\005\"\212\001\n\023GetFunctionsRe" +
+      "quest\022\020\n\010tenantId\030\001 \002(\014\022\025\n\rfunctionNames" +
+      "\030\002 \003(\014\022\032\n\022functionTimestamps\030\003 \003(\003\022\027\n\017cl" +
+      "ientTimestamp\030\004 \002(\003\022\025\n\rclientVersion\030\005 \001" +
+      "(\005\"K\n\022CreateTableRequest\022\036\n\026tableMetadat" +
+      "aMutations\030\001 \003(\014\022\025\n\rclientVersion\030\002 \001(\005\"" +
+      "r\n\025CreateFunctionRequest\022\036\n\026tableMetadat" +
+      "aMutations\030\001 \003(\014\022\021\n\ttemporary\030\002 \002(\010\022\017\n\007r" +
+      "eplace\030\003 \001(\010\022\025\n\rclientVersion\030\004 \001(\005\"m\n\020D" +
+      "ropTableRequest\022\036\n\026tableMetadataMutation",
+      "s\030\001 \003(\014\022\021\n\ttableType\030\002 \002(\t\022\017\n\007cascade\030\003 " +
+      "\001(\010\022\025\n\rclientVersion\030\004 \001(\005\"I\n\020AddColumnR" +
+      "equest\022\036\n\026tableMetadataMutations\030\001 \003(\014\022\025" +
+      "\n\rclientVersion\030\002 \001(\005\"J\n\021DropColumnReque" +
+      "st\022\036\n\026tableMetadataMutations\030\001 \003(\014\022\025\n\rcl" +
+      "ientVersion\030\002 \001(\005\"^\n\023DropFunctionRequest" +
+      "\022\036\n\026tableMetadataMutations\030\001 \003(\014\022\020\n\010ifEx" +
+      "ists\030\002 \001(\010\022\025\n\rclientVersion\030\003 \001(\005\"P\n\027Upd" +
+      "ateIndexStateRequest\022\036\n\026tableMetadataMut" +
+      "ations\030\001 \003(\014\022\025\n\rclientVersion\030\002 \001(\005\"*\n\021C",
+      "learCacheRequest\022\025\n\rclientVersion\030\001 \001(\005\"" +
+      "\024\n\022ClearCacheResponse\"*\n\021GetVersionReque" +
+      "st\022\025\n\rclientVersion\030\001 \001(\005\"%\n\022GetVersionR" +
+      "esponse\022\017\n\007version\030\001 \002(\003\"\205\001\n\032ClearTableF" +
+      "romCacheRequest\022\020\n\010tenantId\030\001 \002(\014\022\022\n\nsch" +
+      "emaName\030\002 \002(\014\022\021\n\ttableName\030\003 \002(\014\022\027\n\017clie" +
+      "ntTimestamp\030\004 \002(\003\022\025\n\rclientVersion\030\005 \001(\005" +
+      "\"\035\n\033ClearTableFromCacheResponse*\365\002\n\014Muta" +
+      "tionCode\022\030\n\024TABLE_ALREADY_EXISTS\020\000\022\023\n\017TA" +
+      "BLE_NOT_FOUND\020\001\022\024\n\020COLUMN_NOT_FOUND\020\002\022\031\n",
+      "\025COLUMN_ALREADY_EXISTS\020\003\022\035\n\031CONCURRENT_T" +
+      "ABLE_MUTATION\020\004\022\027\n\023TABLE_NOT_IN_REGION\020\005" +
+      "\022\025\n\021NEWER_TABLE_FOUND\020\006\022\034\n\030UNALLOWED_TAB" +
+      "LE_MUTATION\020\007\022\021\n\rNO_PK_COLUMNS\020\010\022\032\n\026PARE" +
+      "NT_TABLE_NOT_FOUND\020\t\022\033\n\027FUNCTION_ALREADY" +
+      "_EXISTS\020\n\022\026\n\022FUNCTION_NOT_FOUND\020\013\022\030\n\024NEW" +
+      "ER_FUNCTION_FOUND\020\014\022\032\n\026FUNCTION_NOT_IN_R" +
+      "EGION\020\r2\304\005\n\017MetaDataService\022/\n\010getTable\022" +
+      "\020.GetTableRequest\032\021.MetaDataResponse\0227\n\014" +
+      "getFunctions\022\024.GetFunctionsRequest\032\021.Met",
+      "aDataResponse\0225\n\013createTable\022\023.CreateTab" +
+      "leRequest\032\021.MetaDataResponse\022;\n\016createFu" +
+      "nction\022\026.CreateFunctionRequest\032\021.MetaDat" +
+      "aResponse\0221\n\tdropTable\022\021.DropTableReques" +
+      "t\032\021.MetaDataResponse\0227\n\014dropFunction\022\024.D" +
+      "ropFunctionRequest\032\021.MetaDataResponse\0221\n" +
+      "\taddColumn\022\021.AddColumnRequest\032\021.MetaData" +
+      "Response\0223\n\ndropColumn\022\022.DropColumnReque" +
+      "st\032\021.MetaDataResponse\022?\n\020updateIndexStat" +
+      "e\022\030.UpdateIndexStateRequest\032\021.MetaDataRe",
+      "sponse\0225\n\nclearCache\022\022.ClearCacheRequest" +
+      "\032\023.ClearCacheResponse\0225\n\ngetVersion\022\022.Ge" +
+      "tVersionRequest\032\023.GetVersionResponse\022P\n\023" +
+      "clearTableFromCache\022\033.ClearTableFromCach" +
+      "eRequest\032\034.ClearTableFromCacheResponseBB" +
+      "\n(org.apache.phoenix.coprocessor.generat" +
+      "edB\016MetaDataProtosH\001\210\001\001\240\001\001"
     };
     com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
       new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
@@ -11227,61 +12338,61 @@ public final class MetaDataProtos {
           internal_static_GetTableRequest_fieldAccessorTable = new
             com.google.protobuf.GeneratedMessage.FieldAccessorTable(
               internal_static_GetTableRequest_descriptor,
-              new java.lang.String[] { "TenantId", "SchemaName", "TableName", "TableTimestamp", "ClientTimestamp", });
+              new java.lang.String[] { "TenantId", "SchemaName", "TableName", "TableTimestamp", "ClientTimestamp", "ClientVersion", });
           internal_static_GetFunctionsRequest_descriptor =
             getDescriptor().getMessageTypes().get(2);
           internal_static_GetFunctionsRequest_fieldAccessorTable = new
             com.google.protobuf.GeneratedMessage.FieldAccessorTable(
               internal_static_GetFunctionsRequest_descriptor,
-              new java.lang.String[] { "TenantId", "FunctionNames", "FunctionTimestamps", "ClientTimestamp", });
+              new java.lang.String[] { "TenantId", "FunctionNames", "FunctionTimestamps", "ClientTimestamp", "ClientVersion", });
           internal_static_CreateTableRequest_descriptor =
             getDescriptor().getMessageTypes().get(3);
           internal_static_CreateTableRequest_fieldAccessorTable = new
             com.google.protobuf.GeneratedMessage.FieldAccessorTable(
               internal_static_CreateTableRequest_descriptor,
-              new java.lang.String[] { "TableMetadataMutations", });
+              new java.lang.String[] { "TableMetadataMutations", "ClientVersion", });
           internal_static_CreateFunctionRequest_descriptor =
             getDescriptor().getMessageTypes().get(4);
           internal_static_CreateFunctionRequest_fieldAccessorTable = new
             com.google.protobuf.GeneratedMessage.FieldAccessorTable(
               internal_static_CreateFunctionRequest_descriptor,
-              new java.lang.String[] { "TableMetadataMutations", "Temporary", "Replace", });
+              new java.lang.String[] { "TableMetadataMutations", "Temporary", "Replace", "ClientVersion", });
           internal_static_DropTableRequest_descriptor =
             getDescriptor().getMessageTypes().get(5);
           internal_static_DropTableRequest_fieldAccessorTable = new
             com.google.protobuf.GeneratedMessage.FieldAccessorTable(
               internal_static_DropTableRequest_descriptor,
-              new java.lang.String[] { "TableMetadataMutations", "TableType", "Cascade", });
+              new java.lang.String[] { "TableMetadataMutations", "TableType", "Cascade", "ClientVersion", });
           internal_static_AddColumnRequest_descriptor =
             getDescriptor().getMessageTypes().get(6);
           internal_static_AddColumnRequest_fieldAccessorTable = new
             com.google.protobuf.GeneratedMessage.FieldAccessorTable(
               internal_static_AddColumnRequest_descriptor,
-              new java.lang.String[] { "TableMetadataMutations", });
+              new java.lang.String[] { "TableMetadataMutations", "ClientVersion", });
           internal_static_DropColumnRequest_descriptor =
             getDescriptor().getMessageTypes().get(7);
           internal_static_DropColumnRequest_fieldAccessorTable = new
             com.google.protobuf.GeneratedMessage.FieldAccessorTable(
               internal_static_DropColumnRequest_descriptor,
-              new java.lang.String[] { "TableMetadataMutations", });
+              new java.lang.String[] { "TableMetadataMutations", "ClientVersion", });
           internal_static_DropFunctionRequest_descriptor =
             getDescriptor().getMessageTypes().get(8);
           internal_static_DropFunctionRequest_fieldAccessorTable = new
             com.google.protobuf.GeneratedMessage.FieldAccessorTable(
               internal_static_DropFunctionRequest_descriptor,
-              new java.lang.String[] { "TableMetadataMutations", "IfExists", });
+              new java.lang.String[] { "TableMetadataMutations", "IfExists", "ClientVersion", });
           internal_static_UpdateIndexStateRequest_descriptor =
             getDescriptor().getMessageTypes().get(9);
           internal_static_UpdateIndexStateRequest_fieldAccessorTable = new
             com.google.protobuf.GeneratedMessage.FieldAccessorTable(
               internal_static_UpdateIndexStateRequest_descriptor,
-              new java.lang.String[] { "TableMetadataMutations", });
+              new java.lang.String[] { "TableMetadataMutations", "ClientVersion", });
           internal_static_ClearCacheRequest_descriptor =
             getDescriptor().getMessageTypes().get(10);
           internal_static_ClearCacheRequest_fieldAccessorTable = new
             com.google.protobuf.GeneratedMessage.FieldAccessorTable(
               internal_static_ClearCacheRequest_descriptor,
-              new java.lang.String[] { });
+              new java.lang.String[] { "ClientVersion", });
           internal_static_ClearCacheResponse_descriptor =
             getDescriptor().getMessageTypes().get(11);
           internal_static_ClearCacheResponse_fieldAccessorTable = new
@@ -11293,7 +12404,7 @@ public final class MetaDataProtos {
           internal_static_GetVersionRequest_fieldAccessorTable = new
             com.google.protobuf.GeneratedMessage.FieldAccessorTable(
               internal_static_GetVersionRequest_descriptor,
-              new java.lang.String[] { });
+              new java.lang.String[] { "ClientVersion", });
           internal_static_GetVersionResponse_descriptor =
             getDescriptor().getMessageTypes().get(13);
           internal_static_GetVersionResponse_fieldAccessorTable = new
@@ -11305,7 +12416,7 @@ public final class MetaDataProtos {
           internal_static_ClearTableFromCacheRequest_fieldAccessorTable = new
             com.google.protobuf.GeneratedMessage.FieldAccessorTable(
               internal_static_ClearTableFromCacheRequest_descriptor,
-              new java.lang.String[] { "TenantId", "SchemaName", "TableName", "ClientTimestamp", });
+              new java.lang.String[] { "TenantId", "SchemaName", "TableName", "ClientTimestamp", "ClientVersion", });
           internal_static_ClearTableFromCacheResponse_descriptor =
             getDescriptor().getMessageTypes().get(15);
           internal_static_ClearTableFromCacheResponse_fieldAccessorTable = new


[45/50] [abbrv] phoenix git commit: PHOENIX-2101 Implement ARRAY_TO_STRING built in function (Dumindu Buddhika)

Posted by ma...@apache.org.
PHOENIX-2101 Implement ARRAY_TO_STRING built in function (Dumindu
Buddhika)


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/236ce1c8
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/236ce1c8
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/236ce1c8

Branch: refs/heads/calcite
Commit: 236ce1c87c729c9beba5985e4fe3871e300176e6
Parents: 33d6050
Author: ramkrishna <ra...@gmail.com>
Authored: Thu Jul 16 21:20:36 2015 +0530
Committer: ramkrishna <ra...@gmail.com>
Committed: Thu Jul 16 21:20:36 2015 +0530

----------------------------------------------------------------------
 .../end2end/ArrayToStringFunctionIT.java        | 654 +++++++++++++++++++
 .../phoenix/expression/ExpressionType.java      |   4 +-
 .../function/ArrayToStringFunction.java         |  84 +++
 .../phoenix/schema/types/PArrayDataType.java    |  30 +
 .../expression/ArrayToStringFunctionTest.java   | 374 +++++++++++
 5 files changed, 1145 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/236ce1c8/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayToStringFunctionIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayToStringFunctionIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayToStringFunctionIT.java
new file mode 100644
index 0000000..3b3f1d5
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayToStringFunctionIT.java
@@ -0,0 +1,654 @@
+/*
+ * 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.end2end;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.sql.*;
+
+import org.junit.Test;
+
+public class ArrayToStringFunctionIT extends BaseHBaseManagedTimeIT {
+    private void initTables(Connection conn) throws Exception {
+        String ddl = "CREATE TABLE regions (region_name VARCHAR PRIMARY KEY,varchars VARCHAR[],integers INTEGER[],doubles DOUBLE[],bigints BIGINT[],chars CHAR(15)[],double1 DOUBLE,varchar1 VARCHAR,nullcheck INTEGER,chars2 CHAR(15)[])";
+        conn.createStatement().execute(ddl);
+        String dml = "UPSERT INTO regions(region_name,varchars,integers,doubles,bigints,chars,double1,varchar1,nullcheck,chars2) VALUES('SF Bay Area'," +
+                "ARRAY['2345','46345','23234']," +
+                "ARRAY[2345,46345,23234,456]," +
+                "ARRAY[23.45,46.345,23.234,45.6,5.78]," +
+                "ARRAY[12,34,56,78,910]," +
+                "ARRAY['a','bbbb','c','ddd','e']," +
+                "23.45," +
+                "', '," +
+                "NULL," +
+                "ARRAY['a','bbbb','c','ddd','e','foo']" +
+                ")";
+        PreparedStatement stmt = conn.prepareStatement(dml);
+        stmt.execute();
+        conn.commit();
+    }
+
+    @Test
+    public void testArrayToStringFunctionVarchar1() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_TO_STRING(varchars, ',','*') FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        String expected = "2345,46345,23234";
+
+        assertEquals(expected, rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionVarchar2() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_TO_STRING(varchars, ',') FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        String expected = "2345,46345,23234";
+
+        assertEquals(expected, rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionVarchar3() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_TO_STRING(ARRAY['hello', 'hello'], ',') FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        String expected = "hello,hello";
+
+        assertEquals(expected, rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionInt() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_TO_STRING(integers, ',') FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        String expected = "2345,46345,23234,456";
+
+        assertEquals(expected, rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionDouble1() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_TO_STRING(doubles, ', ') FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        String expected = "23.45, 46.345, 23.234, 45.6, 5.78";
+
+        assertEquals(expected, rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionDouble2() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_TO_STRING(ARRAY[2.3, 4.5], ', ') FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        String expected = "2.3, 4.5";
+
+        assertEquals(expected, rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionBigint() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_TO_STRING(bigints, ', ') FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        String expected = "12, 34, 56, 78, 910";
+
+        assertEquals(expected, rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionChar1() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_TO_STRING(chars, ', ') FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        String expected = "a   , bbbb, c   , ddd , e   ";
+
+        assertEquals(expected, rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionChar2() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_TO_STRING(chars2, ', ') FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        String expected = "a   , bbbb, c   , ddd , e   , foo ";
+
+        assertEquals(expected, rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionChar3() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_TO_STRING(chars2, varchar1) FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        String expected = "a   , bbbb, c   , ddd , e   , foo ";
+
+        assertEquals(expected, rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionWithNestedFunctions1() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_TO_STRING(ARRAY[integers[1],integers[1]], ', ') FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        String expected = "2345, 2345";
+
+        assertEquals(expected, rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionWithNestedFunctions2() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_TO_STRING(ARRAY[ARRAY_ELEM(ARRAY[2,4],1),ARRAY_ELEM(ARRAY[2,4],2)], ', ') FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        String expected = "2, 4";
+
+        assertEquals(expected, rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionWithNestedFunctions3() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_TO_STRING(ARRAY[ARRAY_ELEM(doubles, 1), ARRAY_ELEM(doubles, 1)], ', ') FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        String expected = "23.45, 23.45";
+
+        assertEquals(expected, rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionWithNestedFunctions4() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_ELEM(ARRAY_APPEND(ARRAY['abc','bcd'], ARRAY_TO_STRING(ARRAY['a','b'], 'c')), 3) FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        String expected = "acb";
+
+        assertEquals(expected, rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionWithUpsert1() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+
+        String ddl = "CREATE TABLE regions (region_name VARCHAR PRIMARY KEY,varchar VARCHAR)";
+        conn.createStatement().execute(ddl);
+
+        String dml = "UPSERT INTO regions(region_name,varchar) VALUES('SF Bay Area',ARRAY_TO_STRING(ARRAY['hello','world'],','))";
+        conn.createStatement().execute(dml);
+        conn.commit();
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT varchar FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        String expected = "hello,world";
+
+        assertEquals(expected, rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionWithUpsert2() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+
+        String ddl = "CREATE TABLE regions (region_name VARCHAR PRIMARY KEY,varchar VARCHAR)";
+        conn.createStatement().execute(ddl);
+
+        String dml = "UPSERT INTO regions(region_name,varchar) VALUES('SF Bay Area',ARRAY_TO_STRING(ARRAY[3, 4, 5],', '))";
+        conn.createStatement().execute(dml);
+        conn.commit();
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT varchar FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        String expected = "3, 4, 5";
+
+        assertEquals(expected, rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionWithUpsert3() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+
+        String ddl = "CREATE TABLE regions (region_name VARCHAR PRIMARY KEY,varchar VARCHAR)";
+        conn.createStatement().execute(ddl);
+
+        String dml = "UPSERT INTO regions(region_name,varchar) VALUES('SF Bay Area',ARRAY_TO_STRING(ARRAY[3.1, 4.2, 5.5],', '))";
+        conn.createStatement().execute(dml);
+        conn.commit();
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT varchar FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        String expected = "3.1, 4.2, 5.5";
+
+        assertEquals(expected, rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionWithUpsert4() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+
+        String ddl = "CREATE TABLE regions (region_name VARCHAR PRIMARY KEY,varchar VARCHAR)";
+        conn.createStatement().execute(ddl);
+
+        String dml = "UPSERT INTO regions(region_name,varchar) VALUES('SF Bay Area',ARRAY_TO_STRING(ARRAY[true, false, true],', '))";
+        conn.createStatement().execute(dml);
+        conn.commit();
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT varchar FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        String expected = "true, false, true";
+
+        assertEquals(expected, rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionWithUpsertSelect1() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+
+        String ddl = "CREATE TABLE source (region_name VARCHAR PRIMARY KEY,doubles DOUBLE[])";
+        conn.createStatement().execute(ddl);
+
+        ddl = "CREATE TABLE target (region_name VARCHAR PRIMARY KEY,varchar VARCHAR)";
+        conn.createStatement().execute(ddl);
+
+        String dml = "UPSERT INTO source(region_name,doubles) VALUES('SF Bay Area', ARRAY[5.67, 7.87])";
+        conn.createStatement().execute(dml);
+
+        dml = "UPSERT INTO source(region_name,doubles) VALUES('SF Bay Area2', ARRAY[9.2, 3.4])";
+        conn.createStatement().execute(dml);
+        conn.commit();
+
+        dml = "UPSERT INTO target(region_name, varchar) SELECT region_name, ARRAY_TO_STRING(doubles, ', ') FROM source";
+        conn.createStatement().execute(dml);
+        conn.commit();
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT varchar FROM target");
+        assertTrue(rs.next());
+
+        String expected = "5.67, 7.87";
+        assertEquals(expected, rs.getString(1));
+        assertTrue(rs.next());
+
+        expected = "9.2, 3.4";
+        assertEquals(expected, rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionWithUpsertSelect2() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+
+        String ddl = "CREATE TABLE source (region_name VARCHAR PRIMARY KEY,varchars VARCHAR[])";
+        conn.createStatement().execute(ddl);
+
+        ddl = "CREATE TABLE target (region_name VARCHAR PRIMARY KEY,varchar VARCHAR)";
+        conn.createStatement().execute(ddl);
+
+        String dml = "UPSERT INTO source(region_name,varchars) VALUES('SF Bay Area', ARRAY['hello', '-)'])";
+        conn.createStatement().execute(dml);
+
+        dml = "UPSERT INTO source(region_name,varchars) VALUES('SF Bay Area2', ARRAY['hello', '-('])";
+        conn.createStatement().execute(dml);
+        conn.commit();
+
+        dml = "UPSERT INTO target(region_name, varchar) SELECT region_name, ARRAY_TO_STRING(varchars, ':') FROM source";
+        conn.createStatement().execute(dml);
+        conn.commit();
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT varchar FROM target");
+        assertTrue(rs.next());
+
+        String expected = "hello:-)";
+        assertEquals(expected, rs.getString(1));
+        assertTrue(rs.next());
+
+        expected = "hello:-(";
+        assertEquals(expected, rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionWithUpsertSelect3() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+
+        String ddl = "CREATE TABLE source (region_name VARCHAR PRIMARY KEY,booleans BOOLEAN[])";
+        conn.createStatement().execute(ddl);
+
+        ddl = "CREATE TABLE target (region_name VARCHAR PRIMARY KEY,varchar VARCHAR)";
+        conn.createStatement().execute(ddl);
+
+        String dml = "UPSERT INTO source(region_name, booleans) VALUES('SF Bay Area', ARRAY[true, true])";
+        conn.createStatement().execute(dml);
+
+        dml = "UPSERT INTO source(region_name, booleans) VALUES('SF Bay Area2', ARRAY[false, false])";
+        conn.createStatement().execute(dml);
+        conn.commit();
+
+        dml = "UPSERT INTO target(region_name, varchar) SELECT region_name, ARRAY_TO_STRING(booleans, ', ') FROM source";
+        conn.createStatement().execute(dml);
+        conn.commit();
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT varchar FROM target");
+        assertTrue(rs.next());
+
+        String expected = "true, true";
+        assertEquals(expected, rs.getString(1));
+        assertTrue(rs.next());
+
+        expected = "false, false";
+        assertEquals(expected, rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionInWhere1() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT region_name FROM regions WHERE '2345,46345,23234,456' = ARRAY_TO_STRING(integers,',')");
+        assertTrue(rs.next());
+
+        assertEquals("SF Bay Area", rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionInWhere2() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT region_name FROM regions WHERE 'a,b,c' = ARRAY_TO_STRING(ARRAY['a', 'b', 'c'], ',')");
+        assertTrue(rs.next());
+
+        assertEquals("SF Bay Area", rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionInWhere3() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT region_name FROM regions WHERE '1.1,2.2,3.3' = ARRAY_TO_STRING(ARRAY[1.1, 2.2, 3.3], ',')");
+        assertTrue(rs.next());
+
+        assertEquals("SF Bay Area", rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionInWhere4() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT region_name FROM regions WHERE 'true,true,true' = ARRAY_TO_STRING(ARRAY[true, true, true], ',')");
+        assertTrue(rs.next());
+
+        assertEquals("SF Bay Area", rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionInWhere5() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT region_name FROM regions WHERE 'a, bbbb, c, ddd, e' = ARRAY_TO_STRING(ARRAY['a', 'bbbb', 'c' , 'ddd', 'e'], ', ')");
+        assertTrue(rs.next());
+
+        assertEquals("SF Bay Area", rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionInWhere6() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT region_name FROM regions WHERE ARRAY_TO_STRING(ARRAY[1,2,3], varchar1) = '1, 2, 3'");
+        assertTrue(rs.next());
+
+        assertEquals("SF Bay Area", rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionInWhere7() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT region_name FROM regions WHERE ARRAY_TO_STRING(varchars, varchar1) = '2345, 46345, 23234'");
+        assertTrue(rs.next());
+
+        assertEquals("SF Bay Area", rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionWithNulls1() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_TO_STRING(ARRAY['a', NULL, 'b'], ', ','*') FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        String expected = "a, *, b";
+
+        assertEquals(expected, rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionWithNulls2() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_TO_STRING(ARRAY['a', NULL, 'b'], ', ') FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        String expected = "a, b";
+
+        assertEquals(expected, rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionWithNulls3() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_TO_STRING(ARRAY[NULL, 'a', 'b'], ', ', '*') FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        String expected = "*, a, b";
+
+        assertEquals(expected, rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionWithNulls4() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_TO_STRING(ARRAY[NULL, 'a', 'b'], ', ') FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        String expected = "a, b";
+
+        assertEquals(expected, rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionWithNulls5() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_TO_STRING(ARRAY['a', 'b', NULL], ', ', '*') FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        String expected = "a, b, *";
+
+        assertEquals(expected, rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionWithNulls6() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_TO_STRING(ARRAY['a', 'b', NULL], ', ') FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        String expected = "a, b";
+
+        assertEquals(expected, rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionWithNulls7() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_TO_STRING(ARRAY[NULL, NULL, 'a', 'b', NULL, 'c', 'd', NULL, 'e', NULL, NULL], ', ', '*') FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        String expected = "*, *, a, b, *, c, d, *, e, *, *";
+
+        assertEquals(expected, rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayToStringFunctionWithNulls8() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_TO_STRING(ARRAY[NULL, NULL, 'a', 'b', NULL, 'c', 'd', NULL, 'e', NULL, NULL], ', ') FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        String expected = "a, b, c, d, e";
+
+        assertEquals(expected, rs.getString(1));
+        assertFalse(rs.next());
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/236ce1c8/phoenix-core/src/main/java/org/apache/phoenix/expression/ExpressionType.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/ExpressionType.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/ExpressionType.java
index ef14e6a..3c364c0 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/expression/ExpressionType.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/ExpressionType.java
@@ -29,6 +29,7 @@ import org.apache.phoenix.expression.function.ArrayFillFunction;
 import org.apache.phoenix.expression.function.ArrayIndexFunction;
 import org.apache.phoenix.expression.function.ArrayLengthFunction;
 import org.apache.phoenix.expression.function.ArrayPrependFunction;
+import org.apache.phoenix.expression.function.ArrayToStringFunction;
 import org.apache.phoenix.expression.function.ByteBasedRegexpReplaceFunction;
 import org.apache.phoenix.expression.function.ByteBasedRegexpSplitFunction;
 import org.apache.phoenix.expression.function.ByteBasedRegexpSubstrFunction;
@@ -249,7 +250,8 @@ public enum ExpressionType {
     ExpFunction(ExpFunction.class),
     PowerFunction(PowerFunction.class),
     ArrayConcatFunction(ArrayConcatFunction.class),
-    ArrayFillFunction(ArrayFillFunction.class)
+    ArrayFillFunction(ArrayFillFunction.class),
+    ArrayToStringFunction(ArrayToStringFunction.class)
     ;
 
     ExpressionType(Class<? extends Expression> clazz) {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/236ce1c8/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToStringFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToStringFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToStringFunction.java
new file mode 100644
index 0000000..9102df4
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToStringFunction.java
@@ -0,0 +1,84 @@
+/*
+ * 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.expression.function;
+
+import java.util.List;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.parse.FunctionParseNode;
+import org.apache.phoenix.schema.SortOrder;
+import org.apache.phoenix.schema.tuple.Tuple;
+import org.apache.phoenix.schema.types.*;
+
+@FunctionParseNode.BuiltInFunction(name = ArrayToStringFunction.NAME, args = {
+        @FunctionParseNode.Argument(allowedTypes = {PBinaryArray.class, PVarbinaryArray.class}),
+        @FunctionParseNode.Argument(allowedTypes = {PVarchar.class, PChar.class}),
+        @FunctionParseNode.Argument(allowedTypes = {PVarchar.class, PChar.class}, defaultValue = "null")})
+public class ArrayToStringFunction extends ScalarFunction {
+    public static final String NAME = "ARRAY_TO_STRING";
+
+    public ArrayToStringFunction() {
+    }
+
+    public ArrayToStringFunction(List<Expression> children) {
+        super(children);
+    }
+
+    @Override
+    public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
+        Expression delimiterExpr = children.get(1);
+        if (!delimiterExpr.evaluate(tuple, ptr)) {
+            return false;
+        } else if (ptr.getLength() == 0) {
+            return true;
+        }
+        String delimiter = (String) delimiterExpr.getDataType().toObject(ptr, delimiterExpr.getSortOrder(), delimiterExpr.getMaxLength(), delimiterExpr.getScale());
+
+        Expression arrayExpr = children.get(0);
+        if (!arrayExpr.evaluate(tuple, ptr)) {
+            return false;
+        } else if (ptr.getLength() == 0) {
+            return true;
+        }
+        PhoenixArray array = (PhoenixArray) arrayExpr.getDataType().toObject(ptr, arrayExpr.getSortOrder(), arrayExpr.getMaxLength(), arrayExpr.getScale());
+
+        Expression nullExpr = children.get(2);
+        String nullString = null;
+        if (nullExpr.evaluate(tuple, ptr) && ptr.getLength() != 0) {
+            nullString = (String) nullExpr.getDataType().toObject(ptr, nullExpr.getSortOrder(), nullExpr.getMaxLength(), nullExpr.getScale());
+        }
+
+        return PArrayDataType.arrayToString(ptr, array, delimiter, nullString, getSortOrder());
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public PDataType getDataType() {
+        return PVarchar.INSTANCE;
+    }
+
+    @Override
+    public SortOrder getSortOrder() {
+        return children.get(0).getSortOrder();
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/236ce1c8/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PArrayDataType.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PArrayDataType.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PArrayDataType.java
index 9ca64c5..bad02e4 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PArrayDataType.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PArrayDataType.java
@@ -957,6 +957,36 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
         return true;
     }
 
+    public static boolean arrayToString(ImmutableBytesWritable ptr, PhoenixArray array, String delimiter, String nullString, SortOrder sortOrder) {
+        StringBuilder result = new StringBuilder();
+        boolean delimiterPending = false;
+        for (int i = 0; i < array.getDimensions() - 1; i++) {
+            Object element = array.getElement(i);
+            if (element == null) {
+                if (nullString != null) {
+                    result.append(nullString);
+                }
+            } else {
+                result.append(element.toString());
+                delimiterPending = true;
+            }
+            if (nullString != null || (array.getElement(i + 1) != null && delimiterPending)) {
+                result.append(delimiter);
+                delimiterPending = false;
+            }
+        }
+        Object element = array.getElement(array.getDimensions() - 1);
+        if (element == null) {
+            if (nullString != null) {
+                result.append(nullString);
+            }
+        } else {
+            result.append(element.toString());
+        }
+        ptr.set(PVarchar.INSTANCE.toBytes(result.toString(), sortOrder));
+        return true;
+    }
+
     public static int serailizeOffsetArrayIntoStream(DataOutputStream oStream, TrustedByteArrayOutputStream byteStream,
             int noOfElements, int maxOffset, int[] offsetPos) throws IOException {
         int offsetPosition = (byteStream.size());

http://git-wip-us.apache.org/repos/asf/phoenix/blob/236ce1c8/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayToStringFunctionTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayToStringFunctionTest.java b/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayToStringFunctionTest.java
new file mode 100644
index 0000000..92eb6b5
--- /dev/null
+++ b/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayToStringFunctionTest.java
@@ -0,0 +1,374 @@
+/*
+ * 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.expression;
+
+import static org.junit.Assert.assertEquals;
+
+import java.math.BigDecimal;
+import java.sql.Date;
+import java.sql.SQLException;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.List;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.phoenix.expression.function.ArrayToStringFunction;
+import org.apache.phoenix.schema.SortOrder;
+import org.apache.phoenix.schema.types.*;
+import org.junit.Test;
+
+import com.google.common.collect.Lists;
+
+public class ArrayToStringFunctionTest {
+
+    private static void testExpression(LiteralExpression array, LiteralExpression delimiter, LiteralExpression nullString, String expected)
+            throws SQLException {
+        List<Expression> expressions = Lists.newArrayList((Expression) array);
+        expressions.add(delimiter);
+        expressions.add(nullString);
+
+        Expression arrayToStringFunction = new ArrayToStringFunction(expressions);
+        ImmutableBytesWritable ptr = new ImmutableBytesWritable();
+        arrayToStringFunction.evaluate(null, ptr);
+        String result = (String) arrayToStringFunction.getDataType().toObject(ptr, arrayToStringFunction.getSortOrder(), arrayToStringFunction.getMaxLength(), arrayToStringFunction.getScale());
+        assertEquals(expected, result);
+    }
+
+    private static void test(PhoenixArray array, PDataType arrayDataType, Integer arrMaxLen, Integer arrScale, String delimiter, String nullString, String expected, SortOrder arraySortOrder, SortOrder delimiterSortOrder, SortOrder nullStringSortOrder) throws SQLException {
+        LiteralExpression arrayLiteral, delimiterLiteral, nullStringLiteral;
+        arrayLiteral = LiteralExpression.newConstant(array, arrayDataType, arrMaxLen, arrScale, arraySortOrder, Determinism.ALWAYS);
+        delimiterLiteral = LiteralExpression.newConstant(delimiter, PVarchar.INSTANCE, null, null, delimiterSortOrder, Determinism.ALWAYS);
+        nullStringLiteral = LiteralExpression.newConstant(nullString, PVarchar.INSTANCE, null, null, nullStringSortOrder, Determinism.ALWAYS);
+        testExpression(arrayLiteral, delimiterLiteral, nullStringLiteral, expected);
+    }
+
+    @Test
+    public void testInt1() throws SQLException {
+        PDataType type = PIntegerArray.INSTANCE;
+        PDataType base = PInteger.INSTANCE;
+        Object[] o1 = new Object[]{1, 2, 3, 4, 5};
+        PhoenixArray arr = new PhoenixArray.PrimitiveIntPhoenixArray(base, o1);
+        String delimiter = ",";
+        String nullString = "*";
+        String expected = "1,2,3,4,5";
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC);
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC);
+    }
+
+    @Test
+    public void testInt2() throws SQLException {
+        PDataType type = PIntegerArray.INSTANCE;
+        PDataType base = PInteger.INSTANCE;
+        Object[] o1 = new Object[]{1, 2, 3, 4, 5};
+        PhoenixArray arr = new PhoenixArray.PrimitiveIntPhoenixArray(base, o1);
+        String delimiter = ",";
+        String nullString = "";
+        String expected = "1,2,3,4,5";
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC);
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC);
+    }
+
+    @Test
+    public void testInt3() throws SQLException {
+        PDataType type = PIntegerArray.INSTANCE;
+        PDataType base = PInteger.INSTANCE;
+        Object[] o1 = new Object[]{1, 2, 3, 4, 5};
+        PhoenixArray arr = new PhoenixArray.PrimitiveIntPhoenixArray(base, o1);
+        String delimiter = "";
+        String nullString = "";
+        String expected = null;
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC);
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC);
+    }
+
+    @Test
+    public void testInt4() throws SQLException {
+        PDataType type = PIntegerArray.INSTANCE;
+        PDataType base = PInteger.INSTANCE;
+        Object[] o1 = new Object[]{1};
+        PhoenixArray arr = new PhoenixArray.PrimitiveIntPhoenixArray(base, o1);
+        String delimiter = ",";
+        String nullString = "";
+        String expected = "1";
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC);
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC);
+    }
+
+    @Test
+    public void testFloat1() throws SQLException {
+        PDataType type = PFloatArray.INSTANCE;
+        PDataType base = PFloat.INSTANCE;
+        Object[] o1 = new Object[]{(float) 1.1, (float) 2.2, (float) 3.3, (float) 4.4, (float) 5.5};
+        PhoenixArray arr = new PhoenixArray.PrimitiveFloatPhoenixArray(base, o1);
+        String delimiter = ",";
+        String nullString = "*";
+        String expected = "1.1,2.2,3.3,4.4,5.5";
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC);
+    }
+
+    @Test
+    public void testFloat2() throws SQLException {
+        PDataType type = PFloatArray.INSTANCE;
+        PDataType base = PFloat.INSTANCE;
+        Object[] o1 = new Object[]{(float) 1.1, (float) 2.2, (float) 3.3, (float) 4.4, (float) 5.5};
+        PhoenixArray arr = new PhoenixArray.PrimitiveFloatPhoenixArray(base, o1);
+        String delimiter = ", ";
+        String nullString = "*";
+        String expected = "1.1, 2.2, 3.3, 4.4, 5.5";
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC);
+    }
+
+    @Test
+    public void testDate() throws SQLException {
+        PDataType type = PDateArray.INSTANCE;
+        PDataType base = PDate.INSTANCE;
+        Object[] o1 = new Object[]{new Date(0l), new Date(0l), new Date(0l)};
+        PhoenixArray arr = new PhoenixArray(base, o1);
+        String delimiter = ", ";
+        String nullString = "*";
+        String expected = "1970-01-01, 1970-01-01, 1970-01-01";
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC);
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC);
+    }
+
+    @Test
+    public void testTime() throws SQLException {
+        PDataType type = PTimeArray.INSTANCE;
+        PDataType base = PTime.INSTANCE;
+        Object[] o1 = new Object[]{new Time(0l), new Time(0l), new Time(0l)};
+        PhoenixArray arr = new PhoenixArray(base, o1);
+        String delimiter = ", ";
+        String nullString = "*";
+        String expected = "";
+        for (int i = 0; i < o1.length - 1; i++) {
+            expected += o1[i].toString() + ", ";
+        }
+        expected += o1[o1.length - 1];
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC);
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC);
+    }
+
+    @Test
+    public void testTimestamp() throws SQLException {
+        PDataType type = PTimestampArray.INSTANCE;
+        PDataType base = PTimestamp.INSTANCE;
+        Object[] o1 = new Object[]{new Timestamp(0l), new Timestamp(0l), new Timestamp(0l)};
+        PhoenixArray arr = new PhoenixArray(base, o1);
+        String delimiter = ", ";
+        String nullString = "*";
+        String expected = "";
+        for (int i = 0; i < o1.length - 1; i++) {
+            expected += o1[i].toString() + ", ";
+        }
+        expected += o1[o1.length - 1];
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC);
+    }
+
+    @Test
+    public void testVarchar1() throws SQLException {
+        PDataType type = PVarcharArray.INSTANCE;
+        PDataType base = PVarchar.INSTANCE;
+        Object[] o1 = new Object[]{"hello", null, "hello", null};
+        PhoenixArray arr = new PhoenixArray(base, o1);
+        String delimiter = ", ";
+        String nullString = "*";
+        String expected = "hello, *, hello, *";
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC);
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC);
+    }
+
+    @Test
+    public void testVarchar2() throws SQLException {
+        PDataType type = PVarcharArray.INSTANCE;
+        PDataType base = PVarchar.INSTANCE;
+        Object[] o1 = new Object[]{"hello", null, "hello", null, null};
+        PhoenixArray arr = new PhoenixArray(base, o1);
+        String delimiter = ", ";
+        String nullString = "*";
+        String expected = "hello, *, hello, *, *";
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC);
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC);
+    }
+
+    @Test
+    public void testVarchar3() throws SQLException {
+        PDataType type = PVarcharArray.INSTANCE;
+        PDataType base = PVarchar.INSTANCE;
+        Object[] o1 = new Object[]{"hello", null, "hello", null, null};
+        PhoenixArray arr = new PhoenixArray(base, o1);
+        String delimiter = ", ";
+        String nullString = "";
+        String expected = "hello, hello";
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC);
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC);
+    }
+
+    @Test
+    public void testVarchar4() throws SQLException {
+        PDataType type = PVarcharArray.INSTANCE;
+        PDataType base = PVarchar.INSTANCE;
+        Object[] o1 = new Object[]{null, "hello", "hello", null, null};
+        PhoenixArray arr = new PhoenixArray(base, o1);
+        String delimiter = ", ";
+        String nullString = "";
+        String expected = "hello, hello";
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC);
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC);
+    }
+
+    @Test
+    public void testVarchar5() throws SQLException {
+        PDataType type = PVarcharArray.INSTANCE;
+        PDataType base = PVarchar.INSTANCE;
+        Object[] o1 = new Object[]{"hello"};
+        PhoenixArray arr = new PhoenixArray(base, o1);
+        String delimiter = ", ";
+        String nullString = "";
+        String expected = "hello";
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC);
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC);
+    }
+
+    @Test
+    public void testVarchar6() throws SQLException {
+        PDataType type = PVarcharArray.INSTANCE;
+        PDataType base = PVarchar.INSTANCE;
+        Object[] o1 = new Object[]{null, null, null, null, "hello"};
+        PhoenixArray arr = new PhoenixArray(base, o1);
+        String delimiter = ", ";
+        String nullString = "";
+        String expected = "hello";
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC);
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC);
+    }
+
+    @Test
+    public void testVarchar7() throws SQLException {
+        PDataType type = PVarcharArray.INSTANCE;
+        PDataType base = PVarchar.INSTANCE;
+        Object[] o1 = new Object[]{null, null, null, null, "hello"};
+        PhoenixArray arr = new PhoenixArray(base, o1);
+        String delimiter = ", ";
+        String nullString = "*";
+        String expected = "*, *, *, *, hello";
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC);
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC);
+    }
+
+    @Test
+    public void testDouble() throws SQLException {
+        PDataType type = PDoubleArray.INSTANCE;
+        PDataType base = PDouble.INSTANCE;
+        Object[] o1 = new Object[]{23.4, 56.8, 2.4};
+        PhoenixArray arr = new PhoenixArray.PrimitiveDoublePhoenixArray(base, o1);
+        String delimiter = ",";
+        String nullString = "*";
+        String expected = "23.4,56.8,2.4";
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC);
+    }
+
+    @Test
+    public void testTinyint() throws SQLException {
+        PDataType type = PTinyintArray.INSTANCE;
+        PDataType base = PTinyint.INSTANCE;
+        Object[] o1 = new Object[]{(byte) 2, (byte) 4, (byte) 5};
+        PhoenixArray arr = new PhoenixArray.PrimitiveBytePhoenixArray(base, o1);
+        String delimiter = ",";
+        String nullString = "*";
+        String expected = "2,4,5";
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC);
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC);
+    }
+
+    @Test
+    public void testSmallint() throws SQLException {
+        PDataType type = PSmallintArray.INSTANCE;
+        PDataType base = PSmallint.INSTANCE;
+        Object[] o1 = new Object[]{(short) 6, (short) 7, (short) 8};
+        PhoenixArray arr = new PhoenixArray.PrimitiveShortPhoenixArray(base, o1);
+        String delimiter = ",";
+        String nullString = "*";
+        String expected = "6,7,8";
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC);
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC);
+    }
+
+    @Test
+    public void testBoolean() throws SQLException {
+        PDataType type = PBooleanArray.INSTANCE;
+        PDataType base = PBoolean.INSTANCE;
+        Object[] o1 = new Object[]{true, false, true};
+        PhoenixArray arr = new PhoenixArray.PrimitiveBooleanPhoenixArray(base, o1);
+        String delimiter = ",";
+        String nullString = "*";
+        String expected = "true,false,true";
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC);
+    }
+
+    @Test
+    public void testLong() throws SQLException {
+        PDataType type = PLongArray.INSTANCE;
+        PDataType base = PLong.INSTANCE;
+        Object[] o1 = new Object[]{(long) 23, (long) 34, (long) 45};
+        PhoenixArray arr = new PhoenixArray.PrimitiveLongPhoenixArray(base, o1);
+        String delimiter = ",";
+        String nullString = "*";
+        String expected = "23,34,45";
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC);
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC);
+    }
+
+    @Test
+    public void testDecimal1() throws SQLException {
+        PDataType type = PDecimalArray.INSTANCE;
+        PDataType base = PDecimal.INSTANCE;
+        Object[] o1 = new Object[]{BigDecimal.valueOf(23.45), BigDecimal.valueOf(2.345), BigDecimal.valueOf(234.5)};
+        PhoenixArray arr = new PhoenixArray(base, o1);
+        String delimiter = ",";
+        String nullString = "*";
+        String expected = "23.45,2.345,234.5";
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC);
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC);
+    }
+
+    @Test
+    public void testDecimal2() throws SQLException {
+        PDataType type = PDecimalArray.INSTANCE;
+        PDataType base = PDecimal.INSTANCE;
+        Object[] o1 = new Object[]{BigDecimal.valueOf(23.45), BigDecimal.valueOf(2.345), BigDecimal.valueOf(234.5), null};
+        PhoenixArray arr = new PhoenixArray(base, o1);
+        String delimiter = ",";
+        String nullString = "*";
+        String expected = "23.45,2.345,234.5,*";
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC);
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC);
+    }
+
+    @Test
+    public void testDecimal3() throws SQLException {
+        PDataType type = PDecimalArray.INSTANCE;
+        PDataType base = PDecimal.INSTANCE;
+        Object[] o1 = new Object[]{BigDecimal.valueOf(23.45), BigDecimal.valueOf(2.345), null, BigDecimal.valueOf(234.5)};
+        PhoenixArray arr = new PhoenixArray(base, o1);
+        String delimiter = ",";
+        String nullString = "*";
+        String expected = "23.45,2.345,*,234.5";
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.ASC, SortOrder.ASC, SortOrder.ASC);
+        test(arr, type, null, null, delimiter, nullString, expected, SortOrder.DESC, SortOrder.ASC, SortOrder.ASC);
+    }
+}


[42/50] [abbrv] phoenix git commit: PHOENIX-2121 Fix flapping RowValueConstructorIT

Posted by ma...@apache.org.
PHOENIX-2121 Fix flapping RowValueConstructorIT


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/553d3ccf
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/553d3ccf
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/553d3ccf

Branch: refs/heads/calcite
Commit: 553d3ccf40fbef78de9d8b14e2b774d2a2fc6597
Parents: a8f0d76
Author: Samarth <sa...@salesforce.com>
Authored: Wed Jul 15 12:48:55 2015 -0700
Committer: Samarth <sa...@salesforce.com>
Committed: Wed Jul 15 12:48:55 2015 -0700

----------------------------------------------------------------------
 .../phoenix/end2end/RowValueConstructorIT.java  | 46 +++++++++++---------
 1 file changed, 25 insertions(+), 21 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/553d3ccf/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java
index e5cfeb0..5bf0a1e 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java
@@ -1430,21 +1430,22 @@ public class RowValueConstructorIT extends BaseClientManagedTimeIT {
     @Test
     public void testCountDistinct1() throws Exception {
         Connection conn = nextConnection(getUrl());
-        String ddl = "CREATE TABLE regions1 (region_name VARCHAR PRIMARY KEY, a INTEGER, b INTEGER)";
+        String tableName = "testCountDistinct1rvc";
+        String ddl = "CREATE TABLE " + tableName + " (region_name VARCHAR PRIMARY KEY, a INTEGER, b INTEGER)";
         conn.createStatement().execute(ddl);
         
         conn = nextConnection(getUrl());
-        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO regions1(region_name, a, b) VALUES('a', 6,3)");
+        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO  " + tableName + " (region_name, a, b) VALUES('a', 6,3)");
         stmt.execute();
-        stmt = conn.prepareStatement("UPSERT INTO regions1(region_name, a, b) VALUES('b', 2,4)");
+        stmt = conn.prepareStatement("UPSERT INTO  " + tableName + " (region_name, a, b) VALUES('b', 2,4)");
         stmt.execute();
-        stmt = conn.prepareStatement("UPSERT INTO regions1(region_name, a, b) VALUES('c', 6,3)");
+        stmt = conn.prepareStatement("UPSERT INTO  " + tableName + " (region_name, a, b) VALUES('c', 6,3)");
         stmt.execute();
         conn.commit();
 
         conn = nextConnection(getUrl());
         ResultSet rs;
-        rs = conn.createStatement().executeQuery("SELECT COUNT(DISTINCT (a,b)) from regions1");
+        rs = conn.createStatement().executeQuery("SELECT COUNT(DISTINCT (a,b)) from " + tableName);
         assertTrue(rs.next());
         assertEquals(2, rs.getInt(1));
         conn.close();
@@ -1453,22 +1454,23 @@ public class RowValueConstructorIT extends BaseClientManagedTimeIT {
     @Test
     public void testCountDistinct2() throws Exception {
         Connection conn = nextConnection(getUrl());
-        String ddl = "CREATE TABLE regions2 (region_name VARCHAR PRIMARY KEY, a VARCHAR, b VARCHAR)";
+        String tableName = "testCountDistinct2rvc";
+        String ddl = "CREATE TABLE  " + tableName + "  (region_name VARCHAR PRIMARY KEY, a VARCHAR, b VARCHAR)";
         conn.createStatement().execute(ddl);
         conn.commit();
         
         conn = nextConnection(getUrl());
-        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO regions2(region_name, a, b) VALUES('a', 'fooo','abc')");
+        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO  " + tableName + " (region_name, a, b) VALUES('a', 'fooo','abc')");
         stmt.execute();
-        stmt = conn.prepareStatement("UPSERT INTO regions2(region_name, a, b) VALUES('b', 'off','bac')");
+        stmt = conn.prepareStatement("UPSERT INTO  " + tableName + " (region_name, a, b) VALUES('b', 'off','bac')");
         stmt.execute();
-        stmt = conn.prepareStatement("UPSERT INTO regions2(region_name, a, b) VALUES('c', 'fooo', 'abc')");
+        stmt = conn.prepareStatement("UPSERT INTO  " + tableName + " (region_name, a, b) VALUES('c', 'fooo', 'abc')");
         stmt.execute();
         conn.commit();
 
         conn = nextConnection(getUrl());
         ResultSet rs;
-        rs = conn.createStatement().executeQuery("SELECT COUNT(DISTINCT (a,b)) from regions2");
+        rs = conn.createStatement().executeQuery("SELECT COUNT(DISTINCT (a,b)) from  " + tableName);
         assertTrue(rs.next());
         assertEquals(2, rs.getInt(1));
     }
@@ -1476,23 +1478,24 @@ public class RowValueConstructorIT extends BaseClientManagedTimeIT {
     @Test
     public void testCountDistinct3() throws Exception {
         Connection conn = nextConnection(getUrl());
-        String ddl = "CREATE TABLE regions3 (region_name VARCHAR PRIMARY KEY, a Boolean, b Boolean)";
+        String tableName = "testCountDistinct3rvc";
+        String ddl = "CREATE TABLE  " + tableName + "  (region_name VARCHAR PRIMARY KEY, a Boolean, b Boolean)";
         conn.createStatement().execute(ddl);
         
         conn = nextConnection(getUrl());
-        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO regions3(region_name, a, b) VALUES('a', true, true)");
+        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO  " + tableName + " (region_name, a, b) VALUES('a', true, true)");
         stmt.execute();
-        stmt = conn.prepareStatement("UPSERT INTO regions3(region_name, a, b) VALUES('b', true, False)");
+        stmt = conn.prepareStatement("UPSERT INTO  " + tableName + " (region_name, a, b) VALUES('b', true, False)");
         stmt.execute();
-        stmt = conn.prepareStatement("UPSERT INTO regions3(region_name, a, b) VALUES('c', true, true)");
+        stmt = conn.prepareStatement("UPSERT INTO  " + tableName + " (region_name, a, b) VALUES('c', true, true)");
         stmt.execute();
-        stmt = conn.prepareStatement("UPSERT INTO regions3(region_name, a, b) VALUES('d', true, false)");
+        stmt = conn.prepareStatement("UPSERT INTO  " + tableName + " (region_name, a, b) VALUES('d', true, false)");
         stmt.execute();
         conn.commit();
 
         conn = nextConnection(getUrl());
         ResultSet rs;
-        rs = conn.createStatement().executeQuery("SELECT COUNT(DISTINCT (a,b)) from regions3");
+        rs = conn.createStatement().executeQuery("SELECT COUNT(DISTINCT (a,b)) from  " + tableName);
         assertTrue(rs.next());
         assertEquals(2, rs.getInt(1));
     }
@@ -1500,21 +1503,22 @@ public class RowValueConstructorIT extends BaseClientManagedTimeIT {
     @Test
     public void testCountDistinct4() throws Exception {
         Connection conn = nextConnection(getUrl());
-        String ddl = "CREATE TABLE regions4 (region_name VARCHAR PRIMARY KEY, a VARCHAR, b VARCHAR)";
+        String tableName = "testCountDistinct4rvc";
+        String ddl = "CREATE TABLE  " + tableName + "  (region_name VARCHAR PRIMARY KEY, a VARCHAR, b VARCHAR)";
         conn.createStatement().execute(ddl);
         
         conn = nextConnection(getUrl());
-        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO regions4(region_name, a, b) VALUES('a', 'fooo','abc')");
+        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO  " + tableName + " (region_name, a, b) VALUES('a', 'fooo','abc')");
         stmt.execute();
-        stmt = conn.prepareStatement("UPSERT INTO regions4(region_name, a, b) VALUES('b', 'off','bac')");
+        stmt = conn.prepareStatement("UPSERT INTO  " + tableName + " (region_name, a, b) VALUES('b', 'off','bac')");
         stmt.execute();
-        stmt = conn.prepareStatement("UPSERT INTO regions4(region_name, a, b) VALUES('c', 'foo', 'abc')");
+        stmt = conn.prepareStatement("UPSERT INTO  " + tableName + " (region_name, a, b) VALUES('c', 'foo', 'abc')");
         stmt.execute();
         conn.commit();
         
         conn = nextConnection(getUrl());
         ResultSet rs;
-        rs = conn.createStatement().executeQuery("SELECT COUNT(DISTINCT (a,b)) from regions4");
+        rs = conn.createStatement().executeQuery("SELECT COUNT(DISTINCT (a,b)) from  " + tableName);
         assertTrue(rs.next());
         assertEquals(3, rs.getInt(1));
     }


[02/50] [abbrv] phoenix git commit: PHOENIX-2036 - PhoenixConfigurationUtil should provide a pre-normalize table name to PhoenixRuntime

Posted by ma...@apache.org.
PHOENIX-2036 - PhoenixConfigurationUtil should provide a pre-normalize table name to PhoenixRuntime


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/c398e182
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/c398e182
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/c398e182

Branch: refs/heads/calcite
Commit: c398e1823d8fe16c729e3b75eaf754eef0702856
Parents: be5aba5
Author: ravimagham <ra...@apache.org>
Authored: Sat Jul 4 10:08:00 2015 -0700
Committer: ravimagham <ra...@apache.org>
Committed: Sat Jul 4 10:08:00 2015 -0700

----------------------------------------------------------------------
 .../util/PhoenixConfigurationUtilTest.java      | 28 +++++++++++++--
 .../org/apache/phoenix/util/QueryUtilTest.java  | 36 ++++++++++++++++++--
 2 files changed, 58 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/c398e182/phoenix-core/src/test/java/org/apache/phoenix/mapreduce/util/PhoenixConfigurationUtilTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/mapreduce/util/PhoenixConfigurationUtilTest.java b/phoenix-core/src/test/java/org/apache/phoenix/mapreduce/util/PhoenixConfigurationUtilTest.java
index f8f2a63..aa03501 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/mapreduce/util/PhoenixConfigurationUtilTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/mapreduce/util/PhoenixConfigurationUtilTest.java
@@ -73,7 +73,29 @@ public class PhoenixConfigurationUtilTest extends BaseConnectionlessQueryTest {
             configuration.set(HConstants.ZOOKEEPER_QUORUM, getUrl());
             PhoenixConfigurationUtil.setInputTableName(configuration, tableName);
             final String selectStatement = PhoenixConfigurationUtil.getSelectStatement(configuration);
-            final String expectedSelectStatement = "SELECT \"A_STRING\",\"A_BINARY\",\"0\".\"COL1\" FROM " + SchemaUtil.getEscapedArgument(tableName) ; 
+            final String expectedSelectStatement = "SELECT \"A_STRING\",\"A_BINARY\",\"0\".\"COL1\" FROM " + tableName ; 
+            assertEquals(expectedSelectStatement, selectStatement);
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testSelectStatementWithSchema() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TestUtil.TEST_PROPERTIES));
+        final String tableName = "TEST_TABLE";
+        final String schemaName = SchemaUtil.getEscapedArgument("schema");
+        final String fullTableName = SchemaUtil.getTableName(schemaName, tableName);
+        try {
+            String ddl = "CREATE TABLE "+ fullTableName + 
+                    "  (a_string varchar not null, a_binary varbinary not null, col1 integer" +
+                    "  CONSTRAINT pk PRIMARY KEY (a_string, a_binary))\n";
+            conn.createStatement().execute(ddl);
+            final Configuration configuration = new Configuration ();
+            configuration.set(HConstants.ZOOKEEPER_QUORUM, getUrl());
+            PhoenixConfigurationUtil.setInputTableName(configuration, fullTableName);
+            final String selectStatement = PhoenixConfigurationUtil.getSelectStatement(configuration);
+            final String expectedSelectStatement = "SELECT \"A_STRING\",\"A_BINARY\",\"0\".\"COL1\" FROM " + fullTableName; 
             assertEquals(expectedSelectStatement, selectStatement);
         } finally {
             conn.close();
@@ -94,7 +116,7 @@ public class PhoenixConfigurationUtilTest extends BaseConnectionlessQueryTest {
             PhoenixConfigurationUtil.setInputTableName(configuration, tableName);
             PhoenixConfigurationUtil.setSelectColumnNames(configuration, "A_BINARY");
             final String selectStatement = PhoenixConfigurationUtil.getSelectStatement(configuration);
-            final String expectedSelectStatement = "SELECT \"A_BINARY\" FROM " + SchemaUtil.getEscapedArgument(tableName) ; 
+            final String expectedSelectStatement = "SELECT \"A_BINARY\" FROM " + tableName ; 
             assertEquals(expectedSelectStatement, selectStatement);
         } finally {
             conn.close();
@@ -115,7 +137,7 @@ public class PhoenixConfigurationUtilTest extends BaseConnectionlessQueryTest {
             PhoenixConfigurationUtil.setSchemaType(configuration, SchemaType.QUERY);
             PhoenixConfigurationUtil.setInputTableName(configuration, tableName);
             final String selectStatement = PhoenixConfigurationUtil.getSelectStatement(configuration);
-            final String expectedSelectStatement = "SELECT \"ID\",\"0\".\"VCARRAY\" FROM " + SchemaUtil.getEscapedArgument(tableName) ; 
+            final String expectedSelectStatement = "SELECT \"ID\",\"0\".\"VCARRAY\" FROM " + tableName ; 
             assertEquals(expectedSelectStatement, selectStatement);
         } finally {
             conn.close();

http://git-wip-us.apache.org/repos/asf/phoenix/blob/c398e182/phoenix-core/src/test/java/org/apache/phoenix/util/QueryUtilTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/util/QueryUtilTest.java b/phoenix-core/src/test/java/org/apache/phoenix/util/QueryUtilTest.java
index 8446e9e..45f536d 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/util/QueryUtilTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/util/QueryUtilTest.java
@@ -17,6 +17,11 @@
  */
 package org.apache.phoenix.util;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 import java.sql.Types;
 import java.util.Properties;
 
@@ -26,8 +31,6 @@ import org.junit.Test;
 
 import com.google.common.collect.ImmutableList;
 
-import static org.junit.Assert.*;
-
 public class QueryUtilTest {
 
     private static final ColumnInfo ID_COLUMN = new ColumnInfo("ID", Types.BIGINT);
@@ -61,10 +64,37 @@ public class QueryUtilTest {
     @Test
     public void testConstructSelectStatement() {
         assertEquals(
-                "SELECT \"ID\",\"NAME\" FROM \"MYTAB\"",
+                "SELECT \"ID\",\"NAME\" FROM MYTAB",
                 QueryUtil.constructSelectStatement("MYTAB", ImmutableList.of(ID_COLUMN,NAME_COLUMN),null));
     }
 
+    @Test
+    public void testConstructSelectStatementWithSchema() {
+        assertEquals(
+                "SELECT \"ID\",\"NAME\" FROM A.MYTAB",
+                QueryUtil.constructSelectStatement("A.MYTAB", ImmutableList.of(ID_COLUMN,NAME_COLUMN),null));
+    }
+    
+    @Test
+    public void testConstructSelectStatementWithCaseSensitiveSchema() {
+        final String tableName = "MYTAB";
+        final String schemaName = SchemaUtil.getEscapedArgument("a");
+        final String fullTableName = SchemaUtil.getTableName(schemaName, tableName);
+        assertEquals(
+                "SELECT \"ID\",\"NAME\" FROM \"a\".MYTAB",
+                QueryUtil.constructSelectStatement(fullTableName, ImmutableList.of(ID_COLUMN,NAME_COLUMN),null));
+    }
+    
+    @Test
+    public void testConstructSelectStatementWithCaseSensitiveTable() {
+        final String tableName = SchemaUtil.getEscapedArgument("mytab");
+        final String schemaName = SchemaUtil.getEscapedArgument("a");
+        final String fullTableName = SchemaUtil.getTableName(schemaName, tableName);
+        assertEquals(
+                "SELECT \"ID\",\"NAME\" FROM \"a\".\"mytab\"",
+                QueryUtil.constructSelectStatement(fullTableName, ImmutableList.of(ID_COLUMN,NAME_COLUMN),null));
+    }
+    
     /**
      * Test that we create connection strings from the HBase Configuration that match the
      * expected syntax. Expected to log exceptions as it uses ZK host names that don't exist


[18/50] [abbrv] phoenix git commit: PHOENIX-1954 Reserve chunks of numbers for a sequence (Jan Fernando)

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/phoenix/blob/3b1bfa0d/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
index 69520b0..a9f455a 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
@@ -135,6 +135,7 @@ import org.apache.phoenix.schema.PTableType;
 import org.apache.phoenix.schema.ReadOnlyTableException;
 import org.apache.phoenix.schema.SaltingUtil;
 import org.apache.phoenix.schema.Sequence;
+import org.apache.phoenix.schema.SequenceAllocation;
 import org.apache.phoenix.schema.SequenceKey;
 import org.apache.phoenix.schema.TableAlreadyExistsException;
 import org.apache.phoenix.schema.TableNotFoundException;
@@ -2272,8 +2273,8 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
      * Verifies that sequences exist and reserves values for them if reserveValues is true
      */
     @Override
-    public void validateSequences(List<SequenceKey> sequenceKeys, long timestamp, long[] values, SQLException[] exceptions, Sequence.ValueOp action) throws SQLException {
-        incrementSequenceValues(sequenceKeys, timestamp, values, exceptions, action);
+    public void validateSequences(List<SequenceAllocation> sequenceAllocations, long timestamp, long[] values, SQLException[] exceptions, Sequence.ValueOp action) throws SQLException {
+        incrementSequenceValues(sequenceAllocations, timestamp, values, exceptions, action);
     }
 
     /**
@@ -2286,14 +2287,15 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
      *
      */
     @Override
-    public void incrementSequences(List<SequenceKey> sequenceKeys, long timestamp, long[] values, SQLException[] exceptions) throws SQLException {
-        incrementSequenceValues(sequenceKeys, timestamp, values, exceptions, Sequence.ValueOp.INCREMENT_SEQUENCE);
+    public void incrementSequences(List<SequenceAllocation> sequenceAllocations, long timestamp, long[] values, SQLException[] exceptions) throws SQLException {
+        incrementSequenceValues(sequenceAllocations, timestamp, values, exceptions, Sequence.ValueOp.INCREMENT_SEQUENCE);
     }
 
     @SuppressWarnings("deprecation")
-    private void incrementSequenceValues(List<SequenceKey> keys, long timestamp, long[] values, SQLException[] exceptions, Sequence.ValueOp op) throws SQLException {
-        List<Sequence> sequences = Lists.newArrayListWithExpectedSize(keys.size());
-        for (SequenceKey key : keys) {
+    private void incrementSequenceValues(List<SequenceAllocation> sequenceAllocations, long timestamp, long[] values, SQLException[] exceptions, Sequence.ValueOp op) throws SQLException {
+        List<Sequence> sequences = Lists.newArrayListWithExpectedSize(sequenceAllocations.size());
+        for (SequenceAllocation sequenceAllocation : sequenceAllocations) {
+            SequenceKey key = sequenceAllocation.getSequenceKey();
             Sequence newSequences = new Sequence(key);
             Sequence sequence = sequenceMap.putIfAbsent(key, newSequences);
             if (sequence == null) {
@@ -2312,11 +2314,11 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
             for (int i = 0; i < sequences.size(); i++) {
                 Sequence sequence = sequences.get(i);
                 try {
-                    values[i] = sequence.incrementValue(timestamp, op);
+                    values[i] = sequence.incrementValue(timestamp, op, sequenceAllocations.get(i).getNumAllocations());
                 } catch (EmptySequenceCacheException e) {
                     indexes[toIncrementList.size()] = i;
                     toIncrementList.add(sequence);
-                    Increment inc = sequence.newIncrement(timestamp, op);
+                    Increment inc = sequence.newIncrement(timestamp, op, sequenceAllocations.get(i).getNumAllocations());
                     incrementBatch.add(inc);
                 } catch (SQLException e) {
                     exceptions[i] = e;
@@ -2355,7 +2357,8 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
                 Sequence sequence = toIncrementList.get(i);
                 Result result = (Result)resultObjects[i];
                 try {
-                    values[indexes[i]] = sequence.incrementValue(result, op);
+                    long numToAllocate = Bytes.toLong(incrementBatch.get(i).getAttribute(SequenceRegionObserver.NUM_TO_ALLOCATE));
+                    values[indexes[i]] = sequence.incrementValue(result, op, numToAllocate);
                 } catch (SQLException e) {
                     exceptions[indexes[i]] = e;
                 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3b1bfa0d/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionlessQueryServicesImpl.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionlessQueryServicesImpl.java b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionlessQueryServicesImpl.java
index 4d582be..3fa0c1e 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionlessQueryServicesImpl.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionlessQueryServicesImpl.java
@@ -66,6 +66,7 @@ import org.apache.phoenix.schema.PTableImpl;
 import org.apache.phoenix.schema.PTableKey;
 import org.apache.phoenix.schema.PTableType;
 import org.apache.phoenix.schema.Sequence;
+import org.apache.phoenix.schema.SequenceAllocation;
 import org.apache.phoenix.schema.SequenceAlreadyExistsException;
 import org.apache.phoenix.schema.SequenceInfo;
 import org.apache.phoenix.schema.SequenceKey;
@@ -385,13 +386,13 @@ public class ConnectionlessQueryServicesImpl extends DelegateQueryServices imple
     }
 
     @Override
-    public void validateSequences(List<SequenceKey> sequenceKeys, long timestamp, long[] values,
-            SQLException[] exceptions, Sequence.ValueOp action) throws SQLException {
+    public void validateSequences(List<SequenceAllocation> sequenceAllocations, long timestamp,
+            long[] values, SQLException[] exceptions, Sequence.ValueOp action) throws SQLException {
         int i = 0;
-        for (SequenceKey key : sequenceKeys) {
-            SequenceInfo info = sequenceMap.get(key);
+        for (SequenceAllocation sequenceAllocation : sequenceAllocations) {
+            SequenceInfo info = sequenceMap.get(sequenceAllocation.getSequenceKey());
             if (info == null) {
-                exceptions[i] = new SequenceNotFoundException(key.getSchemaName(), key.getSequenceName());
+                exceptions[i] = new SequenceNotFoundException(sequenceAllocation.getSequenceKey().getSchemaName(), sequenceAllocation.getSequenceKey().getSequenceName());
             } else {
                 values[i] = info.sequenceValue;          
             }
@@ -400,10 +401,11 @@ public class ConnectionlessQueryServicesImpl extends DelegateQueryServices imple
     }
 
     @Override
-    public void incrementSequences(List<SequenceKey> sequenceKeys, long timestamp, long[] values,
-            SQLException[] exceptions) throws SQLException {
+    public void incrementSequences(List<SequenceAllocation> sequenceAllocations, long timestamp,
+            long[] values, SQLException[] exceptions) throws SQLException {
         int i = 0;
-		for (SequenceKey key : sequenceKeys) {
+		for (SequenceAllocation sequenceAllocation : sequenceAllocations) {
+		    SequenceKey key = sequenceAllocation.getSequenceKey();
 			SequenceInfo info = sequenceMap.get(key);
 			if (info == null) {
 				exceptions[i] = new SequenceNotFoundException(
@@ -429,7 +431,7 @@ public class ConnectionlessQueryServicesImpl extends DelegateQueryServices imple
         i = 0;
         for (SQLException e : exceptions) {
             if (e != null) {
-                sequenceMap.remove(sequenceKeys.get(i));
+                sequenceMap.remove(sequenceAllocations.get(i).getSequenceKey());
             }
             i++;
         }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3b1bfa0d/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateConnectionQueryServices.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateConnectionQueryServices.java b/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateConnectionQueryServices.java
index 2a98cd5..4153652 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateConnectionQueryServices.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateConnectionQueryServices.java
@@ -42,6 +42,7 @@ import org.apache.phoenix.schema.PName;
 import org.apache.phoenix.schema.PTable;
 import org.apache.phoenix.schema.PTableType;
 import org.apache.phoenix.schema.Sequence;
+import org.apache.phoenix.schema.SequenceAllocation;
 import org.apache.phoenix.schema.SequenceKey;
 import org.apache.phoenix.schema.stats.PTableStats;
 
@@ -183,15 +184,15 @@ public class DelegateConnectionQueryServices extends DelegateQueryServices imple
     }
 
     @Override
-    public void validateSequences(List<SequenceKey> sequenceKeys, long timestamp, long[] values,
-            SQLException[] exceptions, Sequence.ValueOp action) throws SQLException {
-        getDelegate().validateSequences(sequenceKeys, timestamp, values, exceptions, action);
+    public void validateSequences(List<SequenceAllocation> sequenceAllocations, long timestamp,
+            long[] values, SQLException[] exceptions, Sequence.ValueOp action) throws SQLException {
+        getDelegate().validateSequences(sequenceAllocations, timestamp, values, exceptions, action);
     }
 
     @Override
-    public void incrementSequences(List<SequenceKey> sequenceKeys, long timestamp, long[] values,
-            SQLException[] exceptions) throws SQLException {
-        getDelegate().incrementSequences(sequenceKeys, timestamp, values, exceptions);
+    public void incrementSequences(List<SequenceAllocation> sequenceAllocations, long timestamp,
+            long[] values, SQLException[] exceptions) throws SQLException {
+        getDelegate().incrementSequences(sequenceAllocations, timestamp, values, exceptions);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3b1bfa0d/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
index 9e74d2a..9d0c1aa 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
@@ -1255,7 +1255,7 @@ public class MetaDataClient {
                         dataTable.getTimeStamp());
                     long[] seqValues = new long[1];
                     SQLException[] sqlExceptions = new SQLException[1];
-                    connection.getQueryServices().incrementSequences(Collections.singletonList(key),
+                    connection.getQueryServices().incrementSequences(Collections.singletonList(new SequenceAllocation(key, 1)),
                             Math.max(timestamp, dataTable.getTimeStamp()), seqValues, sqlExceptions);
                     if (sqlExceptions[0] != null) {
                         throw sqlExceptions[0];

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3b1bfa0d/phoenix-core/src/main/java/org/apache/phoenix/schema/Sequence.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/Sequence.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/Sequence.java
index aeba58b..adca5e8 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/Sequence.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/Sequence.java
@@ -62,7 +62,7 @@ import com.google.common.math.LongMath;
 public class Sequence {
     public static final int SUCCESS = 0;
     
-    public enum ValueOp {VALIDATE_SEQUENCE, RESERVE_SEQUENCE, INCREMENT_SEQUENCE};
+    public enum ValueOp {VALIDATE_SEQUENCE, INCREMENT_SEQUENCE};
     public enum MetaOp {CREATE_SEQUENCE, DROP_SEQUENCE, RETURN_SEQUENCE};
     
     // create empty Sequence key values used while created a sequence row
@@ -144,7 +144,7 @@ public class Sequence {
         return value.isDeleted ? null : value;
     }
     
-    private long increment(SequenceValue value, ValueOp op) throws SQLException {       
+    private long increment(SequenceValue value, ValueOp op, long numToAllocate) throws SQLException {       
         boolean increasingSeq = value.incrementBy > 0 && op != ValueOp.VALIDATE_SEQUENCE;
         // check if the the sequence has already reached the min/max limit
         if (value.limitReached && op != ValueOp.VALIDATE_SEQUENCE) {           
@@ -165,7 +165,8 @@ public class Sequence {
             boolean overflowOrUnderflow=false;
             // advance currentValue while checking for overflow
             try {
-                value.currentValue = LongMath.checkedAdd(value.currentValue, value.incrementBy);
+                // advance by numToAllocate * the increment amount
+                value.currentValue = LongMath.checkedAdd(value.currentValue, numToAllocate * value.incrementBy);
             } catch (ArithmeticException e) {
                 overflowOrUnderflow = true;
             }
@@ -180,18 +181,92 @@ public class Sequence {
         return returnValue;
     }
 
-    public long incrementValue(long timestamp, ValueOp op) throws SQLException {
+    public long incrementValue(long timestamp, ValueOp op, long numToAllocate) throws SQLException {
         SequenceValue value = findSequenceValue(timestamp);
         if (value == null) {
             throw EMPTY_SEQUENCE_CACHE_EXCEPTION;
         }
-        if (value.currentValue == value.nextValue) {
+         
+        if (isSequenceCacheExhausted(numToAllocate, value)) {
             if (op == ValueOp.VALIDATE_SEQUENCE) {
                 return value.currentValue;
             }
             throw EMPTY_SEQUENCE_CACHE_EXCEPTION;
-        }    
-        return increment(value, op);
+        }
+        return increment(value, op, numToAllocate);
+    }
+    
+    /**
+     * This method first checks whether value.currentValue = value.nextValue, this check is what 
+     * determines whether we need to refresh the cache when evaluating NEXT VALUE FOR. Once 
+     * current value reaches the next value we know the cache is exhausted as we give sequence
+     * values out one at time. 
+     * 
+     * However for bulk allocations, evaluated by NEXT <n> VALUE FOR, we need a different check
+     * @see isSequenceCacheExhaustedForBulkAllocation
+     * 
+     * Using the bulk allocation method for determining if the cache is exhausted for both cases
+     * works in most of the cases, however when dealing with CYCLEs and overflow and underflow, things
+     * break down due to things like sign changes that can happen if we overflow from a positive to
+     * a negative number and vice versa. Therefore, leaving both checks in place. 
+     * 
+     */
+    private boolean isSequenceCacheExhausted(final long numToAllocate, final SequenceValue value) throws SQLException {
+        return value.currentValue == value.nextValue || (SequenceUtil.isBulkAllocation(numToAllocate) && isSequenceCacheExhaustedForBulkAllocation(numToAllocate, value));
+    }
+
+    /**
+     * This method checks whether there are sufficient values in the SequenceValue
+     * cached on the client to allocate the requested number of slots. It handles
+     * decreasing and increasing sequences as well as any overflows or underflows
+     * encountered.
+     */
+    private boolean isSequenceCacheExhaustedForBulkAllocation(final long numToAllocate, final SequenceValue value) throws SQLException {
+        long targetSequenceValue;
+        
+        performValidationForBulkAllocation(numToAllocate, value);
+        
+        try {
+            targetSequenceValue = LongMath.checkedAdd(value.currentValue, numToAllocate * value.incrementBy);
+        } catch (ArithmeticException e) {
+            // Perform a CheckedAdd to make sure if over/underflow 
+            // We don't treat this as the cache being exhausted as the current value may be valid in the case
+            // of no cycle, logic in increment() will take care of detecting we've hit the limit of the sequence
+            return false;
+        }
+
+        if (value.incrementBy > 0) {
+            return targetSequenceValue > value.nextValue;
+        } else {
+            return  targetSequenceValue < value.nextValue;    
+        }
+    }
+    
+    /**
+     * @throws SQLException with the correct error code if sequence limit is reached with
+     * this request for allocation or we attempt to perform a bulk allocation on a sequence
+     * with cycles.
+     */
+    private void performValidationForBulkAllocation(final long numToAllocate, final SequenceValue value)
+            throws SQLException {
+        boolean increasingSeq = value.incrementBy > 0 ? true : false;
+        
+        // We don't support Bulk Allocations on sequences that have the CYCLE flag set to true
+        // Check for this here so we fail on expression evaluation and don't allow corner case
+        // whereby a client requests less than cached number of slots on sequence with cycle to succeed
+        if (value.cycle && !SequenceUtil.isCycleAllowed(numToAllocate)) {
+            throw new SQLExceptionInfo.Builder(SQLExceptionCode.NUM_SEQ_TO_ALLOCATE_NOT_SUPPORTED)
+            .setSchemaName(key.getSchemaName())
+            .setTableName(key.getSequenceName())
+            .build().buildException();
+        }
+        
+        if (SequenceUtil.checkIfLimitReached(value.currentValue, value.minValue, value.maxValue, value.incrementBy, value.cacheSize, numToAllocate)) {
+            throw new SQLExceptionInfo.Builder(SequenceUtil.getLimitReachedErrorCode(increasingSeq))
+            .setSchemaName(key.getSchemaName())
+            .setTableName(key.getSequenceName())
+            .build().buildException();
+        }
     }
 
     public List<Append> newReturns() {
@@ -249,7 +324,7 @@ public class Sequence {
         return key;
     }
 
-    public long incrementValue(Result result, ValueOp op) throws SQLException {
+    public long incrementValue(Result result, ValueOp op, long numToAllocate) throws SQLException {
         // In this case, we don't definitely know the timestamp of the deleted sequence,
         // but we know anything older is likely deleted. Worse case, we remove a sequence
         // from the cache that we shouldn't have which will cause a gap in sequence values.
@@ -270,19 +345,21 @@ public class Sequence {
                 .build().buildException();
         }
         // If we found the sequence, we update our cache with the new value
-        SequenceValue value = new SequenceValue(result, op);
+        SequenceValue value = new SequenceValue(result, op, numToAllocate);
         insertSequenceValue(value);
-        return increment(value, op);
+        return increment(value, op, numToAllocate);
     }
 
+
     @SuppressWarnings("deprecation")
-    public Increment newIncrement(long timestamp, Sequence.ValueOp action) {
+    public Increment newIncrement(long timestamp, Sequence.ValueOp action, long numToAllocate) {
         Increment inc = new Increment(key.getKey());
         // It doesn't matter what we set the amount too - we always use the values we get
         // from the Get we do to prevent any race conditions. All columns that get added
         // are returned with their current value
         try {
             inc.setTimeRange(MetaDataProtocol.MIN_TABLE_TIMESTAMP, timestamp);
+            inc.setAttribute(SequenceRegionObserver.NUM_TO_ALLOCATE, Bytes.toBytes(numToAllocate));
         } catch (IOException e) {
             throw new RuntimeException(e); // Impossible
         }
@@ -413,7 +490,7 @@ public class Sequence {
             return this.incrementBy == 0;
         }
         
-        public SequenceValue(Result r, ValueOp op) {
+        public SequenceValue(Result r, ValueOp op, long numToAllocate) {
             KeyValue currentValueKV = getCurrentValueKV(r);
             KeyValue incrementByKV = getIncrementByKV(r);
             KeyValue cacheSizeKV = getCacheSizeKV(r);
@@ -429,8 +506,12 @@ public class Sequence {
             this.cycle = (Boolean) PBoolean.INSTANCE.toObject(cycleKV.getValueArray(), cycleKV.getValueOffset(), cycleKV.getValueLength());
             this.limitReached = false;
             currentValue = nextValue;
+            
             if (op != ValueOp.VALIDATE_SEQUENCE) {
-                currentValue -= incrementBy * cacheSize;
+                // We can't just take the max of numToAllocate and cacheSize
+                // We need to handle a valid edgecase where a client requests bulk allocation of 
+                // a number of slots that are less than cache size of the sequence
+                currentValue -= incrementBy * (SequenceUtil.isBulkAllocation(numToAllocate) ? numToAllocate : cacheSize);
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3b1bfa0d/phoenix-core/src/main/java/org/apache/phoenix/schema/SequenceAllocation.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/SequenceAllocation.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/SequenceAllocation.java
new file mode 100644
index 0000000..afb4a20
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/SequenceAllocation.java
@@ -0,0 +1,48 @@
+package org.apache.phoenix.schema;
+
+/**
+ * A SequenceKey and the number of slots requested to be allocated for the sequence. 
+ * It binds these two together to allow operations such as sorting
+ * a Collection of SequenceKeys and at the same time preserving the associated requested
+ * number of slots to allocate.
+ * 
+ * This class delegates hashCode, equals and compareTo to @see{SequenceKey}.
+ *
+ */
+public class SequenceAllocation implements Comparable<SequenceAllocation> {
+    
+    private final SequenceKey sequenceKey;
+    private final long numAllocations;
+    
+    public SequenceAllocation(SequenceKey sequenceKey, long numAllocations) {
+        this.sequenceKey = sequenceKey;
+        this.numAllocations = numAllocations;
+    }
+    
+    
+    public SequenceKey getSequenceKey() {
+        return sequenceKey;
+    }
+
+
+    public long getNumAllocations() {
+        return numAllocations;
+    }
+
+
+    @Override
+    public int hashCode() {
+        return sequenceKey.hashCode();
+    }
+    
+    @Override
+    public boolean equals(Object obj) {
+        return sequenceKey.equals(obj);
+    }
+    
+    @Override
+    public int compareTo(SequenceAllocation that) {
+        return sequenceKey.compareTo(that.sequenceKey);
+    }
+    
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3b1bfa0d/phoenix-core/src/main/java/org/apache/phoenix/util/SequenceUtil.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/SequenceUtil.java b/phoenix-core/src/main/java/org/apache/phoenix/util/SequenceUtil.java
index f97d565..acf1864 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/util/SequenceUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/util/SequenceUtil.java
@@ -16,6 +16,7 @@ import org.apache.phoenix.exception.SQLExceptionCode;
 import org.apache.phoenix.exception.SQLExceptionInfo;
 import org.apache.phoenix.schema.SequenceInfo;
 
+import com.google.common.base.Preconditions;
 import com.google.common.math.LongMath;
 
 /**
@@ -23,17 +24,24 @@ import com.google.common.math.LongMath;
  */
 public class SequenceUtil {
 
+    public static final long DEFAULT_NUM_SLOTS_TO_ALLOCATE = 1L; 
+    
     /**
-     * Returns the nextValue of a sequence 
-     * @throws SQLException if cycle is false and the sequence limit has been reached
+     * @return true if we limit of a sequence has been reached.
      */
     public static boolean checkIfLimitReached(long currentValue, long minValue, long maxValue,
-            long incrementBy, long cacheSize) throws SQLException {
+            long incrementBy, long cacheSize, long numToAllocate) {
         long nextValue = 0;
         boolean increasingSeq = incrementBy > 0 ? true : false;
         // advance currentValue while checking for overflow    
         try {
-            long incrementValue = LongMath.checkedMultiply(incrementBy, cacheSize);
+            long incrementValue;
+            if (isBulkAllocation(numToAllocate)) {
+                // For bulk allocation we increment independent of cache size
+                incrementValue = LongMath.checkedMultiply(incrementBy, numToAllocate);
+            } else {
+                incrementValue = LongMath.checkedMultiply(incrementBy, cacheSize);
+            }
             nextValue = LongMath.checkedAdd(currentValue, incrementValue);
         } catch (ArithmeticException e) {
             return true;
@@ -46,9 +54,28 @@ public class SequenceUtil {
         }
         return false;
     }
+
+    public static boolean checkIfLimitReached(long currentValue, long minValue, long maxValue,
+            long incrementBy, long cacheSize) throws SQLException {
+        return checkIfLimitReached(currentValue, minValue, maxValue, incrementBy, cacheSize, DEFAULT_NUM_SLOTS_TO_ALLOCATE);
+    }
     
     public static boolean checkIfLimitReached(SequenceInfo info) throws SQLException {
-        return checkIfLimitReached(info.sequenceValue, info.minValue, info.maxValue, info.incrementBy, info.cacheSize);
+        return checkIfLimitReached(info.sequenceValue, info.minValue, info.maxValue, info.incrementBy, info.cacheSize, DEFAULT_NUM_SLOTS_TO_ALLOCATE);
+    }
+    
+    /**
+     * Returns true if the value of numToAllocate signals that a bulk allocation of sequence slots
+     * was requested. Prevents proliferation of same comparison in many places throughout the code.
+     */
+    public static boolean isBulkAllocation(long numToAllocate) {
+        Preconditions.checkArgument(numToAllocate > 0);
+        return numToAllocate > DEFAULT_NUM_SLOTS_TO_ALLOCATE;
+    }
+    
+    public static boolean isCycleAllowed(long numToAllocate) {
+        return !isBulkAllocation(numToAllocate);  
+        
     }
     
     /**
@@ -59,5 +86,15 @@ public class SequenceUtil {
         return new SQLExceptionInfo.Builder(code).setSchemaName(schemaName).setTableName(tableName)
                 .build().buildException();
     }
+    
+    /**
+     * Returns the correct instance of SQLExceptionCode when we detect a limit has been reached,
+     * depending upon whether a min or max value caused the limit to be exceeded.
+     */
+    public static SQLExceptionCode getLimitReachedErrorCode(boolean increasingSeq) {
+        SQLExceptionCode code = increasingSeq ? SQLExceptionCode.SEQUENCE_VAL_REACHED_MAX_VALUE
+                : SQLExceptionCode.SEQUENCE_VAL_REACHED_MIN_VALUE;
+        return code;
+    }
 
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3b1bfa0d/phoenix-core/src/test/java/org/apache/phoenix/schema/SequenceAllocationTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/schema/SequenceAllocationTest.java b/phoenix-core/src/test/java/org/apache/phoenix/schema/SequenceAllocationTest.java
new file mode 100644
index 0000000..4a825f2
--- /dev/null
+++ b/phoenix-core/src/test/java/org/apache/phoenix/schema/SequenceAllocationTest.java
@@ -0,0 +1,59 @@
+package org.apache.phoenix.schema;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.phoenix.query.QueryServicesOptions;
+import org.junit.Test;
+
+import com.google.common.collect.Lists;
+
+public class SequenceAllocationTest {
+
+    @Test
+    /**
+     * Validates that sorting a List of SequenceAllocation instances
+     * results in the same sort order as sorting SequenceKey instances.
+     */
+    public void testSortingSequenceAllocation() {
+        
+        // Arrange
+        SequenceKey sequenceKey1 = new SequenceKey(null, "seqalloc", "sequenceC",QueryServicesOptions.DEFAULT_SEQUENCE_TABLE_SALT_BUCKETS);
+        SequenceKey sequenceKey2 = new SequenceKey(null, "seqalloc", "sequenceB",QueryServicesOptions.DEFAULT_SEQUENCE_TABLE_SALT_BUCKETS);
+        SequenceKey sequenceKey3 = new SequenceKey(null, "seqalloc", "sequenceA",QueryServicesOptions.DEFAULT_SEQUENCE_TABLE_SALT_BUCKETS);
+        List<SequenceKey> sequenceKeys = Lists.newArrayList(sequenceKey1, sequenceKey2, sequenceKey3);
+        List<SequenceAllocation> sequenceAllocations = Lists.newArrayList(new SequenceAllocation(sequenceKey2, 1), new SequenceAllocation(sequenceKey1, 1), new SequenceAllocation(sequenceKey3, 1));
+        
+        // Act
+        Collections.sort(sequenceKeys);
+        Collections.sort(sequenceAllocations);
+        
+        // Assert
+        int i = 0;
+        for (SequenceKey sequenceKey : sequenceKeys) {
+            assertEquals(sequenceKey, sequenceAllocations.get(i).getSequenceKey());
+            i++;
+        }
+    }
+    
+    @Test
+    public void testSortingSequenceAllocationPreservesAllocations() {
+        
+        // Arrange
+        SequenceKey sequenceKeyC = new SequenceKey(null, "seqalloc", "sequenceC",QueryServicesOptions.DEFAULT_SEQUENCE_TABLE_SALT_BUCKETS);
+        SequenceKey sequenceKeyB = new SequenceKey(null, "seqalloc", "sequenceB",QueryServicesOptions.DEFAULT_SEQUENCE_TABLE_SALT_BUCKETS);
+        SequenceKey sequenceKeyA = new SequenceKey(null, "seqalloc", "sequenceA",QueryServicesOptions.DEFAULT_SEQUENCE_TABLE_SALT_BUCKETS);
+        List<SequenceAllocation> sequenceAllocations = Lists.newArrayList(new SequenceAllocation(sequenceKeyB, 15), new SequenceAllocation(sequenceKeyC, 11), new SequenceAllocation(sequenceKeyA, 1000));
+        
+        // Act
+        Collections.sort(sequenceAllocations);
+        
+        // Assert
+        assertEquals("sequenceA",sequenceAllocations.get(0).getSequenceKey().getSequenceName());
+        assertEquals(1000,sequenceAllocations.get(0).getNumAllocations());
+        assertEquals(15,sequenceAllocations.get(1).getNumAllocations());
+        assertEquals(11,sequenceAllocations.get(2).getNumAllocations());
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3b1bfa0d/phoenix-core/src/test/java/org/apache/phoenix/util/SequenceUtilTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/util/SequenceUtilTest.java b/phoenix-core/src/test/java/org/apache/phoenix/util/SequenceUtilTest.java
index f25a213..2abc482 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/util/SequenceUtilTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/util/SequenceUtilTest.java
@@ -62,4 +62,58 @@ public class SequenceUtilTest {
     public void testDescendingOverflowCycle() throws SQLException {
     	assertTrue(SequenceUtil.checkIfLimitReached(Long.MIN_VALUE, Long.MIN_VALUE, 0, -1/* incrementBy */, CACHE_SIZE));
     }
+    
+    @Test
+    public void testBulkAllocationAscendingNextValueGreaterThanMax() throws SQLException {
+        assertTrue(SequenceUtil.checkIfLimitReached(MAX_VALUE, MIN_VALUE, MAX_VALUE, 2/* incrementBy */, CACHE_SIZE, 1));
+    }
+    
+    @Test
+    public void testBulkAllocationAscendingNextValueReachLimit() throws SQLException {
+        assertFalse(SequenceUtil.checkIfLimitReached(6, MIN_VALUE, MAX_VALUE, 2/* incrementBy */,  CACHE_SIZE, 2));
+    }
+
+    @Test
+    public void testBulkAllocationAscendingNextValueWithinLimit() throws SQLException {
+        assertFalse(SequenceUtil.checkIfLimitReached(5, MIN_VALUE, MAX_VALUE, 2/* incrementBy */, CACHE_SIZE, 2));
+
+    }
+    
+    @Test
+    public void testBulkAllocationAscendingOverflow() throws SQLException {
+        assertTrue(SequenceUtil.checkIfLimitReached(Long.MAX_VALUE, 0, Long.MAX_VALUE, 1/* incrementBy */, CACHE_SIZE, 100));
+    }
+    
+    
+    @Test
+    public void testBulkAllocationDescendingNextValueLessThanMax() throws SQLException {
+        assertTrue(SequenceUtil.checkIfLimitReached(10, MIN_VALUE, MAX_VALUE, -2/* incrementBy */, CACHE_SIZE, 5));
+    }
+    
+    @Test
+    public void testBulkAllocationDescendingNextValueReachLimit() throws SQLException {
+        assertFalse(SequenceUtil.checkIfLimitReached(7, MIN_VALUE, MAX_VALUE, -2/* incrementBy */,  CACHE_SIZE, 3));
+    }
+
+    @Test
+    public void testBulkAllocationDescendingNextValueWithinLimit() throws SQLException {
+        assertFalse(SequenceUtil.checkIfLimitReached(8, MIN_VALUE, MAX_VALUE, -2/* incrementBy */, CACHE_SIZE, 2));
+
+    }
+
+    @Test
+    public void testBulkAllocationDescendingOverflowCycle() throws SQLException {
+        assertTrue(SequenceUtil.checkIfLimitReached(Long.MIN_VALUE, Long.MIN_VALUE, 0, -1/* incrementBy */, CACHE_SIZE, 100));
+    }
+    
+    @Test
+    public void testIsCycleAllowedForBulkAllocation() {
+        assertFalse(SequenceUtil.isCycleAllowed(2));
+    }
+
+    @Test
+    public void testIsCycleAllowedForStandardAllocation() {
+        assertTrue(SequenceUtil.isCycleAllowed(1));
+    }
+    
 }


[10/50] [abbrv] phoenix git commit: PHOENIX-2095 Lower the default for phoenix.sequence.saltBuckets

Posted by ma...@apache.org.
PHOENIX-2095 Lower the default for phoenix.sequence.saltBuckets


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/efb941ae
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/efb941ae
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/efb941ae

Branch: refs/heads/calcite
Commit: efb941aef6062fe704460fd37f9cc062c2ed2eee
Parents: 973bccb
Author: Andrew Purtell <ap...@apache.org>
Authored: Wed Jul 8 09:34:20 2015 -0700
Committer: Andrew Purtell <ap...@apache.org>
Committed: Wed Jul 8 09:34:20 2015 -0700

----------------------------------------------------------------------
 .../main/java/org/apache/phoenix/query/QueryServicesOptions.java   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/efb941ae/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
index 3efd79f..ea81cf5 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
@@ -174,7 +174,7 @@ public class QueryServicesOptions {
     /**
      * Use only first time SYSTEM.SEQUENCE table is created.
      */
-    public static final int DEFAULT_SEQUENCE_TABLE_SALT_BUCKETS = SaltingUtil.MAX_BUCKET_NUM;
+    public static final int DEFAULT_SEQUENCE_TABLE_SALT_BUCKETS = 0;
     /**
      * Default value for coprocessor priority is between SYSTEM and USER priority.
      */


[05/50] [abbrv] phoenix git commit: Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/phoenix

Posted by ma...@apache.org.
Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/phoenix


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/1e606d57
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/1e606d57
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/1e606d57

Branch: refs/heads/calcite
Commit: 1e606d579965b97254f2c30399ace004a8912a31
Parents: ade12a7 c398e18
Author: maryannxue <we...@intel.com>
Authored: Sat Jul 4 14:05:00 2015 -0400
Committer: maryannxue <we...@intel.com>
Committed: Sat Jul 4 14:05:00 2015 -0400

----------------------------------------------------------------------
 .../util/PhoenixConfigurationUtilTest.java      | 28 +++++++++++++--
 .../org/apache/phoenix/util/QueryUtilTest.java  | 36 ++++++++++++++++++--
 2 files changed, 58 insertions(+), 6 deletions(-)
----------------------------------------------------------------------



[11/50] [abbrv] phoenix git commit: PHOENIX-2097 Add new MD5 test for composite PK

Posted by ma...@apache.org.
PHOENIX-2097 Add new MD5 test for composite PK


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/69e55df8
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/69e55df8
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/69e55df8

Branch: refs/heads/calcite
Commit: 69e55df8ff42aedf8c12da569dc5e85cc13eb95d
Parents: efb941a
Author: James Taylor <jt...@salesforce.com>
Authored: Thu Jul 2 10:03:55 2015 -0700
Committer: James Taylor <jt...@salesforce.com>
Committed: Wed Jul 8 11:57:40 2015 -0700

----------------------------------------------------------------------
 .../apache/phoenix/end2end/MD5FunctionIT.java    | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/69e55df8/phoenix-core/src/it/java/org/apache/phoenix/end2end/MD5FunctionIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/MD5FunctionIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/MD5FunctionIT.java
index ff7ebdb..a6107df 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/MD5FunctionIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/MD5FunctionIT.java
@@ -52,6 +52,25 @@ public class MD5FunctionIT extends BaseHBaseManagedTimeIT {
   }      
   
   @Test
+  public void testRetrieveCompositeKey() throws Exception {
+      String testString = "FOOBAR";
+      
+      Connection conn = DriverManager.getConnection(getUrl());
+      String ddl = "CREATE TABLE IF NOT EXISTS MD5_RETRIEVE_TEST (k1 CHAR(3) NOT NULL, k2 CHAR(3) NOT NULL, CONSTRAINT PK PRIMARY KEY (K1,K2))";
+      conn.createStatement().execute(ddl);
+      String dml = "UPSERT INTO MD5_RETRIEVE_TEST VALUES('FOO','BAR')";
+      conn.createStatement().execute(dml);
+      conn.commit();
+      
+      ResultSet rs = conn.createStatement().executeQuery("SELECT MD5((K1,K2)) FROM MD5_RETRIEVE_TEST");
+      assertTrue(rs.next());
+      byte[] first = MessageDigest.getInstance("MD5").digest(testString.getBytes());
+      byte[] second = rs.getBytes(1);
+      assertArrayEquals(first, second);
+      assertFalse(rs.next());
+  }      
+  
+  @Test
   public void testUpsert() throws Exception {
       String testString1 = "mwalsh1";
       String testString2 = "mwalsh2";


[43/50] [abbrv] phoenix git commit: PHOENIX-2123 Diverged view prevents schema propagation to the entire view hierarchy

Posted by ma...@apache.org.
PHOENIX-2123 Diverged view prevents schema propagation to the entire view hierarchy


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/ec93ec79
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/ec93ec79
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/ec93ec79

Branch: refs/heads/calcite
Commit: ec93ec79c24cc847ed4a2777794da3cc0b0e3da3
Parents: 553d3cc
Author: Samarth <sa...@salesforce.com>
Authored: Wed Jul 15 13:34:39 2015 -0700
Committer: Samarth <sa...@salesforce.com>
Committed: Wed Jul 15 13:34:39 2015 -0700

----------------------------------------------------------------------
 .../apache/phoenix/end2end/AlterTableIT.java    | 34 +++++++++++---------
 .../coprocessor/MetaDataEndpointImpl.java       |  7 ++--
 .../org/apache/phoenix/util/UpgradeUtil.java    |  6 ++--
 3 files changed, 24 insertions(+), 23 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/ec93ec79/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java
index 607f52a..1758dd4 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java
@@ -2575,36 +2575,38 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
     }
     
     @Test
-    public void testDivorcedViewsStayDivorced() throws Exception {
-        String baseTable = "testDivorcedViewsStayDivorced";
-        String viewName = baseTable + "_view";
+    public void testDivergedViewsStayDiverged() throws Exception {
+        String baseTable = "testDivergedViewsStayDiverged";
+        String view1 = baseTable + "_view1";
+        String view2 = baseTable + "_view2";
         try (Connection conn = DriverManager.getConnection(getUrl())) {
             String tableDDL = "CREATE TABLE " + baseTable + " (PK1 VARCHAR NOT NULL PRIMARY KEY, V1 VARCHAR, V2 VARCHAR)";
             conn.createStatement().execute(tableDDL);
             
-            String viewDDL = "CREATE VIEW " + viewName + " AS SELECT * FROM " + baseTable;
+            String viewDDL = "CREATE VIEW " + view1 + " AS SELECT * FROM " + baseTable;
+            conn.createStatement().execute(viewDDL);
+            
+            viewDDL = "CREATE VIEW " + view2 + " AS SELECT * FROM " + baseTable;
             conn.createStatement().execute(viewDDL);
             
-            // Drop the column inherited from base table to divorce the view
-            String dropColumn = "ALTER VIEW " + viewName + " DROP COLUMN V2";
+            // Drop the column inherited from base table to make it diverged
+            String dropColumn = "ALTER VIEW " + view1 + " DROP COLUMN V2";
             conn.createStatement().execute(dropColumn);
             
             String alterBaseTable = "ALTER TABLE " + baseTable + " ADD V3 VARCHAR";
-            try {
-            	conn.createStatement().execute(alterBaseTable);
-	            fail();
-	        }
-	        catch (SQLException e) {
-	        	assertEquals("Unexpected exception", CANNOT_MUTATE_TABLE.getErrorCode(), e.getErrorCode());
-	        }
-            
-            // Column V3 shouldn't have propagated to the divorced view.
-            String sql = "SELECT V3 FROM " + viewName;
+            conn.createStatement().execute(alterBaseTable);
+	        
+            // Column V3 shouldn't have propagated to the diverged view.
+            String sql = "SELECT V3 FROM " + view1;
             try {
                 conn.createStatement().execute(sql);
             } catch (SQLException e) {
                 assertEquals(SQLExceptionCode.COLUMN_NOT_FOUND.getErrorCode(), e.getErrorCode());
             }
+            
+            // However, column V3 should have propagated to the non-diverged view.
+            sql = "SELECT V3 FROM " + view2;
+            conn.createStatement().execute(sql);
         } 
     }
     

http://git-wip-us.apache.org/repos/asf/phoenix/blob/ec93ec79/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 5396a69..05e7acb 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
@@ -1693,11 +1693,10 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
             RowLock viewRowLock = acquireLock(region, viewKey, locks);
             PTable view = doGetTable(viewKey, clientTimeStamp, viewRowLock);
             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
+                // if a view has diverged 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);
+                // FIXME: We should allow PK changes to be propagated to a diverged view See PHOENIX-2110
+                continue;
             }
             int numColsAddedToBaseTable = 0;
             int numColsAddedToView = 0;

http://git-wip-us.apache.org/repos/asf/phoenix/blob/ec93ec79/phoenix-core/src/main/java/org/apache/phoenix/util/UpgradeUtil.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/UpgradeUtil.java b/phoenix-core/src/main/java/org/apache/phoenix/util/UpgradeUtil.java
index e1e2515..b4fcef8 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/util/UpgradeUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/util/UpgradeUtil.java
@@ -637,9 +637,9 @@ public class UpgradeUtil {
                              * two till one of the following happens: 
                              * 
                              * 1) We run into a view column which is different from column in the base physical table.
-                             * This means that the view has divorced itself from the base physical table. In such a case
+                             * This means that the view has diverged from the base physical table. In such a case
                              * we will set a special value for the base column count. That special value will also be used
-                             * on the server side to filter out the divorced view so that meta-data changes on the base 
+                             * on the server side to filter out the diverged view so that meta-data changes on the base 
                              * physical table are not propagated to it.
                              * 
                              * 2) Every physical table column is present in the view. In that case we set the base column count
@@ -667,7 +667,7 @@ public class UpgradeUtil {
                                     clearCache = true;
                                 }
                             } else {
-                                // special value to denote that the view has divorced itself from the base physical table.
+                                // special value to denote that the view has diverged from the base physical table.
                                 upsertBaseColumnCountInHeaderRow(metaConnection, viewTenantId, viewSchema, viewName, DIVERGED_VIEW_BASE_COLUMN_COUNT);
                                 baseColumnCountUpserted = true;
                                 clearCache = true;


[44/50] [abbrv] phoenix git commit: PHOENIX-2067 Sort order incorrect for variable length DESC columns - ARRAY addendum (Dumindu Buddhika)

Posted by ma...@apache.org.
PHOENIX-2067 Sort order incorrect for variable length DESC columns - ARRAY
addendum (Dumindu Buddhika)


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/33d60506
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/33d60506
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/33d60506

Branch: refs/heads/calcite
Commit: 33d60506c5f2d4408a1df79f278d7a45d3401a27
Parents: ec93ec7
Author: ramkrishna <ra...@gmail.com>
Authored: Thu Jul 16 21:13:31 2015 +0530
Committer: ramkrishna <ra...@gmail.com>
Committed: Thu Jul 16 21:13:31 2015 +0530

----------------------------------------------------------------------
 .../phoenix/schema/types/PArrayDataType.java    |  14 +-
 .../expression/ArrayAppendFunctionTest.java     |  76 ++++++++---
 .../expression/ArrayConcatFunctionTest.java     | 129 +++++++++++++++++++
 .../expression/ArrayPrependFunctionTest.java    |  61 +++++++++
 .../schema/types/PDataTypeForArraysTest.java    |  70 +++++++++-
 5 files changed, 324 insertions(+), 26 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/33d60506/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PArrayDataType.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PArrayDataType.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PArrayDataType.java
index dd11569..9ca64c5 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PArrayDataType.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PArrayDataType.java
@@ -379,10 +379,10 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
                 int currOffset = getOffset(bytes, arrayIndex, useShort, indexOffset);
                 int elementLength = 0;
                 if (arrayIndex == (noOfElements - 1)) {
-                    elementLength = bytes[currOffset + initPos] == QueryConstants.SEPARATOR_BYTE ? 0 : indexOffset
+                    elementLength = (bytes[currOffset + initPos] == QueryConstants.SEPARATOR_BYTE || bytes[currOffset + initPos] == QueryConstants.DESC_SEPARATOR_BYTE) ? 0 : indexOffset
                             - (currOffset + initPos) - 3;
                 } else {
-                    elementLength = bytes[currOffset + initPos] == QueryConstants.SEPARATOR_BYTE ? 0 : getOffset(bytes,
+                    elementLength = (bytes[currOffset + initPos] == QueryConstants.SEPARATOR_BYTE || bytes[currOffset + initPos] == QueryConstants.DESC_SEPARATOR_BYTE) ? 0 : getOffset(bytes,
                             arrayIndex + 1, useShort, indexOffset) - currOffset - 1;
                 }
                 ptr.set(bytes, currOffset + initPos, elementLength);
@@ -426,10 +426,10 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
                 int currOffset = getOffset(bytes, arrayIndex, useShort, indexOffset);
                 int elementLength = 0;
                 if (arrayIndex == (noOfElements - 1)) {
-                    elementLength = bytes[currOffset + offset] == QueryConstants.SEPARATOR_BYTE ? 0 : indexOffset
+                    elementLength = (bytes[currOffset + offset] == QueryConstants.SEPARATOR_BYTE || bytes[currOffset + offset] == QueryConstants.DESC_SEPARATOR_BYTE) ? 0 : indexOffset
                             - (currOffset + offset) - 3;
                 } else {
-                    elementLength = bytes[currOffset + offset] == QueryConstants.SEPARATOR_BYTE ? 0 : getOffset(bytes,
+                    elementLength = (bytes[currOffset + offset] == QueryConstants.SEPARATOR_BYTE || bytes[currOffset + offset] == QueryConstants.DESC_SEPARATOR_BYTE) ? 0 : getOffset(bytes,
                             arrayIndex + 1, useShort, indexOffset) - currOffset - 1;
                 }
                 ptr.set(bytes, currOffset + offset, elementLength);
@@ -831,7 +831,7 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
             // count nulls at the end of array 1
             for (int index = actualLengthOfArray1 - 1; index > -1; index--) {
                 int offset = getOffset(array1Bytes, index, !useIntArray1, array1BytesOffset + offsetArrayPositionArray1);
-                if (array1Bytes[array1BytesOffset + offset] == QueryConstants.SEPARATOR_BYTE) {
+                if (array1Bytes[array1BytesOffset + offset] == QueryConstants.SEPARATOR_BYTE || array1Bytes[array1BytesOffset + offset] == QueryConstants.DESC_SEPARATOR_BYTE) {
                     nullsAtTheEndOfArray1++;
                 } else {
                     break;
@@ -1064,11 +1064,11 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
                         nextOff = getOffset(indexArr, countOfElementsRead + 1, useShort, indexOffset);
                     }
                     countOfElementsRead++;
-                    if ((bytes[currOffset + initPos] != QueryConstants.SEPARATOR_BYTE) && foundNull) {
+                    if ((bytes[currOffset + initPos] != QueryConstants.SEPARATOR_BYTE && bytes[currOffset + initPos] != QueryConstants.DESC_SEPARATOR_BYTE) && foundNull) {
                         // Found a non null element
                         foundNull = false;
                     }
-                    if (bytes[currOffset + initPos] == QueryConstants.SEPARATOR_BYTE) {
+                    if (bytes[currOffset + initPos] == QueryConstants.SEPARATOR_BYTE || bytes[currOffset + initPos] == QueryConstants.DESC_SEPARATOR_BYTE) {
                         // Null element
                         foundNull = true;
                         i++;

http://git-wip-us.apache.org/repos/asf/phoenix/blob/33d60506/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayAppendFunctionTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayAppendFunctionTest.java b/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayAppendFunctionTest.java
index 5405ef3..a98bccc 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayAppendFunctionTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayAppendFunctionTest.java
@@ -17,6 +17,7 @@
  */
 package org.apache.phoenix.expression;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertTrue;
 
 import java.math.BigDecimal;
@@ -27,21 +28,7 @@ import java.util.List;
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.phoenix.expression.function.ArrayAppendFunction;
 import org.apache.phoenix.schema.SortOrder;
-import org.apache.phoenix.schema.types.PBoolean;
-import org.apache.phoenix.schema.types.PChar;
-import org.apache.phoenix.schema.types.PDataType;
-import org.apache.phoenix.schema.types.PDate;
-import org.apache.phoenix.schema.types.PDecimal;
-import org.apache.phoenix.schema.types.PDouble;
-import org.apache.phoenix.schema.types.PFloat;
-import org.apache.phoenix.schema.types.PInteger;
-import org.apache.phoenix.schema.types.PLong;
-import org.apache.phoenix.schema.types.PSmallint;
-import org.apache.phoenix.schema.types.PTime;
-import org.apache.phoenix.schema.types.PTimestamp;
-import org.apache.phoenix.schema.types.PTinyint;
-import org.apache.phoenix.schema.types.PVarchar;
-import org.apache.phoenix.schema.types.PhoenixArray;
+import org.apache.phoenix.schema.types.*;
 import org.junit.Test;
 
 import com.google.common.collect.Lists;
@@ -357,4 +344,63 @@ public class ArrayAppendFunctionTest {
         test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), 4, null, baseType, 1, null, expected, SortOrder.ASC, SortOrder.DESC);
     }
 
+    @Test
+    public void testForCorrectSeparatorBytes1() throws Exception {
+        Object[] o = new Object[]{"a", "b", "c"};
+        Object element = "d";
+        PDataType baseType = PVarchar.INSTANCE;
+
+        PhoenixArray arr = new PhoenixArray(baseType, o);
+        LiteralExpression arrayLiteral, elementLiteral;
+        arrayLiteral = LiteralExpression.newConstant(arr, PVarcharArray.INSTANCE, null, null, SortOrder.ASC, Determinism.ALWAYS);
+        elementLiteral = LiteralExpression.newConstant(element, baseType, null, null, SortOrder.ASC, Determinism.ALWAYS);
+        List<Expression> expressions = Lists.newArrayList((Expression) arrayLiteral);
+        expressions.add(elementLiteral);
+
+        Expression arrayAppendFunction = new ArrayAppendFunction(expressions);
+        ImmutableBytesWritable ptr = new ImmutableBytesWritable();
+        arrayAppendFunction.evaluate(null, ptr);
+        byte[] expected = new byte[]{97, 0, 98, 0, 99, 0, 100, 0, 0, 0, -128, 1, -128, 3, -128, 5, -128, 7, 0, 0, 0, 10, 0, 0, 0, 4, 1};
+        assertArrayEquals(expected, ptr.get());
+    }
+
+    @Test
+    public void testForCorrectSeparatorBytes2() throws Exception {
+        Object[] o = new Object[]{"a", "b", "c"};
+        Object element = "d";
+        PDataType baseType = PVarchar.INSTANCE;
+
+        PhoenixArray arr = new PhoenixArray(baseType, o);
+        LiteralExpression arrayLiteral, elementLiteral;
+        arrayLiteral = LiteralExpression.newConstant(arr, PVarcharArray.INSTANCE, null, null, SortOrder.DESC, Determinism.ALWAYS);
+        elementLiteral = LiteralExpression.newConstant(element, baseType, null, null, SortOrder.ASC, Determinism.ALWAYS);
+        List<Expression> expressions = Lists.newArrayList((Expression) arrayLiteral);
+        expressions.add(elementLiteral);
+
+        Expression arrayAppendFunction = new ArrayAppendFunction(expressions);
+        ImmutableBytesWritable ptr = new ImmutableBytesWritable();
+        arrayAppendFunction.evaluate(null, ptr);
+        byte[] expected = new byte[]{-98, -1, -99, -1, -100, -1, -101, -1, -1, -1, -128, 1, -128, 3, -128, 5, -128, 7, 0, 0, 0, 10, 0, 0, 0, 4, 1};
+        assertArrayEquals(expected, ptr.get());
+    }
+
+    @Test
+    public void testForCorrectSeparatorBytes3() throws Exception {
+        Object[] o = new Object[]{"a", null, null, "c"};
+        Object element = "d";
+        PDataType baseType = PVarchar.INSTANCE;
+
+        PhoenixArray arr = new PhoenixArray(baseType, o);
+        LiteralExpression arrayLiteral, elementLiteral;
+        arrayLiteral = LiteralExpression.newConstant(arr, PVarcharArray.INSTANCE, null, null, SortOrder.DESC, Determinism.ALWAYS);
+        elementLiteral = LiteralExpression.newConstant(element, baseType, null, null, SortOrder.ASC, Determinism.ALWAYS);
+        List<Expression> expressions = Lists.newArrayList((Expression) arrayLiteral);
+        expressions.add(elementLiteral);
+
+        Expression arrayAppendFunction = new ArrayAppendFunction(expressions);
+        ImmutableBytesWritable ptr = new ImmutableBytesWritable();
+        arrayAppendFunction.evaluate(null, ptr);
+        byte[] expected = new byte[]{-98, -1, 0, -2, -100, -1, -101, -1, -1, -1, -128, 1, -128, 3, -128, 3, -128, 5, -128, 7, 0, 0, 0, 10, 0, 0, 0, 5, 1};
+        assertArrayEquals(expected, ptr.get());
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/33d60506/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayConcatFunctionTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayConcatFunctionTest.java b/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayConcatFunctionTest.java
index 75d0827..f23729e 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayConcatFunctionTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayConcatFunctionTest.java
@@ -18,6 +18,7 @@
 
 package org.apache.phoenix.expression;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 
 import java.math.BigDecimal;
@@ -485,6 +486,9 @@ public class ArrayConcatFunctionTest {
         PhoenixArray arr2 = new PhoenixArray(base, o2);
         PhoenixArray expected = new PhoenixArray(base, e);
         test(arr1, arr2, type, null, null, type, null, null, expected, SortOrder.ASC, SortOrder.ASC);
+        test(arr1, arr2, type, null, null, type, null, null, expected, SortOrder.ASC, SortOrder.DESC);
+        test(arr1, arr2, type, null, null, type, null, null, expected, SortOrder.DESC, SortOrder.DESC);
+        test(arr1, arr2, type, null, null, type, null, null, expected, SortOrder.DESC, SortOrder.ASC);
     }
 
     @Test
@@ -504,6 +508,9 @@ public class ArrayConcatFunctionTest {
         PhoenixArray arr2 = new PhoenixArray(base, o2);
         PhoenixArray expected = new PhoenixArray(base, e);
         test(arr1, arr2, type, null, null, type, null, null, expected, SortOrder.ASC, SortOrder.ASC);
+        test(arr1, arr2, type, null, null, type, null, null, expected, SortOrder.ASC, SortOrder.DESC);
+        test(arr1, arr2, type, null, null, type, null, null, expected, SortOrder.DESC, SortOrder.DESC);
+        test(arr1, arr2, type, null, null, type, null, null, expected, SortOrder.DESC, SortOrder.ASC);
     }
 
     @Test
@@ -523,6 +530,9 @@ public class ArrayConcatFunctionTest {
         PhoenixArray arr2 = new PhoenixArray(base, o2);
         PhoenixArray expected = new PhoenixArray(base, e);
         test(arr1, arr2, type, null, null, type, null, null, expected, SortOrder.ASC, SortOrder.ASC);
+        test(arr1, arr2, type, null, null, type, null, null, expected, SortOrder.ASC, SortOrder.DESC);
+        test(arr1, arr2, type, null, null, type, null, null, expected, SortOrder.DESC, SortOrder.DESC);
+        test(arr1, arr2, type, null, null, type, null, null, expected, SortOrder.DESC, SortOrder.ASC);
     }
 
     @Test
@@ -542,6 +552,9 @@ public class ArrayConcatFunctionTest {
         PhoenixArray arr2 = new PhoenixArray(base, o2);
         PhoenixArray expected = new PhoenixArray(base, e);
         test(arr1, arr2, type, null, null, type, null, null, expected, SortOrder.ASC, SortOrder.ASC);
+        test(arr1, arr2, type, null, null, type, null, null, expected, SortOrder.ASC, SortOrder.DESC);
+        test(arr1, arr2, type, null, null, type, null, null, expected, SortOrder.DESC, SortOrder.DESC);
+        test(arr1, arr2, type, null, null, type, null, null, expected, SortOrder.DESC, SortOrder.ASC);
     }
 
     @Test
@@ -561,6 +574,9 @@ public class ArrayConcatFunctionTest {
         PhoenixArray arr2 = new PhoenixArray(base, o2);
         PhoenixArray expected = new PhoenixArray(base, e);
         test(arr1, arr2, type, null, null, type, null, null, expected, SortOrder.ASC, SortOrder.ASC);
+        test(arr1, arr2, type, null, null, type, null, null, expected, SortOrder.ASC, SortOrder.DESC);
+        test(arr1, arr2, type, null, null, type, null, null, expected, SortOrder.DESC, SortOrder.DESC);
+        test(arr1, arr2, type, null, null, type, null, null, expected, SortOrder.DESC, SortOrder.ASC);
     }
 
     @Test
@@ -580,5 +596,118 @@ public class ArrayConcatFunctionTest {
         PhoenixArray arr2 = new PhoenixArray(base, o2);
         PhoenixArray expected = new PhoenixArray(base, e);
         test(arr1, arr2, type, null, null, type, null, null, expected, SortOrder.ASC, SortOrder.ASC);
+        test(arr1, arr2, type, null, null, type, null, null, expected, SortOrder.ASC, SortOrder.DESC);
+        test(arr1, arr2, type, null, null, type, null, null, expected, SortOrder.DESC, SortOrder.DESC);
+        test(arr1, arr2, type, null, null, type, null, null, expected, SortOrder.DESC, SortOrder.ASC);
+    }
+
+    @Test
+    public void testForCorrectSeparatorBytes1() throws Exception {
+        Object[] o1 = new Object[]{"a", "b"};
+        Object[] o2 = new Object[]{"c", "d", "e"};
+        PDataType type = PVarcharArray.INSTANCE;
+        PDataType base = PVarchar.INSTANCE;
+
+        PhoenixArray arr1 = new PhoenixArray(base, o1);
+        PhoenixArray arr2 = new PhoenixArray(base, o2);
+        LiteralExpression array1Literal, array2Literal;
+        array1Literal = LiteralExpression.newConstant(arr1, type, null, null, SortOrder.ASC, Determinism.ALWAYS);
+        array2Literal = LiteralExpression.newConstant(arr2, type, null, null, SortOrder.ASC, Determinism.ALWAYS);
+        List<Expression> expressions = Lists.newArrayList((Expression) array1Literal);
+        expressions.add(array2Literal);
+
+        Expression arrayConcatFunction = new ArrayConcatFunction(expressions);
+        ImmutableBytesWritable ptr = new ImmutableBytesWritable();
+        arrayConcatFunction.evaluate(null, ptr);
+        byte[] expected = new byte[]{97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 0, 0, -128, 1, -128, 3, -128, 5, -128, 7, -128, 9, 0, 0, 0, 12, 0, 0, 0, 5, 1};
+        assertArrayEquals(expected, ptr.get());
+    }
+
+    @Test
+    public void testForCorrectSeparatorBytes2() throws Exception {
+        Object[] o1 = new Object[]{"a", "b"};
+        Object[] o2 = new Object[]{"c", "d", "e"};
+        PDataType type = PVarcharArray.INSTANCE;
+        PDataType base = PVarchar.INSTANCE;
+
+        PhoenixArray arr1 = new PhoenixArray(base, o1);
+        PhoenixArray arr2 = new PhoenixArray(base, o2);
+        LiteralExpression array1Literal, array2Literal;
+        array1Literal = LiteralExpression.newConstant(arr1, type, null, null, SortOrder.ASC, Determinism.ALWAYS);
+        array2Literal = LiteralExpression.newConstant(arr2, type, null, null, SortOrder.DESC, Determinism.ALWAYS);
+        List<Expression> expressions = Lists.newArrayList((Expression) array1Literal);
+        expressions.add(array2Literal);
+
+        Expression arrayConcatFunction = new ArrayConcatFunction(expressions);
+        ImmutableBytesWritable ptr = new ImmutableBytesWritable();
+        arrayConcatFunction.evaluate(null, ptr);
+        byte[] expected = new byte[]{97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 0, 0, -128, 1, -128, 3, -128, 5, -128, 7, -128, 9, 0, 0, 0, 12, 0, 0, 0, 5, 1};
+        assertArrayEquals(expected, ptr.get());
+    }
+
+    @Test
+    public void testForCorrectSeparatorBytes3() throws Exception {
+        Object[] o1 = new Object[]{"a", "b"};
+        Object[] o2 = new Object[]{"c", "d", "e"};
+        PDataType type = PVarcharArray.INSTANCE;
+        PDataType base = PVarchar.INSTANCE;
+
+        PhoenixArray arr1 = new PhoenixArray(base, o1);
+        PhoenixArray arr2 = new PhoenixArray(base, o2);
+        LiteralExpression array1Literal, array2Literal;
+        array1Literal = LiteralExpression.newConstant(arr1, type, null, null, SortOrder.DESC, Determinism.ALWAYS);
+        array2Literal = LiteralExpression.newConstant(arr2, type, null, null, SortOrder.DESC, Determinism.ALWAYS);
+        List<Expression> expressions = Lists.newArrayList((Expression) array1Literal);
+        expressions.add(array2Literal);
+
+        Expression arrayConcatFunction = new ArrayConcatFunction(expressions);
+        ImmutableBytesWritable ptr = new ImmutableBytesWritable();
+        arrayConcatFunction.evaluate(null, ptr);
+        byte[] expected = new byte[]{-98, -1, -99, -1, -100, -1, -101, -1, -102, -1, -1, -1, -128, 1, -128, 3, -128, 5, -128, 7, -128, 9, 0, 0, 0, 12, 0, 0, 0, 5, 1};
+        assertArrayEquals(expected, ptr.get());
+    }
+
+    @Test
+    public void testForCorrectSeparatorBytes4() throws Exception {
+        Object[] o1 = new Object[]{"a", "b", null};
+        Object[] o2 = new Object[]{null, "c", "d", "e"};
+        PDataType type = PVarcharArray.INSTANCE;
+        PDataType base = PVarchar.INSTANCE;
+
+        PhoenixArray arr1 = new PhoenixArray(base, o1);
+        PhoenixArray arr2 = new PhoenixArray(base, o2);
+        LiteralExpression array1Literal, array2Literal;
+        array1Literal = LiteralExpression.newConstant(arr1, type, null, null, SortOrder.ASC, Determinism.ALWAYS);
+        array2Literal = LiteralExpression.newConstant(arr2, type, null, null, SortOrder.DESC, Determinism.ALWAYS);
+        List<Expression> expressions = Lists.newArrayList((Expression) array1Literal);
+        expressions.add(array2Literal);
+
+        Expression arrayConcatFunction = new ArrayConcatFunction(expressions);
+        ImmutableBytesWritable ptr = new ImmutableBytesWritable();
+        arrayConcatFunction.evaluate(null, ptr);
+        byte[] expected = new byte[]{97, 0, 98, 0, 0, -2, 99, 0, 100, 0, 101, 0, 0, 0, -128, 1, -128, 3, -128, 5, -128, 5, -128, 7, -128, 9, -128, 11, 0, 0, 0, 14, 0, 0, 0, 7, 1};
+        assertArrayEquals(expected, ptr.get());
+    }
+
+    @Test
+    public void testForCorrectSeparatorBytes5() throws Exception {
+        Object[] o1 = new Object[]{"a", "b", null, null};
+        Object[] o2 = new Object[]{null, "c", "d", "e"};
+        PDataType type = PVarcharArray.INSTANCE;
+        PDataType base = PVarchar.INSTANCE;
+
+        PhoenixArray arr1 = new PhoenixArray(base, o1);
+        PhoenixArray arr2 = new PhoenixArray(base, o2);
+        LiteralExpression array1Literal, array2Literal;
+        array1Literal = LiteralExpression.newConstant(arr1, type, null, null, SortOrder.DESC, Determinism.ALWAYS);
+        array2Literal = LiteralExpression.newConstant(arr2, type, null, null, SortOrder.DESC, Determinism.ALWAYS);
+        List<Expression> expressions = Lists.newArrayList((Expression) array1Literal);
+        expressions.add(array2Literal);
+
+        Expression arrayConcatFunction = new ArrayConcatFunction(expressions);
+        ImmutableBytesWritable ptr = new ImmutableBytesWritable();
+        arrayConcatFunction.evaluate(null, ptr);
+        byte[] expected = new byte[]{-98, -1, -99, -1, 0, -3, -100, -1, -101, -1, -102, -1, -1, -1, -128, 1, -128, 3, -128, 5, -128, 5, -128, 5, -128, 7, -128, 9, -128, 11, 0, 0, 0, 14, 0, 0, 0, 8, 1};
+        assertArrayEquals(expected, ptr.get());
     }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/33d60506/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayPrependFunctionTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayPrependFunctionTest.java b/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayPrependFunctionTest.java
index 4d2f960..b64f013 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayPrependFunctionTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayPrependFunctionTest.java
@@ -17,6 +17,7 @@
  */
 package org.apache.phoenix.expression;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 
 import java.math.BigDecimal;
@@ -549,4 +550,64 @@ public class ArrayPrependFunctionTest {
         PhoenixArray expected = new PhoenixArray(baseType, o2);
         test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), null, null, baseType, 1, null, expected, SortOrder.ASC, SortOrder.DESC);
     }
+
+    @Test
+    public void testForCorrectSeparatorBytes1() throws Exception {
+        Object[] o = new Object[]{"a", "b", "c"};
+        Object element = "d";
+        PDataType baseType = PVarchar.INSTANCE;
+
+        PhoenixArray arr = new PhoenixArray(baseType, o);
+        LiteralExpression arrayLiteral, elementLiteral;
+        arrayLiteral = LiteralExpression.newConstant(arr, PVarcharArray.INSTANCE, null, null, SortOrder.ASC, Determinism.ALWAYS);
+        elementLiteral = LiteralExpression.newConstant(element, baseType, null, null, SortOrder.ASC, Determinism.ALWAYS);
+        List<Expression> expressions = Lists.newArrayList((Expression) elementLiteral);
+        expressions.add(arrayLiteral);
+
+        Expression arrayPrependFunction = new ArrayPrependFunction(expressions);
+        ImmutableBytesWritable ptr = new ImmutableBytesWritable();
+        arrayPrependFunction.evaluate(null, ptr);
+        byte[] expected = new byte[]{100, 0, 97, 0, 98, 0, 99, 0, 0, 0, -128, 1, -128, 3, -128, 5, -128, 7, 0, 0, 0, 10, 0, 0, 0, 4, 1};
+        assertArrayEquals(expected, ptr.get());
+    }
+
+    @Test
+    public void testForCorrectSeparatorBytes2() throws Exception {
+        Object[] o = new Object[]{"a", "b", "c"};
+        Object element = "d";
+        PDataType baseType = PVarchar.INSTANCE;
+
+        PhoenixArray arr = new PhoenixArray(baseType, o);
+        LiteralExpression arrayLiteral, elementLiteral;
+        arrayLiteral = LiteralExpression.newConstant(arr, PVarcharArray.INSTANCE, null, null, SortOrder.DESC, Determinism.ALWAYS);
+        elementLiteral = LiteralExpression.newConstant(element, baseType, null, null, SortOrder.ASC, Determinism.ALWAYS);
+        List<Expression> expressions = Lists.newArrayList((Expression) elementLiteral);
+        expressions.add(arrayLiteral);
+
+        Expression arrayPrependFunction = new ArrayPrependFunction(expressions);
+        ImmutableBytesWritable ptr = new ImmutableBytesWritable();
+        arrayPrependFunction.evaluate(null, ptr);
+        byte[] expected = new byte[]{-101, -1, -98, -1, -99, -1, -100, -1, -1, -1, -128, 1, -128, 3, -128, 5, -128, 7, 0, 0, 0, 10, 0, 0, 0, 4, 1};
+        assertArrayEquals(expected, ptr.get());
+    }
+
+    @Test
+    public void testForCorrectSeparatorBytes3() throws Exception {
+        Object[] o = new Object[]{"a", null, null, "c"};
+        Object element = "d";
+        PDataType baseType = PVarchar.INSTANCE;
+
+        PhoenixArray arr = new PhoenixArray(baseType, o);
+        LiteralExpression arrayLiteral, elementLiteral;
+        arrayLiteral = LiteralExpression.newConstant(arr, PVarcharArray.INSTANCE, null, null, SortOrder.DESC, Determinism.ALWAYS);
+        elementLiteral = LiteralExpression.newConstant(element, baseType, null, null, SortOrder.ASC, Determinism.ALWAYS);
+        List<Expression> expressions = Lists.newArrayList((Expression) elementLiteral);
+        expressions.add(arrayLiteral);
+
+        Expression arrayPrependFunction = new ArrayPrependFunction(expressions);
+        ImmutableBytesWritable ptr = new ImmutableBytesWritable();
+        arrayPrependFunction.evaluate(null, ptr);
+        byte[] expected = new byte[]{-101, -1, -98, -1, 0, -2, -100, -1, -1, -1, -128, 1, -128, 3, -128, 5, -128, 5, -128, 7, 0, 0, 0, 10, 0, 0, 0, 5, 1};
+        assertArrayEquals(expected, ptr.get());
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/33d60506/phoenix-core/src/test/java/org/apache/phoenix/schema/types/PDataTypeForArraysTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/schema/types/PDataTypeForArraysTest.java b/phoenix-core/src/test/java/org/apache/phoenix/schema/types/PDataTypeForArraysTest.java
index 0610cda..7246020 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/schema/types/PDataTypeForArraysTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/schema/types/PDataTypeForArraysTest.java
@@ -17,9 +17,6 @@
  */
 package org.apache.phoenix.schema.types;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
 import java.math.BigDecimal;
 import java.sql.Date;
 import java.sql.Time;
@@ -27,6 +24,8 @@ import java.sql.Timestamp;
 
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.phoenix.query.QueryConstants;
+import org.apache.phoenix.schema.SortOrder;
 import org.apache.phoenix.schema.types.PArrayDataType;
 import org.apache.phoenix.schema.types.PBoolean;
 import org.apache.phoenix.schema.types.PBooleanArray;
@@ -76,6 +75,8 @@ import org.apache.phoenix.schema.types.PhoenixArray;
 import org.junit.Ignore;
 import org.junit.Test;
 
+import static org.junit.Assert.*;
+
 public class PDataTypeForArraysTest {
 	@Test
 	public void testForIntegerArray() {
@@ -1137,5 +1138,66 @@ public class PDataTypeForArraysTest {
         byte[] bytes2 = PVarcharArray.INSTANCE.toBytes(arr);
         assertTrue(Bytes.compareTo(bytes1, bytes2) == 0);
     }
-    
+
+    @Test
+    public void testIsRowKeyOrderOptimized1() {
+        Object[] objects = new Object[]{"a", "b", "c"};
+        PhoenixArray arr = new PhoenixArray(PVarchar.INSTANCE, objects);
+        byte[] bytes = PVarcharArray.INSTANCE.toBytes(arr, PVarchar.INSTANCE, SortOrder.ASC);
+        assertTrue(PArrayDataType.isRowKeyOrderOptimized(PVarcharArray.INSTANCE, SortOrder.ASC, bytes, 0, bytes.length));
+    }
+
+    @Test
+    public void testIsRowKeyOrderOptimized2() {
+        Object[] objects = new Object[]{"a", "b", "c"};
+        PhoenixArray arr = new PhoenixArray(PVarchar.INSTANCE, objects);
+        byte[] bytes = PVarcharArray.INSTANCE.toBytes(arr, SortOrder.DESC);
+        assertTrue(PArrayDataType.isRowKeyOrderOptimized(PVarcharArray.INSTANCE, SortOrder.DESC, bytes, 0, bytes.length));
+    }
+
+    @Test
+    public void testIsRowKeyOrderOptimized3() {
+        Object[] objects = new Object[]{"a", "b", "c"};
+        PhoenixArray arr = new PhoenixArray(PVarchar.INSTANCE, objects);
+        byte[] bytes = PVarcharArray.INSTANCE.toBytes(arr, SortOrder.DESC);
+        for (int i = 0; i < bytes.length; i++) {
+            if (bytes[i] == QueryConstants.DESC_SEPARATOR_BYTE) {
+                bytes[i] = QueryConstants.SEPARATOR_BYTE;
+            }
+        }
+        assertFalse(PArrayDataType.isRowKeyOrderOptimized(PVarcharArray.INSTANCE, SortOrder.DESC, bytes, 0, bytes.length));
+    }
+
+    @Test
+    public void testIsRowKeyOrderOptimized4() {
+        assertTrue(PArrayDataType.isRowKeyOrderOptimized(PVarcharArray.INSTANCE, SortOrder.DESC, null, 0, 0));
+    }
+
+    @Test
+    public void testIsRowKeyOrderOptimized5() {
+        Object[] objects = new Object[]{1, 2, 3};
+        PhoenixArray arr = new PhoenixArray.PrimitiveIntPhoenixArray(PInteger.INSTANCE, objects);
+        byte[] bytes = PIntegerArray.INSTANCE.toBytes(arr, PInteger.INSTANCE, SortOrder.ASC);
+        assertTrue(PArrayDataType.isRowKeyOrderOptimized(PIntegerArray.INSTANCE, SortOrder.ASC, bytes, 0, bytes.length));
+    }
+
+    @Test
+    public void testVarcharArrayDesc(){
+        Object[] objects = new Object[]{"a", "b", null};
+        PhoenixArray arr = new PhoenixArray(PVarchar.INSTANCE, objects);
+        byte[] bytes = PVarcharArray.INSTANCE.toBytes(arr, PVarchar.INSTANCE, SortOrder.DESC);
+        PhoenixArray arr2 = (PhoenixArray)PVarcharArray.INSTANCE.toObject(bytes, SortOrder.DESC);
+        assertEquals(arr, arr2);
+    }
+
+    @Test
+    public void testPositionAtArrayElementWithDescArray(){
+        Object[] objects = new Object[]{"a", "b", null};
+        PhoenixArray arr = new PhoenixArray(PVarchar.INSTANCE, objects);
+        byte[] bytes = PVarcharArray.INSTANCE.toBytes(arr, PVarchar.INSTANCE, SortOrder.DESC);
+        ImmutableBytesWritable ptr = new ImmutableBytesWritable(bytes);
+        PArrayDataType.positionAtArrayElement(ptr, 2, PVarchar.INSTANCE, null);
+        String value = (String)PVarchar.INSTANCE.toObject(ptr, SortOrder.DESC);
+        assertEquals(null, value);
+    }
 }


[31/50] [abbrv] phoenix git commit: PHOENIX-2067 Sort order incorrect for variable length DESC columns

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDataType.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDataType.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDataType.java
index 60d2020..2c91dc5 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDataType.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDataType.java
@@ -47,1060 +47,1036 @@ import com.google.common.primitives.Longs;
  */
 public abstract class PDataType<T> implements DataType<T>, Comparable<PDataType<?>> {
 
-  private final String sqlTypeName;
-  private final int sqlType;
-  private final Class clazz;
-  private final byte[] clazzNameBytes;
-  private final byte[] sqlTypeNameBytes;
-  private final PDataCodec codec;
-  private final int ordinal;
-
-  protected PDataType(String sqlTypeName, int sqlType, Class clazz, PDataCodec codec, int ordinal) {
-    this.sqlTypeName = sqlTypeName;
-    this.sqlType = sqlType;
-    this.clazz = clazz;
-    this.clazzNameBytes = Bytes.toBytes(clazz.getName());
-    this.sqlTypeNameBytes = Bytes.toBytes(sqlTypeName);
-    this.codec = codec;
-    this.ordinal = ordinal;
-  }
-
-  @Deprecated
-  public static PDataType[] values() {
-    return PDataTypeFactory.getInstance().getOrderedTypes();
-  }
-
-  @Deprecated
-  public int ordinal() {
-    return ordinal;
-  }
-
-  @Override
-  public Class<T> encodedClass() {
-    return getJavaClass();
-  }
-
-  public boolean isCastableTo(PDataType targetType) {
-    return isComparableTo(targetType);
-  }
-
-  public final PDataCodec getCodec() {
-    return codec;
-  }
-
-  public boolean isBytesComparableWith(PDataType otherType) {
-    return this == otherType
-        || this.getClass() == PVarbinary.class
-        || otherType == PVarbinary.INSTANCE
-        || this.getClass() == PBinary.class
-        || otherType == PBinary.INSTANCE;
-  }
-
-  public int estimateByteSize(Object o) {
-    if (isFixedWidth()) {
-      return getByteSize();
-    }
-    if (isArrayType()) {
-      PhoenixArray array = (PhoenixArray) o;
-      int noOfElements = array.numElements;
-      int totalVarSize = 0;
-      for (int i = 0; i < noOfElements; i++) {
-        totalVarSize += array.estimateByteSize(i);
-      }
-      return totalVarSize;
-    }
-    // Non fixed width types must override this
-    throw new UnsupportedOperationException();
-  }
-
-  public Integer getMaxLength(Object o) {
-    return null;
-  }
-
-  public Integer getScale(Object o) {
-    return null;
-  }
-
-  /**
-   * Estimate the byte size from the type length. For example, for char, byte size would be the
-   * same as length. For decimal, byte size would have no correlation with the length.
-   */
-  public Integer estimateByteSizeFromLength(Integer length) {
-    if (isFixedWidth()) {
-      return getByteSize();
-    }
-    if (isArrayType()) {
-      return null;
-    }
-    // If not fixed width, default to say the byte size is the same as length.
-    return length;
-  }
-
-  public final String getSqlTypeName() {
-    return sqlTypeName;
-  }
-
-  public final int getSqlType() {
-    return sqlType;
-  }
-
-  public final Class getJavaClass() {
-    return clazz;
-  }
-
-  public boolean isArrayType() {
-    return false;
-  }
-
-  public final int compareTo(byte[] lhs, int lhsOffset, int lhsLength, SortOrder lhsSortOrder,
-      byte[] rhs, int rhsOffset, int rhsLength, SortOrder rhsSortOrder,
-      PDataType rhsType) {
-    Preconditions.checkNotNull(lhsSortOrder);
-    Preconditions.checkNotNull(rhsSortOrder);
-    if (this.isBytesComparableWith(rhsType)) { // directly compare the bytes
-      return compareTo(lhs, lhsOffset, lhsLength, lhsSortOrder, rhs, rhsOffset, rhsLength,
-          rhsSortOrder);
-    }
-    PDataCodec lhsCodec = this.getCodec();
-    if (lhsCodec
-        == null) { // no lhs native type representation, so convert rhsType to bytes representation of lhsType
-      byte[] rhsConverted =
-          this.toBytes(this.toObject(rhs, rhsOffset, rhsLength, rhsType, rhsSortOrder));
-      if (rhsSortOrder == SortOrder.DESC) {
-        rhsSortOrder = SortOrder.ASC;
-      }
-      if (lhsSortOrder == SortOrder.DESC) {
-        lhs = SortOrder.invert(lhs, lhsOffset, new byte[lhsLength], 0, lhsLength);
-      }
-      return Bytes.compareTo(lhs, lhsOffset, lhsLength, rhsConverted, 0, rhsConverted.length);
-    }
-    PDataCodec rhsCodec = rhsType.getCodec();
-    if (rhsCodec == null) {
-      byte[] lhsConverted =
-          rhsType.toBytes(rhsType.toObject(lhs, lhsOffset, lhsLength, this, lhsSortOrder));
-      if (lhsSortOrder == SortOrder.DESC) {
-        lhsSortOrder = SortOrder.ASC;
-      }
-      if (rhsSortOrder == SortOrder.DESC) {
-        rhs = SortOrder.invert(rhs, rhsOffset, new byte[rhsLength], 0, rhsLength);
-      }
-      return Bytes.compareTo(lhsConverted, 0, lhsConverted.length, rhs, rhsOffset, rhsLength);
-    }
-    // convert to native and compare
-    if (this.isCoercibleTo(PLong.INSTANCE)
-        && rhsType
-        .isCoercibleTo(PLong.INSTANCE)) { // native long to long comparison
-      return Longs.compare(this.getCodec().decodeLong(lhs, lhsOffset, lhsSortOrder),
-          rhsType.getCodec().decodeLong(rhs, rhsOffset, rhsSortOrder));
-    } else if (isDoubleOrFloat(this) && isDoubleOrFloat(
-        rhsType)) { // native double to double comparison
-      return Doubles.compare(this.getCodec().decodeDouble(lhs, lhsOffset, lhsSortOrder),
-          rhsType.getCodec().decodeDouble(rhs, rhsOffset, rhsSortOrder));
-    } else { // native float/double to long comparison
-      float fvalue = 0.0F;
-      double dvalue = 0.0;
-      long lvalue = 0;
-      boolean isFloat = false;
-      int invert = 1;
-
-      if (this.isCoercibleTo(PLong.INSTANCE)) {
-        lvalue = this.getCodec().decodeLong(lhs, lhsOffset, lhsSortOrder);
-      } else if (this.getClass() == PFloat.class) {
-        isFloat = true;
-        fvalue = this.getCodec().decodeFloat(lhs, lhsOffset, lhsSortOrder);
-      } else if (this.isCoercibleTo(PDouble.INSTANCE)) {
-        dvalue = this.getCodec().decodeDouble(lhs, lhsOffset, lhsSortOrder);
-      }
-      if (rhsType.isCoercibleTo(PLong.INSTANCE)) {
-        lvalue = rhsType.getCodec().decodeLong(rhs, rhsOffset, rhsSortOrder);
-      } else if (rhsType == PFloat.INSTANCE) {
-        invert = -1;
-        isFloat = true;
-        fvalue = rhsType.getCodec().decodeFloat(rhs, rhsOffset, rhsSortOrder);
-      } else if (rhsType.isCoercibleTo(PDouble.INSTANCE)) {
-        invert = -1;
-        dvalue = rhsType.getCodec().decodeDouble(rhs, rhsOffset, rhsSortOrder);
-      }
-      // Invert the comparison if float/double value is on the RHS
-      return invert * (isFloat ?
-          compareFloatToLong(fvalue, lvalue) :
-          compareDoubleToLong(dvalue, lvalue));
-    }
-  }
-
-  public static boolean isDoubleOrFloat(PDataType type) {
-    return type == PFloat.INSTANCE
-        || type == PDouble.INSTANCE
-        || type == PUnsignedFloat.INSTANCE || type == PUnsignedDouble.INSTANCE;
-  }
-
-  /**
-   * Compares a float against a long. Behaves better than
-   * {@link #compareDoubleToLong(double, long)} for float
-   * values outside of Integer.MAX_VALUE and Integer.MIN_VALUE.
-   *
-   * @param f a float value
-   * @param l a long value
-   * @return -1 if f is less than l, 1 if f is greater than l, and 0 if f is equal to l
-   */
-  private static int compareFloatToLong(float f, long l) {
-    if (f > Integer.MAX_VALUE || f < Integer.MIN_VALUE) {
-      return f < l ? -1 : f > l ? 1 : 0;
-    }
-    long diff = (long) f - l;
-    return Long.signum(diff);
-  }
-
-  /**
-   * Compares a double against a long.
-   *
-   * @param d a double value
-   * @param l a long value
-   * @return -1 if d is less than l, 1 if d is greater than l, and 0 if d is equal to l
-   */
-  private static int compareDoubleToLong(double d, long l) {
-    if (d > Long.MAX_VALUE) {
-      return 1;
-    }
-    if (d < Long.MIN_VALUE) {
-      return -1;
-    }
-    long diff = (long) d - l;
-    return Long.signum(diff);
-  }
-
-  protected static void checkForSufficientLength(byte[] b, int offset, int requiredLength) {
-    if (b.length < offset + requiredLength) {
-      throw new RuntimeException(new SQLExceptionInfo.Builder(SQLExceptionCode.ILLEGAL_DATA)
-          .setMessage(
-              "Expected length of at least " + requiredLength + " bytes, but had " + (b.length
-                  - offset)).build().buildException());
-    }
-  }
-
-  protected static Void throwConstraintViolationException(PDataType source, PDataType target) {
-    throw new ConstraintViolationException(
-        new SQLExceptionInfo.Builder(SQLExceptionCode.TYPE_MISMATCH)
-            .setMessage(source + " cannot be coerced to " + target).build().buildException());
-  }
-
-  protected static RuntimeException newIllegalDataException() {
-    return new IllegalDataException(new SQLExceptionInfo.Builder(SQLExceptionCode.ILLEGAL_DATA)
-        .build().buildException());
-  }
-
-  protected static RuntimeException newIllegalDataException(String msg) {
-    return new IllegalDataException(new SQLExceptionInfo.Builder(SQLExceptionCode.ILLEGAL_DATA)
-        .setMessage(msg).build().buildException());
-  }
-
-  protected static RuntimeException newIllegalDataException(Exception e) {
-    return new IllegalDataException(new SQLExceptionInfo.Builder(SQLExceptionCode.ILLEGAL_DATA)
-        .setRootCause(e).build().buildException());
-  }
+    private final String sqlTypeName;
+    private final int sqlType;
+    private final Class clazz;
+    private final byte[] clazzNameBytes;
+    private final byte[] sqlTypeNameBytes;
+    private final PDataCodec codec;
+    private final int ordinal;
+
+    protected PDataType(String sqlTypeName, int sqlType, Class clazz, PDataCodec codec, int ordinal) {
+        this.sqlTypeName = sqlTypeName;
+        this.sqlType = sqlType;
+        this.clazz = clazz;
+        this.clazzNameBytes = Bytes.toBytes(clazz.getName());
+        this.sqlTypeNameBytes = Bytes.toBytes(sqlTypeName);
+        this.codec = codec;
+        this.ordinal = ordinal;
+    }
+
+    @Deprecated
+    public static PDataType[] values() {
+        return PDataTypeFactory.getInstance().getOrderedTypes();
+    }
+
+    @Deprecated
+    public int ordinal() {
+        return ordinal;
+    }
+
+    @Override
+    public Class<T> encodedClass() {
+        return getJavaClass();
+    }
+
+    public boolean isCastableTo(PDataType targetType) {
+        return isComparableTo(targetType);
+    }
+
+    public final PDataCodec getCodec() {
+        return codec;
+    }
+
+    public boolean isBytesComparableWith(PDataType otherType) {
+        return this == otherType || this.getClass() == PVarbinary.class || otherType == PVarbinary.INSTANCE
+                || this.getClass() == PBinary.class || otherType == PBinary.INSTANCE;
+    }
+
+    public int estimateByteSize(Object o) {
+        if (isFixedWidth()) { return getByteSize(); }
+        if (isArrayType()) {
+            PhoenixArray array = (PhoenixArray)o;
+            int noOfElements = array.numElements;
+            int totalVarSize = 0;
+            for (int i = 0; i < noOfElements; i++) {
+                totalVarSize += array.estimateByteSize(i);
+            }
+            return totalVarSize;
+        }
+        // Non fixed width types must override this
+        throw new UnsupportedOperationException();
+    }
+
+    public Integer getMaxLength(Object o) {
+        return null;
+    }
+
+    public Integer getScale(Object o) {
+        return null;
+    }
+
+    /**
+     * Estimate the byte size from the type length. For example, for char, byte size would be the same as length. For
+     * decimal, byte size would have no correlation with the length.
+     */
+    public Integer estimateByteSizeFromLength(Integer length) {
+        if (isFixedWidth()) { return getByteSize(); }
+        if (isArrayType()) { return null; }
+        // If not fixed width, default to say the byte size is the same as length.
+        return length;
+    }
+
+    public final String getSqlTypeName() {
+        return sqlTypeName;
+    }
+
+    public final int getSqlType() {
+        return sqlType;
+    }
+
+    public final Class getJavaClass() {
+        return clazz;
+    }
+
+    public boolean isArrayType() {
+        return false;
+    }
 
-  @Override
-  public boolean equals(Object o) {
-    // PDataType's are expected to be singletons.
-    // TODO: this doesn't jive with HBase's DataType
-    if (o == null) return false;
-    return getClass() == o.getClass();
-  }
+    public final int compareTo(byte[] lhs, int lhsOffset, int lhsLength, SortOrder lhsSortOrder, byte[] rhs,
+            int rhsOffset, int rhsLength, SortOrder rhsSortOrder, PDataType rhsType) {
+        Preconditions.checkNotNull(lhsSortOrder);
+        Preconditions.checkNotNull(rhsSortOrder);
+        if (this.isBytesComparableWith(rhsType)) { // directly compare the bytes
+            // Special case as we may be comparing two arrays that have different separator characters due to PHOENIX-2067
+            if (!this.isArrayType() || !rhsType.isArrayType() || 
+                    PArrayDataType.isRowKeyOrderOptimized(this, lhsSortOrder, lhs, lhsOffset, lhsLength) == PArrayDataType.isRowKeyOrderOptimized(rhsType, rhsSortOrder, rhs, rhsOffset, rhsLength)) {
+                return compareTo(lhs, lhsOffset, lhsLength, lhsSortOrder, rhs, rhsOffset, rhsLength, rhsSortOrder);
+            }
+        }
+        PDataCodec lhsCodec = this.getCodec();
+        if ( lhsCodec == null ) {
+            byte[] rhsConverted;
+            Object o = this.toObject(rhs, rhsOffset, rhsLength, rhsType, rhsSortOrder);
+            
+            // No lhs native type representation, so convert rhsType to bytes representation of lhs type
+            // Due to PHOENIX-2067, favor the array that is already in the new format so we don't have to convert both.
+            if ( this.isArrayType() && PArrayDataType.isRowKeyOrderOptimized(this, lhsSortOrder, lhs, lhsOffset, lhsLength) == PArrayDataType.isRowKeyOrderOptimized(rhsType, rhsSortOrder, rhs, rhsOffset, rhsLength)) {
+                rhsConverted = ((PArrayDataType)this).toBytes(o, PArrayDataType.arrayBaseType(this), lhsSortOrder, PArrayDataType.isRowKeyOrderOptimized(this, lhsSortOrder, lhs, lhsOffset, lhsLength));
+            } else {
+                rhsConverted = this.toBytes(o);
+                if (rhsSortOrder == SortOrder.DESC) {
+                    rhsSortOrder = SortOrder.ASC;
+                }
+                if (lhsSortOrder == SortOrder.DESC) {
+                    lhs = SortOrder.invert(lhs, lhsOffset, new byte[lhsLength], 0, lhsLength);
+                }
+            }
+            return Bytes.compareTo(lhs, lhsOffset, lhsLength, rhsConverted, 0, rhsConverted.length);
+        }
+        PDataCodec rhsCodec = rhsType.getCodec();
+        if (rhsCodec == null) {
+            byte[] lhsConverted;
+            Object o = rhsType.toObject(lhs, lhsOffset, lhsLength, this, lhsSortOrder);
+            
+            // No rhs native type representation, so convert lhsType to bytes representation of rhs type
+            // Due to PHOENIX-2067, favor the array that is already in the new format so we don't have to convert both.
+            if ( rhsType.isArrayType() && PArrayDataType.isRowKeyOrderOptimized(rhsType, rhsSortOrder, rhs, rhsOffset, rhsLength) == PArrayDataType.isRowKeyOrderOptimized(this, lhsSortOrder, lhs, lhsOffset, lhsLength)) {
+                lhsConverted = ((PArrayDataType)rhsType).toBytes(o, PArrayDataType.arrayBaseType(rhsType), rhsSortOrder, PArrayDataType.isRowKeyOrderOptimized(rhsType, rhsSortOrder, rhs, rhsOffset, rhsLength));
+            } else {
+                lhsConverted = rhsType.toBytes(o);
+                if (lhsSortOrder == SortOrder.DESC) {
+                    lhsSortOrder = SortOrder.ASC;
+                }
+                if (rhsSortOrder == SortOrder.DESC) {
+                    rhs = SortOrder.invert(rhs, rhsOffset, new byte[rhsLength], 0, rhsLength);
+                }
+            }
+            return Bytes.compareTo(lhsConverted, 0, lhsConverted.length, rhs, rhsOffset, rhsLength);
+        }
+        // convert to native and compare
+        if (this.isCoercibleTo(PLong.INSTANCE) && rhsType.isCoercibleTo(PLong.INSTANCE)) { // native long to long
+                                                                                           // comparison
+            return Longs.compare(this.getCodec().decodeLong(lhs, lhsOffset, lhsSortOrder), rhsType.getCodec()
+                    .decodeLong(rhs, rhsOffset, rhsSortOrder));
+        } else if (isDoubleOrFloat(this) && isDoubleOrFloat(rhsType)) { // native double to double comparison
+            return Doubles.compare(this.getCodec().decodeDouble(lhs, lhsOffset, lhsSortOrder), rhsType.getCodec()
+                    .decodeDouble(rhs, rhsOffset, rhsSortOrder));
+        } else { // native float/double to long comparison
+            float fvalue = 0.0F;
+            double dvalue = 0.0;
+            long lvalue = 0;
+            boolean isFloat = false;
+            int invert = 1;
+
+            if (this.isCoercibleTo(PLong.INSTANCE)) {
+                lvalue = this.getCodec().decodeLong(lhs, lhsOffset, lhsSortOrder);
+            } else if (this.getClass() == PFloat.class) {
+                isFloat = true;
+                fvalue = this.getCodec().decodeFloat(lhs, lhsOffset, lhsSortOrder);
+            } else if (this.isCoercibleTo(PDouble.INSTANCE)) {
+                dvalue = this.getCodec().decodeDouble(lhs, lhsOffset, lhsSortOrder);
+            }
+            if (rhsType.isCoercibleTo(PLong.INSTANCE)) {
+                lvalue = rhsType.getCodec().decodeLong(rhs, rhsOffset, rhsSortOrder);
+            } else if (rhsType == PFloat.INSTANCE) {
+                invert = -1;
+                isFloat = true;
+                fvalue = rhsType.getCodec().decodeFloat(rhs, rhsOffset, rhsSortOrder);
+            } else if (rhsType.isCoercibleTo(PDouble.INSTANCE)) {
+                invert = -1;
+                dvalue = rhsType.getCodec().decodeDouble(rhs, rhsOffset, rhsSortOrder);
+            }
+            // Invert the comparison if float/double value is on the RHS
+            return invert * (isFloat ? compareFloatToLong(fvalue, lvalue) : compareDoubleToLong(dvalue, lvalue));
+        }
+    }
 
-  /**
-   * @return true when {@code lhs} equals any of {@code rhs}.
-   */
-  public static boolean equalsAny(PDataType lhs, PDataType... rhs) {
-    for (int i = 0; i < rhs.length; i++) {
-      if (lhs.equals(rhs[i])) return true;
+    public static boolean isDoubleOrFloat(PDataType type) {
+        return type == PFloat.INSTANCE || type == PDouble.INSTANCE || type == PUnsignedFloat.INSTANCE
+                || type == PUnsignedDouble.INSTANCE;
     }
-    return false;
-  }
 
-  public static interface PDataCodec {
-    public long decodeLong(ImmutableBytesWritable ptr, SortOrder sortOrder);
+    /**
+     * Compares a float against a long. Behaves better than {@link #compareDoubleToLong(double, long)} for float values
+     * outside of Integer.MAX_VALUE and Integer.MIN_VALUE.
+     *
+     * @param f
+     *            a float value
+     * @param l
+     *            a long value
+     * @return -1 if f is less than l, 1 if f is greater than l, and 0 if f is equal to l
+     */
+    private static int compareFloatToLong(float f, long l) {
+        if (f > Integer.MAX_VALUE || f < Integer.MIN_VALUE) { return f < l ? -1 : f > l ? 1 : 0; }
+        long diff = (long)f - l;
+        return Long.signum(diff);
+    }
 
-    public long decodeLong(byte[] b, int o, SortOrder sortOrder);
+    /**
+     * Compares a double against a long.
+     *
+     * @param d
+     *            a double value
+     * @param l
+     *            a long value
+     * @return -1 if d is less than l, 1 if d is greater than l, and 0 if d is equal to l
+     */
+    private static int compareDoubleToLong(double d, long l) {
+        if (d > Long.MAX_VALUE) { return 1; }
+        if (d < Long.MIN_VALUE) { return -1; }
+        long diff = (long)d - l;
+        return Long.signum(diff);
+    }
 
-    public int decodeInt(ImmutableBytesWritable ptr, SortOrder sortOrder);
+    protected static void checkForSufficientLength(byte[] b, int offset, int requiredLength) {
+        if (b.length < offset + requiredLength) { throw new RuntimeException(new SQLExceptionInfo.Builder(
+                SQLExceptionCode.ILLEGAL_DATA)
+                .setMessage("Expected length of at least " + requiredLength + " bytes, but had " + (b.length - offset))
+                .build().buildException()); }
+    }
 
-    public int decodeInt(byte[] b, int o, SortOrder sortOrder);
+    protected static Void throwConstraintViolationException(PDataType source, PDataType target) {
+        throw new ConstraintViolationException(new SQLExceptionInfo.Builder(SQLExceptionCode.TYPE_MISMATCH)
+                .setMessage(source + " cannot be coerced to " + target).build().buildException());
+    }
 
-    public byte decodeByte(ImmutableBytesWritable ptr, SortOrder sortOrder);
+    protected static RuntimeException newIllegalDataException() {
+        return new IllegalDataException(new SQLExceptionInfo.Builder(SQLExceptionCode.ILLEGAL_DATA).build()
+                .buildException());
+    }
 
-    public byte decodeByte(byte[] b, int o, SortOrder sortOrder);
+    protected static RuntimeException newIllegalDataException(String msg) {
+        return new IllegalDataException(new SQLExceptionInfo.Builder(SQLExceptionCode.ILLEGAL_DATA).setMessage(msg)
+                .build().buildException());
+    }
 
-    public short decodeShort(ImmutableBytesWritable ptr, SortOrder sortOrder);
+    protected static RuntimeException newIllegalDataException(Exception e) {
+        return new IllegalDataException(new SQLExceptionInfo.Builder(SQLExceptionCode.ILLEGAL_DATA).setRootCause(e)
+                .build().buildException());
+    }
 
-    public short decodeShort(byte[] b, int o, SortOrder sortOrder);
+    @Override
+    public boolean equals(Object o) {
+        // PDataType's are expected to be singletons.
+        // TODO: this doesn't jive with HBase's DataType
+        if (o == null) return false;
+        return getClass() == o.getClass();
+    }
+
+    /**
+     * @return true when {@code lhs} equals any of {@code rhs}.
+     */
+    public static boolean equalsAny(PDataType lhs, PDataType... rhs) {
+        for (int i = 0; i < rhs.length; i++) {
+            if (lhs.equals(rhs[i])) return true;
+        }
+        return false;
+    }
 
-    public float decodeFloat(ImmutableBytesWritable ptr, SortOrder sortOrder);
+    public static interface PDataCodec {
+        public long decodeLong(ImmutableBytesWritable ptr, SortOrder sortOrder);
 
-    public float decodeFloat(byte[] b, int o, SortOrder sortOrder);
+        public long decodeLong(byte[] b, int o, SortOrder sortOrder);
 
-    public double decodeDouble(ImmutableBytesWritable ptr, SortOrder sortOrder);
+        public int decodeInt(ImmutableBytesWritable ptr, SortOrder sortOrder);
 
-    public double decodeDouble(byte[] b, int o, SortOrder sortOrder);
+        public int decodeInt(byte[] b, int o, SortOrder sortOrder);
 
-    public int encodeLong(long v, ImmutableBytesWritable ptr);
+        public byte decodeByte(ImmutableBytesWritable ptr, SortOrder sortOrder);
 
-    public int encodeLong(long v, byte[] b, int o);
+        public byte decodeByte(byte[] b, int o, SortOrder sortOrder);
 
-    public int encodeInt(int v, ImmutableBytesWritable ptr);
+        public short decodeShort(ImmutableBytesWritable ptr, SortOrder sortOrder);
 
-    public int encodeInt(int v, byte[] b, int o);
+        public short decodeShort(byte[] b, int o, SortOrder sortOrder);
 
-    public int encodeByte(byte v, ImmutableBytesWritable ptr);
+        public float decodeFloat(ImmutableBytesWritable ptr, SortOrder sortOrder);
 
-    public int encodeByte(byte v, byte[] b, int o);
+        public float decodeFloat(byte[] b, int o, SortOrder sortOrder);
 
-    public int encodeShort(short v, ImmutableBytesWritable ptr);
+        public double decodeDouble(ImmutableBytesWritable ptr, SortOrder sortOrder);
 
-    public int encodeShort(short v, byte[] b, int o);
+        public double decodeDouble(byte[] b, int o, SortOrder sortOrder);
 
-    public int encodeFloat(float v, ImmutableBytesWritable ptr);
+        public int encodeLong(long v, ImmutableBytesWritable ptr);
 
-    public int encodeFloat(float v, byte[] b, int o);
+        public int encodeLong(long v, byte[] b, int o);
 
-    public int encodeDouble(double v, ImmutableBytesWritable ptr);
+        public int encodeInt(int v, ImmutableBytesWritable ptr);
 
-    public int encodeDouble(double v, byte[] b, int o);
+        public int encodeInt(int v, byte[] b, int o);
 
-    public PhoenixArrayFactory getPhoenixArrayFactory();
-  }
+        public int encodeByte(byte v, ImmutableBytesWritable ptr);
 
-  public static abstract class BaseCodec implements PDataCodec {
-    @Override
-    public int decodeInt(ImmutableBytesWritable ptr, SortOrder sortOrder) {
-      return decodeInt(ptr.get(), ptr.getOffset(), sortOrder);
+        public int encodeByte(byte v, byte[] b, int o);
+
+        public int encodeShort(short v, ImmutableBytesWritable ptr);
+
+        public int encodeShort(short v, byte[] b, int o);
+
+        public int encodeFloat(float v, ImmutableBytesWritable ptr);
+
+        public int encodeFloat(float v, byte[] b, int o);
+
+        public int encodeDouble(double v, ImmutableBytesWritable ptr);
+
+        public int encodeDouble(double v, byte[] b, int o);
+
+        public PhoenixArrayFactory getPhoenixArrayFactory();
     }
 
-    @Override
-    public long decodeLong(ImmutableBytesWritable ptr, SortOrder sortOrder) {
-      return decodeLong(ptr.get(), ptr.getOffset(), sortOrder);
+    public static abstract class BaseCodec implements PDataCodec {
+        @Override
+        public int decodeInt(ImmutableBytesWritable ptr, SortOrder sortOrder) {
+            return decodeInt(ptr.get(), ptr.getOffset(), sortOrder);
+        }
+
+        @Override
+        public long decodeLong(ImmutableBytesWritable ptr, SortOrder sortOrder) {
+            return decodeLong(ptr.get(), ptr.getOffset(), sortOrder);
+        }
+
+        @Override
+        public byte decodeByte(ImmutableBytesWritable ptr, SortOrder sortOrder) {
+            return decodeByte(ptr.get(), ptr.getOffset(), sortOrder);
+        }
+
+        @Override
+        public short decodeShort(ImmutableBytesWritable ptr, SortOrder sortOrder) {
+            return decodeShort(ptr.get(), ptr.getOffset(), sortOrder);
+        }
+
+        @Override
+        public float decodeFloat(ImmutableBytesWritable ptr, SortOrder sortOrder) {
+            return decodeFloat(ptr.get(), ptr.getOffset(), sortOrder);
+        }
+
+        @Override
+        public float decodeFloat(byte[] b, int o, SortOrder sortOrder) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public double decodeDouble(ImmutableBytesWritable ptr, SortOrder sortOrder) {
+            return decodeDouble(ptr.get(), ptr.getOffset(), sortOrder);
+        }
+
+        @Override
+        public double decodeDouble(byte[] b, int o, SortOrder sortOrder) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public int encodeInt(int v, ImmutableBytesWritable ptr) {
+            return encodeInt(v, ptr.get(), ptr.getOffset());
+        }
+
+        @Override
+        public int encodeLong(long v, ImmutableBytesWritable ptr) {
+            return encodeLong(v, ptr.get(), ptr.getOffset());
+        }
+
+        @Override
+        public int encodeByte(byte v, ImmutableBytesWritable ptr) {
+            return encodeByte(v, ptr.get(), ptr.getOffset());
+        }
+
+        @Override
+        public int encodeShort(short v, ImmutableBytesWritable ptr) {
+            return encodeShort(v, ptr.get(), ptr.getOffset());
+        }
+
+        @Override
+        public int encodeFloat(float v, ImmutableBytesWritable ptr) {
+            return encodeFloat(v, ptr.get(), ptr.getOffset());
+        }
+
+        @Override
+        public int encodeDouble(double v, ImmutableBytesWritable ptr) {
+            return encodeDouble(v, ptr.get(), ptr.getOffset());
+        }
+
+        @Override
+        public int encodeInt(int v, byte[] b, int o) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public int encodeLong(long v, byte[] b, int o) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public int encodeByte(byte v, byte[] b, int o) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public int encodeShort(short v, byte[] b, int o) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public int encodeFloat(float v, byte[] b, int o) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public int encodeDouble(double v, byte[] b, int o) {
+            throw new UnsupportedOperationException();
+        }
     }
 
-    @Override
-    public byte decodeByte(ImmutableBytesWritable ptr, SortOrder sortOrder) {
-      return decodeByte(ptr.get(), ptr.getOffset(), sortOrder);
+    public static final int MAX_PRECISION = 38;
+    // Max precision guaranteed to fit into a long (and this should be plenty)
+    public static final int MIN_DECIMAL_AVG_SCALE = 4;
+    public static final MathContext DEFAULT_MATH_CONTEXT = new MathContext(MAX_PRECISION, RoundingMode.HALF_UP);
+    public static final int DEFAULT_SCALE = 0;
+
+    protected static final Integer MAX_BIG_DECIMAL_BYTES = 21;
+    protected static final Integer MAX_TIMESTAMP_BYTES = Bytes.SIZEOF_LONG + Bytes.SIZEOF_INT;
+
+    protected static final byte ZERO_BYTE = (byte)0x80;
+    protected static final byte NEG_TERMINAL_BYTE = (byte)102;
+    protected static final int EXP_BYTE_OFFSET = 65;
+    protected static final int POS_DIGIT_OFFSET = 1;
+    protected static final int NEG_DIGIT_OFFSET = 101;
+    protected static final BigInteger MAX_LONG = BigInteger.valueOf(Long.MAX_VALUE);
+    protected static final BigInteger MIN_LONG = BigInteger.valueOf(Long.MIN_VALUE);
+    protected static final long MAX_LONG_FOR_DESERIALIZE = Long.MAX_VALUE / 1000;
+    protected static final BigInteger ONE_HUNDRED = BigInteger.valueOf(100);
+
+    protected static final byte FALSE_BYTE = 0;
+    protected static final byte TRUE_BYTE = 1;
+    public static final byte[] FALSE_BYTES = new byte[] { FALSE_BYTE };
+    public static final byte[] TRUE_BYTES = new byte[] { TRUE_BYTE };
+    public static final byte[] NULL_BYTES = ByteUtil.EMPTY_BYTE_ARRAY;
+    protected static final Integer BOOLEAN_LENGTH = 1;
+
+    public final static Integer ZERO = 0;
+    public final static Integer INT_PRECISION = 10;
+    public final static Integer LONG_PRECISION = 19;
+    public final static Integer SHORT_PRECISION = 5;
+    public final static Integer BYTE_PRECISION = 3;
+    public final static Integer DOUBLE_PRECISION = 15;
+
+    public static final int ARRAY_TYPE_BASE = 3000;
+    public static final String ARRAY_TYPE_SUFFIX = "ARRAY";
+
+    protected static final ThreadLocal<Random> RANDOM = new ThreadLocal<Random>() {
+        @Override
+        protected Random initialValue() {
+            return new Random();
+        }
+    };
+
+    /**
+     * Serialize a BigDecimal into a variable length byte array in such a way that it is binary comparable.
+     *
+     * @param v
+     *            the BigDecimal
+     * @param result
+     *            the byte array to contain the serialized bytes. Max size necessary would be 21 bytes.
+     * @param length
+     *            the number of bytes required to store the big decimal. May be adjusted down if it exceeds
+     *            {@link #MAX_BIG_DECIMAL_BYTES}
+     * @return the number of bytes that make up the serialized BigDecimal
+     */
+    protected static int toBytes(BigDecimal v, byte[] result, final int offset, int length) {
+        // From scale to exponent byte (if BigDecimal is positive): (-(scale+(scale % 2 == 0 : 0 : 1)) / 2 + 65) | 0x80
+        // If scale % 2 is 1 (i.e. it's odd), then multiple last base-100 digit by 10
+        // For example: new BigDecimal(BigInteger.valueOf(1), -4);
+        // (byte)((-(-4+0) / 2 + 65) | 0x80) = -61
+        // From scale to exponent byte (if BigDecimal is negative): ~(-(scale+1)/2 + 65 + 128) & 0x7F
+        // For example: new BigDecimal(BigInteger.valueOf(1), 2);
+        // ~(-2/2 + 65 + 128) & 0x7F = 63
+        int signum = v.signum();
+        if (signum == 0) {
+            result[offset] = ZERO_BYTE;
+            return 1;
+        }
+        int index = offset + length;
+        int scale = v.scale();
+        int expOffset = scale % 2 * (scale < 0 ? -1 : 1);
+        // In order to get twice as much of a range for scale, it
+        // is multiplied by 2. If the scale is an odd number, then
+        // the first digit is multiplied by 10 to make up for the
+        // scale being off by one.
+        int multiplyBy;
+        BigInteger divideBy;
+        if (expOffset == 0) {
+            multiplyBy = 1;
+            divideBy = ONE_HUNDRED;
+        } else {
+            multiplyBy = 10;
+            divideBy = BigInteger.TEN;
+        }
+        // Normalize the scale based on what is necessary to end up with a base 100 decimal (i.e. 10.123e3)
+        int digitOffset;
+        BigInteger compareAgainst;
+        if (signum == 1) {
+            digitOffset = POS_DIGIT_OFFSET;
+            compareAgainst = MAX_LONG;
+            scale -= (length - 2) * 2;
+            result[offset] = (byte)((-(scale + expOffset) / 2 + EXP_BYTE_OFFSET) | 0x80);
+        } else {
+            digitOffset = NEG_DIGIT_OFFSET;
+            compareAgainst = MIN_LONG;
+            // Scale adjustment shouldn't include terminal byte in length
+            scale -= (length - 2 - 1) * 2;
+            result[offset] = (byte)(~(-(scale + expOffset) / 2 + EXP_BYTE_OFFSET + 128) & 0x7F);
+            if (length <= MAX_BIG_DECIMAL_BYTES) {
+                result[--index] = NEG_TERMINAL_BYTE;
+            } else {
+                // Adjust length and offset down because we don't have enough room
+                length = MAX_BIG_DECIMAL_BYTES;
+                index = offset + length;
+            }
+        }
+        BigInteger bi = v.unscaledValue();
+        // Use BigDecimal arithmetic until we can fit into a long
+        while (bi.compareTo(compareAgainst) * signum > 0) {
+            BigInteger[] dandr = bi.divideAndRemainder(divideBy);
+            bi = dandr[0];
+            int digit = dandr[1].intValue();
+            result[--index] = (byte)(digit * multiplyBy + digitOffset);
+            multiplyBy = 1;
+            divideBy = ONE_HUNDRED;
+        }
+        long l = bi.longValue();
+        do {
+            long divBy = 100 / multiplyBy;
+            long digit = l % divBy;
+            l /= divBy;
+            result[--index] = (byte)(digit * multiplyBy + digitOffset);
+            multiplyBy = 1;
+        } while (l != 0);
+
+        return length;
     }
 
-    @Override
-    public short decodeShort(ImmutableBytesWritable ptr, SortOrder sortOrder) {
-      return decodeShort(ptr.get(), ptr.getOffset(), sortOrder);
+    /**
+     * Deserialize a variable length byte array into a BigDecimal. Note that because of the normalization that gets done
+     * to the scale, if you roundtrip a BigDecimal, it may not be equal before and after. However, the before and after
+     * number will always compare to be equal (i.e. <nBefore>.compareTo(<nAfter>) == 0)
+     *
+     * @param bytes
+     *            the bytes containing the number
+     * @param offset
+     *            the offset into the byte array
+     * @param length
+     *            the length of the serialized BigDecimal
+     * @return the BigDecimal value.
+     */
+    protected static BigDecimal toBigDecimal(byte[] bytes, int offset, int length) {
+        // From exponent byte back to scale: (<exponent byte> & 0x7F) - 65) * 2
+        // For example, (((-63 & 0x7F) - 65) & 0xFF) * 2 = 0
+        // Another example: ((-64 & 0x7F) - 65) * 2 = -2 (then swap the sign for the scale)
+        // If number is negative, going from exponent byte back to scale: (byte)((~<exponent byte> - 65 - 128) * 2)
+        // For example: new BigDecimal(new BigInteger("-1"), -2);
+        // (byte)((~61 - 65 - 128) * 2) = 2, so scale is -2
+        // Potentially, when switching back, the scale can be added by one and the trailing zero dropped
+        // For digits, just do a mod 100 on the BigInteger. Use long if BigInteger fits
+        if (length == 1 && bytes[offset] == ZERO_BYTE) { return BigDecimal.ZERO; }
+        int signum = ((bytes[offset] & 0x80) == 0) ? -1 : 1;
+        int scale;
+        int index;
+        int digitOffset;
+        long multiplier = 100L;
+        int begIndex = offset + 1;
+        if (signum == 1) {
+            scale = (byte)(((bytes[offset] & 0x7F) - 65) * -2);
+            index = offset + length;
+            digitOffset = POS_DIGIT_OFFSET;
+        } else {
+            scale = (byte)((~bytes[offset] - 65 - 128) * -2);
+            index = offset + length - (bytes[offset + length - 1] == NEG_TERMINAL_BYTE ? 1 : 0);
+            digitOffset = -NEG_DIGIT_OFFSET;
+        }
+        length = index - offset;
+        long l = signum * bytes[--index] - digitOffset;
+        if (l % 10 == 0) { // trailing zero
+            scale--; // drop trailing zero and compensate in the scale
+            l /= 10;
+            multiplier = 10;
+        }
+        // Use long arithmetic for as long as we can
+        while (index > begIndex) {
+            if (l >= MAX_LONG_FOR_DESERIALIZE || multiplier >= Long.MAX_VALUE / 100) {
+                multiplier = LongMath.divide(multiplier, 100L, RoundingMode.UNNECESSARY);
+                break; // Exit loop early so we don't overflow our multiplier
+            }
+            int digit100 = signum * bytes[--index] - digitOffset;
+            l += digit100 * multiplier;
+            multiplier = LongMath.checkedMultiply(multiplier, 100);
+        }
+
+        BigInteger bi;
+        // If still more digits, switch to BigInteger arithmetic
+        if (index > begIndex) {
+            bi = BigInteger.valueOf(l);
+            BigInteger biMultiplier = BigInteger.valueOf(multiplier).multiply(ONE_HUNDRED);
+            do {
+                int digit100 = signum * bytes[--index] - digitOffset;
+                bi = bi.add(biMultiplier.multiply(BigInteger.valueOf(digit100)));
+                biMultiplier = biMultiplier.multiply(ONE_HUNDRED);
+            } while (index > begIndex);
+            if (signum == -1) {
+                bi = bi.negate();
+            }
+        } else {
+            bi = BigInteger.valueOf(l * signum);
+        }
+        // Update the scale based on the precision
+        scale += (length - 2) * 2;
+        BigDecimal v = new BigDecimal(bi, scale);
+        return v;
     }
 
-    @Override
-    public float decodeFloat(ImmutableBytesWritable ptr, SortOrder sortOrder) {
-      return decodeFloat(ptr.get(), ptr.getOffset(), sortOrder);
+    // Calculate the precision and scale of a raw decimal bytes. Returns the values as an int
+    // array. The first value is precision, the second value is scale.
+    // Default scope for testing
+    protected static int[] getDecimalPrecisionAndScale(byte[] bytes, int offset, int length) {
+        // 0, which should have no precision nor scale.
+        if (length == 1 && bytes[offset] == ZERO_BYTE) { return new int[] { 0, 0 }; }
+        int signum = ((bytes[offset] & 0x80) == 0) ? -1 : 1;
+        int scale;
+        int index;
+        int digitOffset;
+        if (signum == 1) {
+            scale = (byte)(((bytes[offset] & 0x7F) - 65) * -2);
+            index = offset + length;
+            digitOffset = POS_DIGIT_OFFSET;
+        } else {
+            scale = (byte)((~bytes[offset] - 65 - 128) * -2);
+            index = offset + length - (bytes[offset + length - 1] == NEG_TERMINAL_BYTE ? 1 : 0);
+            digitOffset = -NEG_DIGIT_OFFSET;
+        }
+        length = index - offset;
+        int precision = 2 * (length - 1);
+        int d = signum * bytes[--index] - digitOffset;
+        if (d % 10 == 0) { // trailing zero
+            // drop trailing zero and compensate in the scale and precision.
+            d /= 10;
+            scale--;
+            precision -= 1;
+        }
+        d = signum * bytes[offset + 1] - digitOffset;
+        if (d < 10) { // Leading single digit
+            // Compensate in the precision.
+            precision -= 1;
+        }
+        // Update the scale based on the precision
+        scale += (length - 2) * 2;
+        if (scale < 0) {
+            precision = precision - scale;
+            scale = 0;
+        }
+        return new int[] { precision, scale };
     }
 
-    @Override
-    public float decodeFloat(byte[] b, int o, SortOrder sortOrder) {
-      throw new UnsupportedOperationException();
+    public boolean isCoercibleTo(PDataType targetType) {
+        return this.equals(targetType) || targetType.equals(PVarbinary.INSTANCE);
     }
 
-    @Override
-    public double decodeDouble(ImmutableBytesWritable ptr, SortOrder sortOrder) {
-      return decodeDouble(ptr.get(), ptr.getOffset(), sortOrder);
+    // Specialized on enums to take into account type hierarchy (i.e. UNSIGNED_LONG is comparable to INTEGER)
+    public boolean isComparableTo(PDataType targetType) {
+        return targetType.isCoercibleTo(this) || this.isCoercibleTo(targetType);
     }
 
-    @Override
-    public double decodeDouble(byte[] b, int o, SortOrder sortOrder) {
-      throw new UnsupportedOperationException();
+    public boolean isCoercibleTo(PDataType targetType, Object value) {
+        return isCoercibleTo(targetType);
     }
 
-    @Override
-    public int encodeInt(int v, ImmutableBytesWritable ptr) {
-      return encodeInt(v, ptr.get(), ptr.getOffset());
+    public boolean isSizeCompatible(ImmutableBytesWritable ptr, Object value, PDataType srcType, Integer maxLength,
+            Integer scale, Integer desiredMaxLength, Integer desiredScale) {
+        return true;
     }
 
-    @Override
-    public int encodeLong(long v, ImmutableBytesWritable ptr) {
-      return encodeLong(v, ptr.get(), ptr.getOffset());
+    public int compareTo(byte[] b1, byte[] b2) {
+        return compareTo(b1, 0, b1.length, SortOrder.getDefault(), b2, 0, b2.length, SortOrder.getDefault());
     }
 
-    @Override
-    public int encodeByte(byte v, ImmutableBytesWritable ptr) {
-      return encodeByte(v, ptr.get(), ptr.getOffset());
+    public final int compareTo(ImmutableBytesWritable ptr1, ImmutableBytesWritable ptr2) {
+        return compareTo(ptr1.get(), ptr1.getOffset(), ptr1.getLength(), SortOrder.getDefault(), ptr2.get(),
+                ptr2.getOffset(), ptr2.getLength(), SortOrder.getDefault());
+    }
+
+    public final int compareTo(byte[] ba1, int offset1, int length1, SortOrder so1, byte[] ba2, int offset2,
+            int length2, SortOrder so2) {
+        Preconditions.checkNotNull(so1);
+        Preconditions.checkNotNull(so2);
+        if (so1 != so2) {
+            int length = Math.min(length1, length2);
+            for (int i = 0; i < length; i++) {
+                byte b1 = ba1[offset1 + i];
+                byte b2 = ba2[offset2 + i];
+                if (so1 == SortOrder.DESC) {
+                    b1 = SortOrder.invert(b1);
+                } else {
+                    b2 = SortOrder.invert(b2);
+                }
+                int c = b1 - b2;
+                if (c != 0) { return c; }
+            }
+            return (length1 - length2);
+        }
+        return Bytes.compareTo(ba1, offset1, length1, ba2, offset2, length2) * (so1 == SortOrder.DESC ? -1 : 1);
+    }
+
+    public final int compareTo(ImmutableBytesWritable ptr1, SortOrder ptr1SortOrder, ImmutableBytesWritable ptr2,
+            SortOrder ptr2SortOrder, PDataType type2) {
+        return compareTo(ptr1.get(), ptr1.getOffset(), ptr1.getLength(), ptr1SortOrder, ptr2.get(), ptr2.getOffset(),
+                ptr2.getLength(), ptr2SortOrder, type2);
+    }
+
+    public int compareTo(Object lhs, Object rhs) {
+        return compareTo(lhs, rhs, this);
+    }
+
+    /*
+     * We need an empty byte array to mean null, since we have no other representation in the row key for null.
+     */
+    public final boolean isNull(byte[] value) {
+        return value == null || value.length == 0;
+    }
+
+    public byte[] toBytes(Object object, SortOrder sortOrder) {
+        Preconditions.checkNotNull(sortOrder);
+        byte[] bytes = toBytes(object);
+        if (sortOrder == SortOrder.DESC) {
+            SortOrder.invert(bytes, 0, bytes, 0, bytes.length);
+        }
+        return bytes;
+    }
+
+    public void coerceBytes(ImmutableBytesWritable ptr, Object o, PDataType actualType, Integer actualMaxLength,
+            Integer actualScale, SortOrder actualModifier, Integer desiredMaxLength, Integer desiredScale,
+            SortOrder expectedModifier, boolean expectedRowKeyOrderOptimizable) {
+        coerceBytes(ptr, o, actualType, actualMaxLength, actualScale, actualModifier, desiredMaxLength, desiredScale,
+                expectedModifier);
+    }
+
+    public void coerceBytes(ImmutableBytesWritable ptr, Object o, PDataType actualType, Integer actualMaxLength,
+            Integer actualScale, SortOrder actualModifier, Integer desiredMaxLength, Integer desiredScale,
+            SortOrder expectedModifier) {
+        Preconditions.checkNotNull(actualModifier);
+        Preconditions.checkNotNull(expectedModifier);
+        if (ptr.getLength() == 0) { return; }
+        if (this.isBytesComparableWith(actualType)) { // No coerce necessary
+            if (actualModifier == expectedModifier) { return; }
+            byte[] b = ptr.copyBytes();
+            SortOrder.invert(b, 0, b, 0, b.length);
+            ptr.set(b);
+            return;
+        }
+
+        // Optimization for cases in which we already have the object around
+        if (o == null) {
+            o = actualType.toObject(ptr, actualType, actualModifier);
+        }
+
+        o = toObject(o, actualType);
+        byte[] b = toBytes(o, expectedModifier);
+        ptr.set(b);
+    }
+
+    public final void coerceBytes(ImmutableBytesWritable ptr, PDataType actualType, SortOrder actualModifier,
+            SortOrder expectedModifier) {
+        coerceBytes(ptr, null, actualType, null, null, actualModifier, null, null, expectedModifier);
+    }
+
+    public final void coerceBytes(ImmutableBytesWritable ptr, PDataType actualType, SortOrder actualModifier,
+            SortOrder expectedModifier, Integer desiredMaxLength) {
+        coerceBytes(ptr, null, actualType, null, null, actualModifier, desiredMaxLength, null, expectedModifier);
+    }
+
+    protected static boolean isNonNegativeDate(java.util.Date date) {
+        return (date == null || date.getTime() >= 0);
+    }
+
+    protected static void throwIfNonNegativeDate(java.util.Date date) {
+        if (!isNonNegativeDate(date)) { throw newIllegalDataException("Value may not be negative(" + date + ")"); }
+    }
+
+    protected static boolean isNonNegativeNumber(Number v) {
+        return v == null || v.longValue() >= 0;
+    }
+
+    protected static void throwIfNonNegativeNumber(Number v) {
+        if (!isNonNegativeNumber(v)) { throw newIllegalDataException("Value may not be negative(" + v + ")"); }
     }
 
     @Override
-    public int encodeShort(short v, ImmutableBytesWritable ptr) {
-      return encodeShort(v, ptr.get(), ptr.getOffset());
+    public boolean isNullable() {
+        return false;
     }
 
+    public abstract Integer getByteSize();
+
     @Override
-    public int encodeFloat(float v, ImmutableBytesWritable ptr) {
-      return encodeFloat(v, ptr.get(), ptr.getOffset());
+    public int encodedLength(T val) {
+        // default implementation based on existing PDataType methods.
+        return getByteSize();
     }
 
     @Override
-    public int encodeDouble(double v, ImmutableBytesWritable ptr) {
-      return encodeDouble(v, ptr.get(), ptr.getOffset());
+    public int skip(PositionedByteRange pbr) {
+        // default implementation based on existing PDataType methods.
+        int len = getByteSize();
+        pbr.setPosition(pbr.getPosition() + len);
+        return len;
     }
 
     @Override
-    public int encodeInt(int v, byte[] b, int o) {
-      throw new UnsupportedOperationException();
+    public boolean isOrderPreserving() {
+        return true;
     }
 
     @Override
-    public int encodeLong(long v, byte[] b, int o) {
-      throw new UnsupportedOperationException();
+    public boolean isSkippable() {
+        return true;
     }
 
     @Override
-    public int encodeByte(byte v, byte[] b, int o) {
-      throw new UnsupportedOperationException();
+    public Order getOrder() {
+        return Order.ASCENDING;
     }
 
+    public abstract boolean isFixedWidth();
+
+    public abstract int compareTo(Object lhs, Object rhs, PDataType rhsType);
+
     @Override
-    public int encodeShort(short v, byte[] b, int o) {
-      throw new UnsupportedOperationException();
+    public int compareTo(PDataType<?> other) {
+        return Integer.compare(this.ordinal(), other.ordinal());
     }
 
+    /**
+     * Convert from the object representation of a data type value into the serialized byte form.
+     *
+     * @param object
+     *            the object to convert
+     * @param bytes
+     *            the byte array into which to put the serialized form of object
+     * @param offset
+     *            the offset from which to start writing the serialized form
+     * @return the byte length of the serialized object
+     */
+    public abstract int toBytes(Object object, byte[] bytes, int offset);
+
     @Override
-    public int encodeFloat(float v, byte[] b, int o) {
-      throw new UnsupportedOperationException();
+    public int encode(PositionedByteRange pbr, T val) {
+        // default implementation based on existing PDataType methods.
+        int pos = pbr.getPosition();
+        pbr.put(toBytes(val));
+        return pbr.getPosition() - pos;
     }
 
     @Override
-    public int encodeDouble(double v, byte[] b, int o) {
-      throw new UnsupportedOperationException();
-    }
-  }
-
-  public static final int MAX_PRECISION = 38;
-      // Max precision guaranteed to fit into a long (and this should be plenty)
-  public static final int MIN_DECIMAL_AVG_SCALE = 4;
-  public static final MathContext DEFAULT_MATH_CONTEXT =
-      new MathContext(MAX_PRECISION, RoundingMode.HALF_UP);
-  public static final int DEFAULT_SCALE = 0;
-
-  protected static final Integer MAX_BIG_DECIMAL_BYTES = 21;
-  protected static final Integer MAX_TIMESTAMP_BYTES = Bytes.SIZEOF_LONG + Bytes.SIZEOF_INT;
-
-  protected static final byte ZERO_BYTE = (byte) 0x80;
-  protected static final byte NEG_TERMINAL_BYTE = (byte) 102;
-  protected static final int EXP_BYTE_OFFSET = 65;
-  protected static final int POS_DIGIT_OFFSET = 1;
-  protected static final int NEG_DIGIT_OFFSET = 101;
-  protected static final BigInteger MAX_LONG = BigInteger.valueOf(Long.MAX_VALUE);
-  protected static final BigInteger MIN_LONG = BigInteger.valueOf(Long.MIN_VALUE);
-  protected static final long MAX_LONG_FOR_DESERIALIZE = Long.MAX_VALUE / 1000;
-  protected static final BigInteger ONE_HUNDRED = BigInteger.valueOf(100);
-
-  protected static final byte FALSE_BYTE = 0;
-  protected static final byte TRUE_BYTE = 1;
-  public static final byte[] FALSE_BYTES = new byte[] { FALSE_BYTE };
-  public static final byte[] TRUE_BYTES = new byte[] { TRUE_BYTE };
-  public static final byte[] NULL_BYTES = ByteUtil.EMPTY_BYTE_ARRAY;
-  protected static final Integer BOOLEAN_LENGTH = 1;
-
-  public final static Integer ZERO = 0;
-  public final static Integer INT_PRECISION = 10;
-  public final static Integer LONG_PRECISION = 19;
-  public final static Integer SHORT_PRECISION = 5;
-  public final static Integer BYTE_PRECISION = 3;
-  public final static Integer DOUBLE_PRECISION = 15;
-
-  public static final int ARRAY_TYPE_BASE = 3000;
-  public static final String ARRAY_TYPE_SUFFIX = "ARRAY";
-
-  protected static final ThreadLocal<Random> RANDOM = new ThreadLocal<Random>() {
+    public String toString() {
+        return sqlTypeName;
+    }
+
+    public abstract byte[] toBytes(Object object);
+
+    /**
+     * Convert from a string to the object representation of a given type
+     *
+     * @param value
+     *            a stringified value
+     * @return the object representation of a string value
+     */
+    public abstract Object toObject(String value);
+
+    /*
+     * Each enum must override this to define the set of object it may be coerced to
+     */
+    public abstract Object toObject(Object object, PDataType actualType);
+
+    /*
+     * Each enum must override this to define the set of objects it may create
+     */
+    public abstract Object toObject(byte[] bytes, int offset, int length, PDataType actualType, SortOrder sortOrder,
+            Integer maxLength, Integer scale);
+
     @Override
-    protected Random initialValue() {
-      return new Random();
-    }
-  };
-
-  /**
-   * Serialize a BigDecimal into a variable length byte array in such a way that it is
-   * binary comparable.
-   *
-   * @param v      the BigDecimal
-   * @param result the byte array to contain the serialized bytes.  Max size
-   *               necessary would be 21 bytes.
-   * @param length the number of bytes required to store the big decimal. May be
-   *               adjusted down if it exceeds {@link #MAX_BIG_DECIMAL_BYTES}
-   * @return the number of bytes that make up the serialized BigDecimal
-   */
-  protected static int toBytes(BigDecimal v, byte[] result, final int offset, int length) {
-    // From scale to exponent byte (if BigDecimal is positive):  (-(scale+(scale % 2 == 0 : 0 : 1)) / 2 + 65) | 0x80
-    // If scale % 2 is 1 (i.e. it's odd), then multiple last base-100 digit by 10
-    // For example: new BigDecimal(BigInteger.valueOf(1), -4);
-    // (byte)((-(-4+0) / 2 + 65) | 0x80) = -61
-    // From scale to exponent byte (if BigDecimal is negative): ~(-(scale+1)/2 + 65 + 128) & 0x7F
-    // For example: new BigDecimal(BigInteger.valueOf(1), 2);
-    // ~(-2/2 + 65 + 128) & 0x7F = 63
-    int signum = v.signum();
-    if (signum == 0) {
-      result[offset] = ZERO_BYTE;
-      return 1;
-    }
-    int index = offset + length;
-    int scale = v.scale();
-    int expOffset = scale % 2 * (scale < 0 ? -1 : 1);
-    // In order to get twice as much of a range for scale, it
-    // is multiplied by 2. If the scale is an odd number, then
-    // the first digit is multiplied by 10 to make up for the
-    // scale being off by one.
-    int multiplyBy;
-    BigInteger divideBy;
-    if (expOffset == 0) {
-      multiplyBy = 1;
-      divideBy = ONE_HUNDRED;
-    } else {
-      multiplyBy = 10;
-      divideBy = BigInteger.TEN;
-    }
-    // Normalize the scale based on what is necessary to end up with a base 100 decimal (i.e. 10.123e3)
-    int digitOffset;
-    BigInteger compareAgainst;
-    if (signum == 1) {
-      digitOffset = POS_DIGIT_OFFSET;
-      compareAgainst = MAX_LONG;
-      scale -= (length - 2) * 2;
-      result[offset] = (byte) ((-(scale + expOffset) / 2 + EXP_BYTE_OFFSET) | 0x80);
-    } else {
-      digitOffset = NEG_DIGIT_OFFSET;
-      compareAgainst = MIN_LONG;
-      // Scale adjustment shouldn't include terminal byte in length
-      scale -= (length - 2 - 1) * 2;
-      result[offset] = (byte) (~(-(scale + expOffset) / 2 + EXP_BYTE_OFFSET + 128) & 0x7F);
-      if (length <= MAX_BIG_DECIMAL_BYTES) {
-        result[--index] = NEG_TERMINAL_BYTE;
-      } else {
-        // Adjust length and offset down because we don't have enough room
-        length = MAX_BIG_DECIMAL_BYTES;
-        index = offset + length;
-      }
-    }
-    BigInteger bi = v.unscaledValue();
-    // Use BigDecimal arithmetic until we can fit into a long
-    while (bi.compareTo(compareAgainst) * signum > 0) {
-      BigInteger[] dandr = bi.divideAndRemainder(divideBy);
-      bi = dandr[0];
-      int digit = dandr[1].intValue();
-      result[--index] = (byte) (digit * multiplyBy + digitOffset);
-      multiplyBy = 1;
-      divideBy = ONE_HUNDRED;
-    }
-    long l = bi.longValue();
-    do {
-      long divBy = 100 / multiplyBy;
-      long digit = l % divBy;
-      l /= divBy;
-      result[--index] = (byte) (digit * multiplyBy + digitOffset);
-      multiplyBy = 1;
-    } while (l != 0);
-
-    return length;
-  }
-
-  /**
-   * Deserialize a variable length byte array into a BigDecimal. Note that because of
-   * the normalization that gets done to the scale, if you roundtrip a BigDecimal,
-   * it may not be equal before and after. However, the before and after number will
-   * always compare to be equal (i.e. <nBefore>.compareTo(<nAfter>) == 0)
-   *
-   * @param bytes  the bytes containing the number
-   * @param offset the offset into the byte array
-   * @param length the length of the serialized BigDecimal
-   * @return the BigDecimal value.
-   */
-  protected static BigDecimal toBigDecimal(byte[] bytes, int offset, int length) {
-    // From exponent byte back to scale: (<exponent byte> & 0x7F) - 65) * 2
-    // For example, (((-63 & 0x7F) - 65) & 0xFF) * 2 = 0
-    // Another example: ((-64 & 0x7F) - 65) * 2 = -2 (then swap the sign for the scale)
-    // If number is negative, going from exponent byte back to scale: (byte)((~<exponent byte> - 65 - 128) * 2)
-    // For example: new BigDecimal(new BigInteger("-1"), -2);
-    // (byte)((~61 - 65 - 128) * 2) = 2, so scale is -2
-    // Potentially, when switching back, the scale can be added by one and the trailing zero dropped
-    // For digits, just do a mod 100 on the BigInteger. Use long if BigInteger fits
-    if (length == 1 && bytes[offset] == ZERO_BYTE) {
-      return BigDecimal.ZERO;
-    }
-    int signum = ((bytes[offset] & 0x80) == 0) ? -1 : 1;
-    int scale;
-    int index;
-    int digitOffset;
-    long multiplier = 100L;
-    int begIndex = offset + 1;
-    if (signum == 1) {
-      scale = (byte) (((bytes[offset] & 0x7F) - 65) * -2);
-      index = offset + length;
-      digitOffset = POS_DIGIT_OFFSET;
-    } else {
-      scale = (byte) ((~bytes[offset] - 65 - 128) * -2);
-      index = offset + length - (bytes[offset + length - 1] == NEG_TERMINAL_BYTE ? 1 : 0);
-      digitOffset = -NEG_DIGIT_OFFSET;
-    }
-    length = index - offset;
-    long l = signum * bytes[--index] - digitOffset;
-    if (l % 10 == 0) { // trailing zero
-      scale--; // drop trailing zero and compensate in the scale
-      l /= 10;
-      multiplier = 10;
-    }
-    // Use long arithmetic for as long as we can
-    while (index > begIndex) {
-      if (l >= MAX_LONG_FOR_DESERIALIZE || multiplier >= Long.MAX_VALUE / 100) {
-        multiplier = LongMath.divide(multiplier, 100L, RoundingMode.UNNECESSARY);
-        break; // Exit loop early so we don't overflow our multiplier
-      }
-      int digit100 = signum * bytes[--index] - digitOffset;
-      l += digit100 * multiplier;
-      multiplier = LongMath.checkedMultiply(multiplier, 100);
-    }
-
-    BigInteger bi;
-    // If still more digits, switch to BigInteger arithmetic
-    if (index > begIndex) {
-      bi = BigInteger.valueOf(l);
-      BigInteger biMultiplier = BigInteger.valueOf(multiplier).multiply(ONE_HUNDRED);
-      do {
-        int digit100 = signum * bytes[--index] - digitOffset;
-        bi = bi.add(biMultiplier.multiply(BigInteger.valueOf(digit100)));
-        biMultiplier = biMultiplier.multiply(ONE_HUNDRED);
-      } while (index > begIndex);
-      if (signum == -1) {
-        bi = bi.negate();
-      }
-    } else {
-      bi = BigInteger.valueOf(l * signum);
-    }
-    // Update the scale based on the precision
-    scale += (length - 2) * 2;
-    BigDecimal v = new BigDecimal(bi, scale);
-    return v;
-  }
-
-  // Calculate the precision and scale of a raw decimal bytes. Returns the values as an int
-  // array. The first value is precision, the second value is scale.
-  // Default scope for testing
-  protected static int[] getDecimalPrecisionAndScale(byte[] bytes, int offset, int length) {
-    // 0, which should have no precision nor scale.
-    if (length == 1 && bytes[offset] == ZERO_BYTE) {
-      return new int[] { 0, 0 };
-    }
-    int signum = ((bytes[offset] & 0x80) == 0) ? -1 : 1;
-    int scale;
-    int index;
-    int digitOffset;
-    if (signum == 1) {
-      scale = (byte) (((bytes[offset] & 0x7F) - 65) * -2);
-      index = offset + length;
-      digitOffset = POS_DIGIT_OFFSET;
-    } else {
-      scale = (byte) ((~bytes[offset] - 65 - 128) * -2);
-      index = offset + length - (bytes[offset + length - 1] == NEG_TERMINAL_BYTE ? 1 : 0);
-      digitOffset = -NEG_DIGIT_OFFSET;
-    }
-    length = index - offset;
-    int precision = 2 * (length - 1);
-    int d = signum * bytes[--index] - digitOffset;
-    if (d % 10 == 0) { // trailing zero
-      // drop trailing zero and compensate in the scale and precision.
-      d /= 10;
-      scale--;
-      precision -= 1;
-    }
-    d = signum * bytes[offset + 1] - digitOffset;
-    if (d < 10) { // Leading single digit
-      // Compensate in the precision.
-      precision -= 1;
-    }
-    // Update the scale based on the precision
-    scale += (length - 2) * 2;
-    if (scale < 0) {
-      precision = precision - scale;
-      scale = 0;
-    }
-    return new int[] { precision, scale };
-  }
-
-  public boolean isCoercibleTo(PDataType targetType) {
-    return this.equals(targetType) || targetType.equals(PVarbinary.INSTANCE);
-  }
-
-  // Specialized on enums to take into account type hierarchy (i.e. UNSIGNED_LONG is comparable to INTEGER)
-  public boolean isComparableTo(PDataType targetType) {
-    return targetType.isCoercibleTo(this) || this.isCoercibleTo(targetType);
-  }
-
-  public boolean isCoercibleTo(PDataType targetType, Object value) {
-    return isCoercibleTo(targetType);
-  }
-
-  public boolean isSizeCompatible(ImmutableBytesWritable ptr, Object value, PDataType srcType,
-      Integer maxLength, Integer scale, Integer desiredMaxLength, Integer desiredScale) {
-    return true;
-  }
-
-  public int compareTo(byte[] b1, byte[] b2) {
-    return compareTo(b1, 0, b1.length, SortOrder.getDefault(), b2, 0, b2.length,
-        SortOrder.getDefault());
-  }
-
-  public final int compareTo(ImmutableBytesWritable ptr1, ImmutableBytesWritable ptr2) {
-    return compareTo(ptr1.get(), ptr1.getOffset(), ptr1.getLength(), SortOrder.getDefault(),
-        ptr2.get(), ptr2.getOffset(), ptr2.getLength(), SortOrder.getDefault());
-  }
-
-  public final int compareTo(byte[] ba1, int offset1, int length1, SortOrder so1, byte[] ba2,
-      int offset2, int length2, SortOrder so2) {
-    Preconditions.checkNotNull(so1);
-    Preconditions.checkNotNull(so2);
-    if (so1 != so2) {
-      int length = Math.min(length1, length2);
-      for (int i = 0; i < length; i++) {
-        byte b1 = ba1[offset1 + i];
-        byte b2 = ba2[offset2 + i];
-        if (so1 == SortOrder.DESC) {
-          b1 = SortOrder.invert(b1);
-        } else {
-          b2 = SortOrder.invert(b2);
+    public T decode(PositionedByteRange pbr) {
+        // default implementation based on existing PDataType methods.
+        byte[] b = new byte[getByteSize()];
+        pbr.get(b);
+        return (T)toObject(b, 0, b.length, this, SortOrder.ASC, getMaxLength(null), getScale(null));
+    }
+
+    /*
+     * Return a valid object of this enum type
+     */
+    public abstract Object getSampleValue(Integer maxLength, Integer arrayLength);
+
+    public final Object getSampleValue() {
+        return getSampleValue(null);
+    }
+
+    public final Object getSampleValue(Integer maxLength) {
+        return getSampleValue(maxLength, null);
+    }
+
+    public final Object toObject(byte[] bytes, int offset, int length, PDataType actualType, SortOrder sortOrder) {
+        return toObject(bytes, offset, length, actualType, sortOrder, null, null);
+    }
+
+    public final Object toObject(byte[] bytes, int offset, int length, PDataType actualType) {
+        return toObject(bytes, offset, length, actualType, SortOrder.getDefault());
+    }
+
+    public final Object toObject(ImmutableBytesWritable ptr, PDataType actualType) {
+        return toObject(ptr, actualType, SortOrder.getDefault());
+    }
+
+    public final Object toObject(ImmutableBytesWritable ptr, PDataType actualType, SortOrder sortOrder) {
+        return this.toObject(ptr.get(), ptr.getOffset(), ptr.getLength(), actualType, sortOrder);
+    }
+
+    public final Object toObject(ImmutableBytesWritable ptr, PDataType actualType, SortOrder sortOrder,
+            Integer maxLength, Integer scale) {
+        return this.toObject(ptr.get(), ptr.getOffset(), ptr.getLength(), actualType, sortOrder, maxLength, scale);
+    }
+
+    public final Object toObject(ImmutableBytesWritable ptr, SortOrder sortOrder, Integer maxLength, Integer scale) {
+        return this.toObject(ptr.get(), ptr.getOffset(), ptr.getLength(), this, sortOrder, maxLength, scale);
+    }
+
+    public final Object toObject(ImmutableBytesWritable ptr) {
+        return toObject(ptr.get(), ptr.getOffset(), ptr.getLength());
+    }
+
+    public final Object toObject(ImmutableBytesWritable ptr, SortOrder sortOrder) {
+        return toObject(ptr.get(), ptr.getOffset(), ptr.getLength(), this, sortOrder);
+    }
+
+    public final Object toObject(byte[] bytes, int offset, int length) {
+        return toObject(bytes, offset, length, this);
+    }
+
+    public final Object toObject(byte[] bytes) {
+        return toObject(bytes, SortOrder.getDefault());
+    }
+
+    public final Object toObject(byte[] bytes, SortOrder sortOrder) {
+        return toObject(bytes, 0, bytes.length, this, sortOrder);
+    }
+
+    public final Object toObject(byte[] bytes, SortOrder sortOrder, PDataType actualType) {
+        return toObject(bytes, 0, bytes.length, actualType, sortOrder);
+    }
+
+    public static PDataType fromSqlTypeName(String sqlTypeName) {
+        for (PDataType t : PDataTypeFactory.getInstance().getTypes()) {
+            if (t.getSqlTypeName().equals(sqlTypeName)) return t;
         }
-        int c = b1 - b2;
-        if (c != 0) {
-          return c;
+        throw newIllegalDataException("Unsupported sql type: " + sqlTypeName);
+    }
+
+    public static int sqlArrayType(String sqlTypeName) {
+        PDataType fromSqlTypeName = fromSqlTypeName(sqlTypeName);
+        return fromSqlTypeName.getSqlType() + PDataType.ARRAY_TYPE_BASE;
+    }
+
+    protected static interface PhoenixArrayFactory {
+        PhoenixArray newArray(PDataType type, Object[] elements);
+    }
+
+    public static PDataType fromTypeId(int typeId) {
+        for (PDataType t : PDataTypeFactory.getInstance().getTypes()) {
+            if (t.getSqlType() == typeId) return t;
         }
-      }
-      return (length1 - length2);
-    }
-    return Bytes.compareTo(ba1, offset1, length1, ba2, offset2, length2) * (so1 == SortOrder.DESC ?
-        -1 :
-        1);
-  }
-
-  public final int compareTo(ImmutableBytesWritable ptr1, SortOrder ptr1SortOrder,
-      ImmutableBytesWritable ptr2, SortOrder ptr2SortOrder, PDataType type2) {
-    return compareTo(ptr1.get(), ptr1.getOffset(), ptr1.getLength(), ptr1SortOrder, ptr2.get(),
-        ptr2.getOffset(), ptr2.getLength(), ptr2SortOrder, type2);
-  }
-
-  public int compareTo(Object lhs, Object rhs) {
-    return compareTo(lhs, rhs, this);
-  }
-
-  /*
-   * We need an empty byte array to mean null, since
-   * we have no other representation in the row key
-   * for null.
-   */
-  public final boolean isNull(byte[] value) {
-    return value == null || value.length == 0;
-  }
-
-  public byte[] toBytes(Object object, SortOrder sortOrder) {
-    Preconditions.checkNotNull(sortOrder);
-    byte[] bytes = toBytes(object);
-    if (sortOrder == SortOrder.DESC) {
-      SortOrder.invert(bytes, 0, bytes, 0, bytes.length);
-    }
-    return bytes;
-  }
-
-  public void coerceBytes(ImmutableBytesWritable ptr, Object o, PDataType actualType,
-      Integer actualMaxLength, Integer actualScale, SortOrder actualModifier,
-      Integer desiredMaxLength, Integer desiredScale, SortOrder expectedModifier) {
-    Preconditions.checkNotNull(actualModifier);
-    Preconditions.checkNotNull(expectedModifier);
-    if (ptr.getLength() == 0) {
-      return;
-    }
-    if (this.isBytesComparableWith(actualType)) { // No coerce necessary
-      if (actualModifier == expectedModifier) {
-        return;
-      }
-      byte[] b = ptr.copyBytes();
-      SortOrder.invert(b, 0, b, 0, b.length);
-      ptr.set(b);
-      return;
-    }
-
-    // Optimization for cases in which we already have the object around
-    if (o == null) {
-      o = actualType.toObject(ptr, actualType, actualModifier);
-    }
-
-    o = toObject(o, actualType);
-    byte[] b = toBytes(o, expectedModifier);
-    ptr.set(b);
-  }
-
-  public final void coerceBytes(ImmutableBytesWritable ptr, PDataType actualType,
-      SortOrder actualModifier, SortOrder expectedModifier) {
-    coerceBytes(ptr, null, actualType, null, null, actualModifier, null, null, expectedModifier);
-  }
-
-  public final void coerceBytes(ImmutableBytesWritable ptr, PDataType actualType,
-      SortOrder actualModifier,
-      SortOrder expectedModifier, Integer desiredMaxLength) {
-    coerceBytes(ptr, null, actualType, null, null, actualModifier, desiredMaxLength, null,
-        expectedModifier);
-  }
-
-  protected static boolean isNonNegativeDate(java.util.Date date) {
-    return (date == null || date.getTime() >= 0);
-  }
-
-  protected static void throwIfNonNegativeDate(java.util.Date date) {
-    if (!isNonNegativeDate(date)) {
-      throw newIllegalDataException("Value may not be negative(" + date + ")");
-    }
-  }
-
-  protected static boolean isNonNegativeNumber(Number v) {
-    return v == null || v.longValue() >= 0;
-  }
-
-  protected static void throwIfNonNegativeNumber(Number v) {
-    if (!isNonNegativeNumber(v)) {
-      throw newIllegalDataException("Value may not be negative(" + v + ")");
-    }
-  }
-
-  @Override
-  public boolean isNullable() {
-    return false;
-  }
-
-  public abstract Integer getByteSize();
-
-  @Override
-  public int encodedLength(T val) {
-    // default implementation based on existing PDataType methods.
-    return getByteSize();
-  }
-
-  @Override
-  public int skip(PositionedByteRange pbr) {
-    // default implementation based on existing PDataType methods.
-    int len = getByteSize();
-    pbr.setPosition(pbr.getPosition() + len);
-    return len;
-  }
-
-  @Override
-  public boolean isOrderPreserving() {
-    return true;
-  }
-
-  @Override
-  public boolean isSkippable() {
-    return true;
-  }
-
-  @Override
-  public Order getOrder() {
-    return Order.ASCENDING;
-  }
-
-  public abstract boolean isFixedWidth();
-
-  public abstract int compareTo(Object lhs, Object rhs, PDataType rhsType);
-
-  @Override
-  public int compareTo(PDataType<?> other) {
-    return Integer.compare(this.ordinal(), other.ordinal());
-  }
-
-  /**
-   * Convert from the object representation of a data type value into
-   * the serialized byte form.
-   *
-   * @param object the object to convert
-   * @param bytes  the byte array into which to put the serialized form of object
-   * @param offset the offset from which to start writing the serialized form
-   * @return the byte length of the serialized object
-   */
-  public abstract int toBytes(Object object, byte[] bytes, int offset);
-
-  @Override
-  public int encode(PositionedByteRange pbr, T val) {
-    // default implementation based on existing PDataType methods.
-    int pos = pbr.getPosition();
-    pbr.put(toBytes(val));
-    return pbr.getPosition() - pos;
-  }
-
-  @Override
-  public String toString() {
-    return sqlTypeName;
-  }
-
-  public abstract byte[] toBytes(Object object);
-
-  /**
-   * Convert from a string to the object representation of a given type
-   *
-   * @param value a stringified value
-   * @return the object representation of a string value
-   */
-  public abstract Object toObject(String value);
-
-  /*
-   * Each enum must override this to define the set of object it may be coerced to
-   */
-  public abstract Object toObject(Object object, PDataType actualType);
-
-  /*
-   * Each enum must override this to define the set of objects it may create
-   */
-  public abstract Object toObject(byte[] bytes, int offset, int length, PDataType actualType,
-      SortOrder sortOrder, Integer maxLength, Integer scale);
-
-  @Override
-  public T decode(PositionedByteRange pbr) {
-    // default implementation based on existing PDataType methods.
-    byte[] b = new byte[getByteSize()];
-    pbr.get(b);
-    return (T) toObject(b, 0, b.length, this, SortOrder.ASC, getMaxLength(null), getScale(null));
-  }
-
-  /*
-     * Return a valid object of this enum type
+        throw newIllegalDataException("Unsupported sql type: " + typeId);
+    }
+
+    public String getJavaClassName() {
+        return getJavaClass().getName();
+    }
+
+    public byte[] getJavaClassNameBytes() {
+        return clazzNameBytes;
+    }
+
+    public byte[] getSqlTypeNameBytes() {
+        return sqlTypeNameBytes;
+    }
+
+    /**
+     * By default returns sqlType for the PDataType, however it allows unknown types (our unsigned types) to return the
+     * regular corresponding sqlType so that tools like SQuirrel correctly display values of this type.
+     *
+     * @return integer representing the SQL type for display of a result set of this type
      */
-  public abstract Object getSampleValue(Integer maxLength, Integer arrayLength);
-
-  public final Object getSampleValue() {
-    return getSampleValue(null);
-  }
-
-  public final Object getSampleValue(Integer maxLength) {
-    return getSampleValue(maxLength, null);
-  }
-
-  public final Object toObject(byte[] bytes, int offset, int length, PDataType actualType,
-      SortOrder sortOrder) {
-    return toObject(bytes, offset, length, actualType, sortOrder, null, null);
-  }
-
-  public final Object toObject(byte[] bytes, int offset, int length, PDataType actualType) {
-    return toObject(bytes, offset, length, actualType, SortOrder.getDefault());
-  }
-
-  public final Object toObject(ImmutableBytesWritable ptr, PDataType actualType) {
-    return toObject(ptr, actualType, SortOrder.getDefault());
-  }
-
-  public final Object toObject(ImmutableBytesWritable ptr, PDataType actualType,
-      SortOrder sortOrder) {
-    return this.toObject(ptr.get(), ptr.getOffset(), ptr.getLength(), actualType, sortOrder);
-  }
-
-  public final Object toObject(ImmutableBytesWritable ptr, PDataType actualType,
-      SortOrder sortOrder, Integer maxLength, Integer scale) {
-    return this
-        .toObject(ptr.get(), ptr.getOffset(), ptr.getLength(), actualType, sortOrder, maxLength,
-            scale);
-  }
-
-  public final Object toObject(ImmutableBytesWritable ptr, SortOrder sortOrder, Integer maxLength,
-      Integer scale) {
-    return this
-        .toObject(ptr.get(), ptr.getOffset(), ptr.getLength(), this, sortOrder, maxLength, scale);
-  }
-
-  public final Object toObject(ImmutableBytesWritable ptr) {
-    return toObject(ptr.get(), ptr.getOffset(), ptr.getLength());
-  }
-
-  public final Object toObject(ImmutableBytesWritable ptr, SortOrder sortOrder) {
-    return toObject(ptr.get(), ptr.getOffset(), ptr.getLength(), this, sortOrder);
-  }
-
-  public final Object toObject(byte[] bytes, int offset, int length) {
-    return toObject(bytes, offset, length, this);
-  }
-
-  public final Object toObject(byte[] bytes) {
-    return toObject(bytes, SortOrder.getDefault());
-  }
-
-  public final Object toObject(byte[] bytes, SortOrder sortOrder) {
-    return toObject(bytes, 0, bytes.length, this, sortOrder);
-  }
-
-  public final Object toObject(byte[] bytes, SortOrder sortOrder, PDataType actualType) {
-    return toObject(bytes, 0, bytes.length, actualType, sortOrder);
-  }
-
-  public static PDataType fromSqlTypeName(String sqlTypeName) {
-    for (PDataType t : PDataTypeFactory.getInstance().getTypes()) {
-      if (t.getSqlTypeName().equals(sqlTypeName)) return t;
-    }
-    throw newIllegalDataException("Unsupported sql type: " + sqlTypeName);
-  }
-
-  public static int sqlArrayType(String sqlTypeName) {
-    PDataType fromSqlTypeName = fromSqlTypeName(sqlTypeName);
-    return fromSqlTypeName.getSqlType() + PDataType.ARRAY_TYPE_BASE;
-  }
-
-  protected static interface PhoenixArrayFactory {
-    PhoenixArray newArray(PDataType type, Object[] elements);
-  }
-
-  public static PDataType fromTypeId(int typeId) {
-    for (PDataType t : PDataTypeFactory.getInstance().getTypes()) {
-      if (t.getSqlType() == typeId) return t;
-    }
-    throw newIllegalDataException("Unsupported sql type: " + typeId);
-  }
-
-  public String getJavaClassName() {
-    return getJavaClass().getName();
-  }
-
-  public byte[] getJavaClassNameBytes() {
-    return clazzNameBytes;
-  }
-
-  public byte[] getSqlTypeNameBytes() {
-    return sqlTypeNameBytes;
-  }
-
-  /**
-   * By default returns sqlType for the PDataType,
-   * however it allows unknown types (our unsigned types)
-   * to return the regular corresponding sqlType so
-   * that tools like SQuirrel correctly display values
-   * of this type.
-   *
-   * @return integer representing the SQL type for display
-   * of a result set of this type
-   */
-  public int getResultSetSqlType() {
-    return this.sqlType;
-  }
-
-  public KeyRange getKeyRange(byte[] point) {
-    return getKeyRange(point, true, point, true);
-  }
-
-  public final String toStringLiteral(ImmutableBytesWritable ptr, Format formatter) {
-    return toStringLiteral(ptr.get(), ptr.getOffset(), ptr.getLength(), formatter);
-  }
-
-  public final String toStringLiteral(byte[] b, Format formatter) {
-    return toStringLiteral(b, 0, b.length, formatter);
-  }
-
-  public String toStringLiteral(byte[] b, int offset, int length, Format formatter) {
-    Object o = toObject(b, offset, length);
-    return toStringLiteral(o, formatter);
-  }
-  
+    public int getResultSetSqlType() {
+        return this.sqlType;
+    }
+
+    public KeyRange getKeyRange(byte[] point) {
+        return getKeyRange(point, true, point, true);
+    }
+
+    public final String toStringLiteral(ImmutableBytesWritable ptr, Format formatter) {
+        return toStringLiteral(ptr.get(), ptr.getOffset(), ptr.getLength(), formatter);
+    }
+
+    public final String toStringLiteral(byte[] b, Format formatter) {
+        return toStringLiteral(b, 0, b.length, formatter);
+    }
+
+    public String toStringLiteral(byte[] b, int offset, int length, Format formatter) {
+        Object o = toObject(b, offset, length);
+        return toStringLiteral(o, formatter);
+    }
+
     public String toStringLiteral(Object o, Format formatter) {
         if (formatter != null) {
             return formatter.format(o);
-        } else if (null == o) {
-            return String.valueOf(o);
-        }
+        } else if (null == o) { return String.valueOf(o); }
         return o.toString();
     }
 
@@ -1109,82 +1085,71 @@ public abstract class PDataType<T> implements DataType<T>, Comparable<PDataType<
         return toStringLiteral(o, null);
     }
 
-  private static final PhoenixArrayFactory DEFAULT_ARRAY_FACTORY = new PhoenixArrayFactory() {
-    @Override public PhoenixArray newArray(PDataType type, Object[] elements) {
-      return new PhoenixArray(type, elements);
-    }
-  };
+    private static final PhoenixArrayFactory DEFAULT_ARRAY_FACTORY = new PhoenixArrayFactory() {
+        @Override
+        public PhoenixArray newArray(PDataType type, Object[] elements) {
+            return new PhoenixArray(type, elements);
+        }
+    };
 
-  public PhoenixArrayFactory getArrayFactory() {
-    if (getCodec() != null)
-      return getCodec().getPhoenixArrayFactory();
-    else
-      return DEFAULT_ARRAY_FACTORY;
-  }
+    public PhoenixArrayFactory getArrayFactory() {
+        if (getCodec() != null)
+            return getCodec().getPhoenixArrayFactory();
+        else
+            return DEFAULT_ARRAY_FACTORY;
+    }
 
-  public static PhoenixArray instantiatePhoenixArray(PDataType actualType, Object[] elements) {
-    return actualType.getArrayFactory().newArray(actualType, elements);
-  }
+    public static PhoenixArray instantiatePhoenixArray(PDataType actualType, Object[] elements) {
+        return actualType.getArrayFactory().newArray(actualType, elements);
+    }
 
-  public KeyRange getKeyRange(byte[] lowerRange, boolean lowerInclusive, byte[] upperRange,
-      boolean upperInclusive) {
+    public KeyRange getKeyRange(byte[] lowerRange, boolean lowerInclusive, byte[] upperRange, boolean upperInclusive) {
         /*
-         * Force lower bound to be inclusive for fixed width keys because it makes
-         * comparisons less expensive when you can count on one bound or the other
-         * being inclusive. Comparing two fixed width exclusive bounds against each
-         * other is inherently more expensive, because you need to take into account
-         * if the bigger key is equal to the next key after the smaller key. For
-         * example:
-         *   (A-B] compared against [A-B)
-         * An exclusive lower bound A is bigger than an exclusive upper bound B.
-         * Forcing a fixed width exclusive lower bound key to be inclusive prevents
-         * us from having to do this extra logic in the compare function.
+         * Force lower bound to be inclusive for fixed width keys because it makes comparisons less expensive when you
+         * can count on one bound or the other being inclusive. Comparing two fixed width exclusive bounds against each
+         * other is inherently more expensive, because you need to take into account if the bigger key is equal to the
+         * next key after the smaller key. For example: (A-B] compared against [A-B) An exclusive lower bound A is
+         * bigger than an exclusive upper bound B. Forcing a fixed width exclusive lower bound key to be inclusive
+         * prevents us from having to do this extra logic in the compare function.
          */
-    if (lowerRange != KeyRange.UNBOUND && !lowerInclusive && isFixedWidth()) {
-      lowerRange = ByteUtil.nextKey(lowerRange);
-      lowerInclusive = true;
-    }
-    return KeyRange.getKeyRange(lowerRange, lowerInclusive, upperRange, upperInclusive);
-  }
-
-  public static PDataType fromLiteral(Object value) {
-    if (value == null) {
-      return null;
-    }
-    for (PDataType type : PDataType.values()) {
-      if (type.isArrayType()) {
-        PhoenixArray arr = (PhoenixArray) value;
-        if ((type.getSqlType() == arr.baseType.sqlType + PDataType.ARRAY_TYPE_BASE)
-            && type.getJavaClass().isInstance(value)) {
-          return type;
+        if (lowerRange != KeyRange.UNBOUND && !lowerInclusive && isFixedWidth()) {
+            lowerRange = ByteUtil.nextKey(lowerRange);
+            lowerInclusive = true;
         }
-      } else {
-        if (type.getJavaClass().isInstance(value)) {
-          return type;
+        return KeyRange.getKeyRange(lowerRange, lowerInclusive, upperRange, upperInclusive);
+    }
+
+    public static PDataType fromLiteral(Object value) {
+        if (value == null) { return null; }
+        for (PDataType type : PDataType.values()) {
+            if (type.isArrayType()) {
+                PhoenixArray arr = (PhoenixArray)value;
+                if ((type.getSqlType() == arr.baseType.sqlType + PDataType.ARRAY_TYPE_BASE)
+                        && type.getJavaClass().isInstance(value)) { return type; }
+            } else {
+                if (type.getJavaClass().isInstance(value)) { return type; }
+            }
         }
-      }
+        throw new UnsupportedOperationException("Unsupported literal value [" + value + "] of type "
+                + value.getClass().getName());
     }
-    throw new UnsupportedOperationException(
-        "Unsupported literal value [" + value + "] of type " + value.getClass().getName());
-  }
 
-  public int getNanos(ImmutableBytesWritable ptr, SortOrder sortOrder) {
-    throw new UnsupportedOperationException("Operation not supported for type " + this);
-  }
+    public int getNanos(ImmutableBytesWritable ptr, SortOrder sortOrder) {
+        throw new UnsupportedOperationException("Operation not supported for type " + this);
+    }
 
-  public long getMillis(ImmutableBytesWritable ptr, SortOrder sortOrder) {
-    throw new UnsupportedOperationException("Operation not supported for type " + this);
-  }
+    public long getMillis(ImmutableBytesWritable ptr, SortOrder sortOrder) {
+        throw new UnsupportedOperationException("Operation not supported for type " + this);
+    }
 
-  public Object pad(Object object, Integer maxLength) {
-    return object;
-  }
+    public Object pad(Object object, Integer maxLength) {
+        return object;
+    }
 
-  public void pad(ImmutableBytesWritable ptr, Integer maxLength, SortOrder sortOrder) {
-  }
+    public void pad(ImmutableBytesWritable ptr, Integer maxLength, SortOrder sortOrder) {}
 
-  public static PDataType arrayBaseType(PDataType arrayType) {
-    Preconditions.checkArgument(arrayType.isArrayType(), "Not a phoenix array type");
-    return fromTypeId(arrayType.getSqlType() - ARRAY_TYPE_BASE);
-  }
+    public static PDataType arrayBaseType(PDataType arrayType) {
+        Preconditions.checkArgument(arrayType.isArrayType(), "Not a phoenix array type");
+        return fromTypeId(arrayType.getSqlType() - ARRAY_TYPE_BASE);
+    }
 }


[08/50] [abbrv] phoenix git commit: PHOENIX-2025 Phoenix-core's hbase-default.xml prevents HBaseTestingUtility from starting up in client apps (Mujtaba Chohan)

Posted by ma...@apache.org.
PHOENIX-2025 Phoenix-core's hbase-default.xml prevents HBaseTestingUtility from starting up in client apps (Mujtaba Chohan)


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/dee7a02f
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/dee7a02f
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/dee7a02f

Branch: refs/heads/calcite
Commit: dee7a02f92ec3928a76c0d7536bd69f65fb1d4ef
Parents: d0c8f9d
Author: Thomas D'Silva <td...@salesforce.com>
Authored: Tue Jul 7 13:40:18 2015 -0700
Committer: Thomas D'Silva <td...@salesforce.com>
Committed: Tue Jul 7 13:40:18 2015 -0700

----------------------------------------------------------------------
 .../apache/phoenix/mapreduce/util/ConnectionUtil.java   |  9 +++++----
 .../mapreduce/util/PhoenixConfigurationUtil.java        | 12 ++++++++++++
 2 files changed, 17 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/dee7a02f/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/util/ConnectionUtil.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/util/ConnectionUtil.java b/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/util/ConnectionUtil.java
index 294d4e9..000ce59 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/util/ConnectionUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/util/ConnectionUtil.java
@@ -54,7 +54,7 @@ public class ConnectionUtil {
      */
     public static Connection getInputConnection(final Configuration conf , final Properties props) throws SQLException {
         Preconditions.checkNotNull(conf);
-        return getConnection(PhoenixConfigurationUtil.getInputCluster(conf),
+        return getConnection(PhoenixConfigurationUtil.getInputCluster(conf), PhoenixConfigurationUtil.getClientPort(conf),
                 PropertiesUtil.extractProperties(props, conf));
     }
 
@@ -77,7 +77,7 @@ public class ConnectionUtil {
      */
     public static Connection getOutputConnection(final Configuration conf, Properties props) throws SQLException {
         Preconditions.checkNotNull(conf);
-        return getConnection(PhoenixConfigurationUtil.getOutputCluster(conf),
+        return getConnection(PhoenixConfigurationUtil.getOutputCluster(conf), PhoenixConfigurationUtil.getClientPort(conf),
                 PropertiesUtil.extractProperties(props, conf));
     }
 
@@ -85,11 +85,12 @@ public class ConnectionUtil {
      * Returns the {@link Connection} from a ZooKeeper cluster string.
      *
      * @param quorum a ZooKeeper quorum connection string
+     * @param clientPort a ZooKeeper client port
      * @return a Phoenix connection to the given connection string
      */
-    private static Connection getConnection(final String quorum, Properties props) throws SQLException {
+    private static Connection getConnection(final String quorum, final int clientPort, Properties props) throws SQLException {
         Preconditions.checkNotNull(quorum);
-        return DriverManager.getConnection(QueryUtil.getUrl(quorum), props);
+        return DriverManager.getConnection(QueryUtil.getUrl(quorum, clientPort), props);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/dee7a02f/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/util/PhoenixConfigurationUtil.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/util/PhoenixConfigurationUtil.java b/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/util/PhoenixConfigurationUtil.java
index 6e0e5e4..bba96ac 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/util/PhoenixConfigurationUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/util/PhoenixConfigurationUtil.java
@@ -94,6 +94,8 @@ public final class PhoenixConfigurationUtil {
     public static final String MAPREDUCE_INPUT_CLUSTER_QUORUM = "phoenix.mapreduce.input.cluster.quorum";
     
     public static final String MAPREDUCE_OUTPUT_CLUSTER_QUORUM = "phoneix.mapreduce.output.cluster.quorum";
+    
+    public static final String HBASE_ZOOKEEPER_CLIENT_PORT = "hbase.zookeeper.property.clientPort";
 
     public enum SchemaType {
         TABLE,
@@ -363,6 +365,16 @@ public final class PhoenixConfigurationUtil {
         }
         return quorum;
     }
+    
+    /**
+     * Returns the HBase Client Port
+     * @param configuration
+     * @return
+     */
+    public static int getClientPort(final Configuration configuration) {
+        Preconditions.checkNotNull(configuration);
+        return Integer.parseInt(configuration.get(HBASE_ZOOKEEPER_CLIENT_PORT));
+    }
 
     public static void loadHBaseConfiguration(Job job) throws IOException {
         // load hbase-site.xml


[25/50] [abbrv] phoenix git commit: PHOENIX-2112 Phoenix-Spark need to support UTF8String for spark 1.4.0 (Yi Tian)

Posted by ma...@apache.org.
PHOENIX-2112 Phoenix-Spark need to support UTF8String for spark 1.4.0 (Yi Tian)


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/70c542d8
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/70c542d8
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/70c542d8

Branch: refs/heads/calcite
Commit: 70c542d8e7b8bdcea58e2ef8cbf76143ec5ae66c
Parents: 052836c
Author: Josh Mahonin <jm...@interset.com>
Authored: Mon Jul 13 16:15:42 2015 -0400
Committer: Josh Mahonin <jm...@interset.com>
Committed: Mon Jul 13 16:15:42 2015 -0400

----------------------------------------------------------------------
 phoenix-spark/pom.xml                                         | 2 +-
 .../main/scala/org/apache/phoenix/spark/PhoenixRelation.scala | 7 ++++---
 2 files changed, 5 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/70c542d8/phoenix-spark/pom.xml
----------------------------------------------------------------------
diff --git a/phoenix-spark/pom.xml b/phoenix-spark/pom.xml
index 289801a..ef4d2c4 100644
--- a/phoenix-spark/pom.xml
+++ b/phoenix-spark/pom.xml
@@ -34,7 +34,7 @@
   <name>Phoenix - Spark</name>
 
   <properties>
-    <spark.version>1.3.0</spark.version>
+    <spark.version>1.4.0</spark.version>
     <scala.version>2.10.4</scala.version>
     <scala.binary.version>2.10</scala.binary.version>
     <top.dir>${project.basedir}/..</top.dir>

http://git-wip-us.apache.org/repos/asf/phoenix/blob/70c542d8/phoenix-spark/src/main/scala/org/apache/phoenix/spark/PhoenixRelation.scala
----------------------------------------------------------------------
diff --git a/phoenix-spark/src/main/scala/org/apache/phoenix/spark/PhoenixRelation.scala b/phoenix-spark/src/main/scala/org/apache/phoenix/spark/PhoenixRelation.scala
index 8804f3b..f20ad61 100644
--- a/phoenix-spark/src/main/scala/org/apache/phoenix/spark/PhoenixRelation.scala
+++ b/phoenix-spark/src/main/scala/org/apache/phoenix/spark/PhoenixRelation.scala
@@ -19,10 +19,10 @@ package org.apache.phoenix.spark
 
 import org.apache.hadoop.conf.Configuration
 import org.apache.spark.rdd.RDD
-import org.apache.spark.sql.types.StructType
+import org.apache.spark.sql.types.{UTF8String, StructType}
 import org.apache.spark.sql.{Row, SQLContext}
 import org.apache.spark.sql.sources._
-import org.apache.commons.lang.StringEscapeUtils.escapeSql
+import org.apache.phoenix.util.StringUtil.escapeStringConstant
 
 case class PhoenixRelation(tableName: String, zkUrl: String)(@transient val sqlContext: SQLContext)
     extends BaseRelation with PrunedFilteredScan {
@@ -91,7 +91,8 @@ case class PhoenixRelation(tableName: String, zkUrl: String)(@transient val sqlC
 
   // Helper function to escape string values in SQL queries
   private def compileValue(value: Any): Any = value match {
-    case stringValue: String => s"'${escapeSql(stringValue)}'"
+    case stringValue: String => s"'${escapeStringConstant(stringValue)}'"
+    case stringValue: UTF8String => s"'${escapeStringConstant(stringValue.toString)}'"
     case _ => value
   }
 }


[23/50] [abbrv] phoenix git commit: PHOENIX-2098 Pig Udf that given a count Reserve chunks of numbers for a sequence (Siddhi Mehta)

Posted by ma...@apache.org.
PHOENIX-2098 Pig Udf that given a count Reserve chunks of numbers for a sequence (Siddhi Mehta)


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/300de02c
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/300de02c
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/300de02c

Branch: refs/heads/calcite
Commit: 300de02c01def7e8f23dfc1930b2d9d281c1bebd
Parents: 81b3cb3
Author: James Taylor <jt...@salesforce.com>
Authored: Fri Jul 10 16:32:56 2015 -0700
Committer: James Taylor <jt...@salesforce.com>
Committed: Fri Jul 10 16:32:56 2015 -0700

----------------------------------------------------------------------
 .../phoenix/pig/udf/ReserveNSequenceTestIT.java | 262 +++++++++++++++++++
 .../phoenix/pig/udf/ReserveNSequence.java       |  88 +++++++
 2 files changed, 350 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/300de02c/phoenix-pig/src/it/java/org/apache/phoenix/pig/udf/ReserveNSequenceTestIT.java
----------------------------------------------------------------------
diff --git a/phoenix-pig/src/it/java/org/apache/phoenix/pig/udf/ReserveNSequenceTestIT.java b/phoenix-pig/src/it/java/org/apache/phoenix/pig/udf/ReserveNSequenceTestIT.java
new file mode 100644
index 0000000..2cbb6cc
--- /dev/null
+++ b/phoenix-pig/src/it/java/org/apache/phoenix/pig/udf/ReserveNSequenceTestIT.java
@@ -0,0 +1,262 @@
+/**
+ * 
+ */
+package org.apache.phoenix.pig.udf;
+
+import static org.apache.phoenix.util.PhoenixRuntime.JDBC_PROTOCOL_SEPARATOR;
+import static org.apache.phoenix.util.TestUtil.LOCALHOST;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.phoenix.end2end.BaseHBaseManagedTimeIT;
+import org.apache.pig.data.Tuple;
+import org.apache.pig.data.TupleFactory;
+import org.apache.pig.impl.util.UDFContext;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+/**
+ * Test class to run all the Pig Sequence UDF integration tests against a virtual map reduce cluster.
+ */
+public class ReserveNSequenceTestIT extends BaseHBaseManagedTimeIT {
+
+    private static final String CREATE_SEQUENCE_SYNTAX = "CREATE SEQUENCE %s START WITH %s INCREMENT BY %s MINVALUE %s MAXVALUE %s CACHE %s";
+    private static final String SEQUENCE_NAME = "my_schema.my_sequence";
+    private static final long MAX_VALUE = 10;
+
+    private static TupleFactory TF;
+    private static Connection conn;
+    private static String zkQuorum;
+    private static Configuration conf;
+    private static UDFContext udfContext;
+
+    @Rule
+    public ExpectedException thrown = ExpectedException.none();
+
+    @BeforeClass
+    public static void setUpBeforeClass() throws Exception {
+        conf = getTestClusterConfig();
+        zkQuorum = LOCALHOST + JDBC_PROTOCOL_SEPARATOR + getZKClientPort(getTestClusterConfig());
+        conf.set(HConstants.ZOOKEEPER_QUORUM, zkQuorum);
+        // Properties props = PropertiesUtil.deepCopy(TestUtil.TEST_PROPERTIES);
+        conn = DriverManager.getConnection(getUrl());
+        // Pig variables
+        TF = TupleFactory.getInstance();
+    }
+
+    @Before
+    public void setUp() throws SQLException {
+        createSequence();
+        createUdfContext();
+    }
+
+    @Test
+    public void testReserve() throws Exception {
+        doTest(new UDFTestProperties(1));
+    }
+
+    @Test
+    public void testReserveN() throws Exception {
+        doTest(new UDFTestProperties(5));
+    }
+
+    @Test
+    public void testReserveNwithPreviousAllocations() throws Exception {
+        UDFTestProperties props = new UDFTestProperties(5);
+        props.setCurrentValue(4);
+        doTest(props);
+    }
+
+    @Test
+    public void testReserveWithZero() throws Exception {
+        UDFTestProperties props = new UDFTestProperties(0);
+        props.setExceptionExpected(true);
+        props.setExceptionClass(IllegalArgumentException.class);
+        props.setErrorMessage(ReserveNSequence.INVALID_NUMBER_MESSAGE);
+        doTest(props);
+    }
+
+    @Test
+    public void testReserveWithNegativeNumber() throws Exception {
+        UDFTestProperties props = new UDFTestProperties(-1);
+        props.setExceptionExpected(true);
+        props.setExceptionClass(IllegalArgumentException.class);
+        props.setErrorMessage(ReserveNSequence.INVALID_NUMBER_MESSAGE);
+        doTest(props);
+    }
+
+    @Test
+    public void testReserveMaxLimit() throws Exception {
+        UDFTestProperties props = new UDFTestProperties(MAX_VALUE);
+        props.setExceptionExpected(true);
+        props.setExceptionClass(IOException.class);
+        props.setErrorMessage("Reached MAXVALUE of sequence");
+        doTest(props);
+    }
+
+    @Test
+    public void testNoSequenceName() throws Exception {
+        UDFTestProperties props = new UDFTestProperties(1);
+        props.setExceptionExpected(true);
+        props.setSequenceName(null);
+        props.setExceptionClass(NullPointerException.class);
+        props.setErrorMessage(ReserveNSequence.EMPTY_SEQUENCE_NAME_MESSAGE);
+        doTest(props);
+    }
+
+    @Test
+    public void testSequenceNotExisting() throws Exception {
+        UDFTestProperties props = new UDFTestProperties(1);
+        props.setExceptionExpected(true);
+        props.setSequenceName("foo.bar");
+        props.setExceptionClass(IOException.class);
+        props.setErrorMessage("Sequence undefined");
+        doTest(props);
+    }
+
+    private void doTest(UDFTestProperties props) throws Exception {
+        setCurrentValue(props.getCurrentValue());
+        Tuple tuple = TF.newTuple(3);
+        tuple.set(0, props.getNumToReserve());
+        tuple.set(1, props.getSequenceName());
+        tuple.set(2, zkQuorum);
+        Long result = null;
+        try {
+            ReserveNSequence udf = new ReserveNSequence();
+            result = udf.exec(tuple);
+            validateReservedSequence(props.getCurrentValue(), props.getNumToReserve(), result);
+        } catch (Exception e) {
+            if (props.isExceptionExpected()) {
+                assertEquals(props.getExceptionClass(), e.getClass());
+                e.getMessage().contains(props.getErrorMessage());
+            } else {
+                throw e;
+            }
+        }
+
+    }
+
+    private void createUdfContext() {
+        conf.set(ReserveNSequence.SEQUENCE_NAME_CONF_KEY, SEQUENCE_NAME);
+        udfContext = UDFContext.getUDFContext();
+        udfContext.addJobConf(conf);
+    }
+
+    private void validateReservedSequence(Long currentValue, long count, Long result) throws SQLException {
+        Long startIndex = currentValue + 1;
+        assertEquals("Start index is incorrect", startIndex, result);
+        final long newNextSequenceValue = getNextSequenceValue();
+        assertEquals(startIndex + count, newNextSequenceValue);
+    }
+
+    private void createSequence() throws SQLException {
+        conn.createStatement().execute(String.format(CREATE_SEQUENCE_SYNTAX, SEQUENCE_NAME, 1, 1, 1, MAX_VALUE, 1));
+        conn.commit();
+    }
+
+    private void setCurrentValue(long currentValue) throws SQLException {
+        for (int i = 1; i <= currentValue; i++) {
+            getNextSequenceValue();
+        }
+    }
+
+    private long getNextSequenceValue() throws SQLException {
+        String ddl = new StringBuilder().append("SELECT NEXT VALUE FOR ").append(SEQUENCE_NAME).toString();
+        ResultSet rs = conn.createStatement().executeQuery(ddl);
+        assertTrue(rs.next());
+        conn.commit();
+        return rs.getLong(1);
+    }
+
+    private void dropSequence() throws Exception {
+        String ddl = new StringBuilder().append("DROP SEQUENCE ").append(SEQUENCE_NAME).toString();
+        conn.createStatement().execute(ddl);
+        conn.commit();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        udfContext.reset();
+        dropSequence();
+    }
+
+    @AfterClass
+    public static void tearDownAfterClass() throws Exception {
+        conn.close();
+    }
+
+    /**
+     * Static class to define properties for the test
+     */
+    private static class UDFTestProperties {
+        private final Long numToReserve;
+        private Long currentValue = 1L;
+        private String sequenceName = SEQUENCE_NAME;
+        private boolean exceptionExpected = false;
+        private Class exceptionClass = null;
+        private String errorMessage = null;
+
+        public UDFTestProperties(long numToReserve) {
+            this.numToReserve = numToReserve;
+        }
+
+        public Long getCurrentValue() {
+            return currentValue;
+        }
+
+        public void setCurrentValue(long currentValue) {
+            this.currentValue = currentValue;
+        }
+
+        public String getSequenceName() {
+            return sequenceName;
+        }
+
+        public void setSequenceName(String sequenceName) {
+            this.sequenceName = sequenceName;
+        }
+
+        public boolean isExceptionExpected() {
+            return exceptionExpected;
+        }
+
+        public void setExceptionExpected(boolean shouldThrowException) {
+            this.exceptionExpected = shouldThrowException;
+        }
+
+        public String getErrorMessage() {
+            return errorMessage;
+        }
+
+        public void setErrorMessage(String errorMessage) {
+            this.errorMessage = errorMessage;
+        }
+
+        public Long getNumToReserve() {
+            return numToReserve;
+        }
+
+        public Class getExceptionClass() {
+            return exceptionClass;
+        }
+
+        public void setExceptionClass(Class exceptionClass) {
+            this.exceptionClass = exceptionClass;
+        }
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/300de02c/phoenix-pig/src/main/java/org/apache/phoenix/pig/udf/ReserveNSequence.java
----------------------------------------------------------------------
diff --git a/phoenix-pig/src/main/java/org/apache/phoenix/pig/udf/ReserveNSequence.java b/phoenix-pig/src/main/java/org/apache/phoenix/pig/udf/ReserveNSequence.java
new file mode 100644
index 0000000..308f170
--- /dev/null
+++ b/phoenix-pig/src/main/java/org/apache/phoenix/pig/udf/ReserveNSequence.java
@@ -0,0 +1,88 @@
+/**
+ * 
+ */
+package org.apache.phoenix.pig.udf;
+
+/*
+ * 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.
+ */
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.phoenix.mapreduce.util.ConnectionUtil;
+import org.apache.pig.EvalFunc;
+import org.apache.pig.data.Tuple;
+import org.apache.pig.impl.util.UDFContext;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * UDF to Reserve a chunk of numbers for a given sequence
+ * 
+ * @note The way this UDF is invoked we open a new connection for every tuple row. The UDF will not perform well on
+ *       large datasets as it involves creating a new connection for every tuple row
+ */
+public class ReserveNSequence extends EvalFunc<Long> {
+
+    public static final String INVALID_TUPLE_MESSAGE = "Tuple should have correct fields(NumtoReserve,SequenceName,zkquorum.";
+    public static final String EMPTY_SEQUENCE_NAME_MESSAGE = "Sequence name should be not null";
+    public static final String EMPTY_ZK_MESSAGE = "ZKQuorum should be not null";
+    public static final String INVALID_NUMBER_MESSAGE = "Number of Sequences to Reserve should be greater than 0";
+    public static final String SEQUENCE_NAME_CONF_KEY = "phoenix.sequence.name";
+
+    /**
+     * Reserve N next sequences for a sequence name. N is the first field in the tuple. Sequence name is the second
+     * field in the tuple zkquorum is the third field in the tuple
+     */
+    @Override
+    public Long exec(Tuple input) throws IOException {
+        Preconditions.checkArgument(input != null && input.size() == 3, INVALID_TUPLE_MESSAGE);
+        Long numToReserve = (Long)(input.get(0));
+        Preconditions.checkArgument(numToReserve > 0, INVALID_NUMBER_MESSAGE);
+        String sequenceName = (String)input.get(1);
+        Preconditions.checkNotNull(sequenceName, EMPTY_SEQUENCE_NAME_MESSAGE);
+        String zkquorum = (String)input.get(2);
+        Preconditions.checkNotNull(zkquorum, EMPTY_ZK_MESSAGE);
+        UDFContext context = UDFContext.getUDFContext();
+        Configuration configuration = context.getJobConf();
+        configuration.set(HConstants.ZOOKEEPER_QUORUM, zkquorum);
+        Connection connection = null;
+        ResultSet rs = null;
+        try {
+            connection = ConnectionUtil.getOutputConnection(configuration);
+            String sql = getNextNSequenceSelectStatement(Long.valueOf(numToReserve), sequenceName);
+            rs = connection.createStatement().executeQuery(sql);
+            Preconditions.checkArgument(rs.next());
+            Long startIndex = rs.getLong(1);
+            rs.close();
+            connection.commit();
+            return startIndex;
+        } catch (SQLException e) {
+            throw new IOException("Caught exception while processing row." + e.getMessage(), e);
+        } finally {
+            try {
+                if (connection != null) {
+                    connection.close();
+                }
+            } catch (SQLException e) {
+                throw new IOException("Caught exception while closing connection", e);
+            }
+        }
+    }
+
+    private String getNextNSequenceSelectStatement(Long numToReserve, String sequenceName) {
+        return new StringBuilder().append("SELECT NEXT " + numToReserve + " VALUES" + " FOR ").append(sequenceName)
+                .toString();
+    }
+
+}


[49/50] [abbrv] phoenix git commit: Merge branch 'master' into calcite

Posted by ma...@apache.org.
Merge branch 'master' into calcite


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/597e0013
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/597e0013
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/597e0013

Branch: refs/heads/calcite
Commit: 597e0013cee4285704aecc5c36d6727262ca9885
Parents: 0641043 b38a624
Author: maryannxue <we...@intel.com>
Authored: Mon Jul 20 12:32:48 2015 -0400
Committer: maryannxue <we...@intel.com>
Committed: Mon Jul 20 12:32:48 2015 -0400

----------------------------------------------------------------------
 dev/eclipse_prefs_phoenix.epf                   |    2 +-
 .../apache/phoenix/end2end/AlterTableIT.java    |  449 +++-
 .../org/apache/phoenix/end2end/ArrayIT.java     |  178 ++
 .../end2end/ArrayToStringFunctionIT.java        |  654 ++++++
 .../org/apache/phoenix/end2end/IsNullIT.java    |   52 +-
 .../apache/phoenix/end2end/LpadFunctionIT.java  |   24 +
 .../apache/phoenix/end2end/MD5FunctionIT.java   |   19 +
 .../apache/phoenix/end2end/ReverseScanIT.java   |   59 +-
 .../phoenix/end2end/RowValueConstructorIT.java  |  103 +-
 .../end2end/SequenceBulkAllocationIT.java       | 1286 +++++++++++
 .../org/apache/phoenix/end2end/SequenceIT.java  |   27 +
 .../apache/phoenix/end2end/SortOrderFIT.java    |  563 -----
 .../org/apache/phoenix/end2end/SortOrderIT.java |  572 +++++
 .../phoenix/end2end/UserDefinedFunctionsIT.java |  212 +-
 .../java/org/apache/phoenix/end2end/ViewIT.java |   23 +
 .../iterate/RoundRobinResultIteratorIT.java     |   80 +
 phoenix-core/src/main/antlr3/PhoenixSQL.g       |   35 +-
 .../phoenix/compile/CreateFunctionCompiler.java |    4 +-
 .../apache/phoenix/compile/FromCompiler.java    |   21 +-
 .../apache/phoenix/compile/JoinCompiler.java    |    8 +-
 .../phoenix/compile/ListJarsQueryPlan.java      |  216 ++
 .../apache/phoenix/compile/OrderByCompiler.java |    4 +-
 .../phoenix/compile/OrderPreservingTracker.java |    7 +-
 .../phoenix/compile/ProjectionCompiler.java     |    2 +-
 .../apache/phoenix/compile/QueryCompiler.java   |    9 +-
 .../org/apache/phoenix/compile/ScanRanges.java  |    5 +-
 .../apache/phoenix/compile/SequenceManager.java |   70 +-
 .../compile/SequenceValueExpression.java        |   14 +-
 .../compile/TupleProjectionCompiler.java        |    5 +-
 .../apache/phoenix/compile/UnionCompiler.java   |    5 +-
 .../apache/phoenix/compile/UpsertCompiler.java  |   16 +-
 .../apache/phoenix/compile/WhereOptimizer.java  |   53 +-
 .../coprocessor/BaseScannerRegionObserver.java  |    2 +
 .../coprocessor/MetaDataEndpointImpl.java       |  457 +++-
 .../phoenix/coprocessor/MetaDataProtocol.java   |    2 +-
 .../coprocessor/SequenceRegionObserver.java     |   64 +-
 .../UngroupedAggregateRegionObserver.java       |  108 +-
 .../coprocessor/generated/MetaDataProtos.java   | 1243 ++++++++++-
 .../coprocessor/generated/PFunctionProtos.java  |  100 +-
 .../coprocessor/generated/PTableProtos.java     |  105 +-
 .../phoenix/exception/SQLExceptionCode.java     |    4 +
 .../apache/phoenix/execute/BaseQueryPlan.java   |   23 +-
 .../DescVarLengthFastByteComparisons.java       |  219 ++
 .../phoenix/execute/EmptyTableQueryPlan.java    |   96 +
 .../org/apache/phoenix/execute/ScanPlan.java    |   14 +-
 .../expression/ArrayConstructorExpression.java  |   21 +-
 .../phoenix/expression/ExpressionType.java      |    4 +-
 .../phoenix/expression/OrderByExpression.java   |   13 +-
 .../RowValueConstructorExpression.java          |    8 +-
 .../aggregator/ServerAggregators.java           |    1 +
 .../function/ArrayConcatFunction.java           |   11 +-
 .../function/ArrayModifierFunction.java         |    3 +-
 .../function/ArrayToStringFunction.java         |   84 +
 .../expression/function/LpadFunction.java       |    8 +-
 .../expression/util/regex/JONIPattern.java      |    5 +-
 .../apache/phoenix/filter/SkipScanFilter.java   |    3 +-
 .../apache/phoenix/index/IndexMaintainer.java   |  127 +-
 .../phoenix/iterate/BaseResultIterators.java    |  109 +-
 .../phoenix/iterate/OrderedResultIterator.java  |   52 +-
 .../iterate/RoundRobinResultIterator.java       |    2 +-
 .../apache/phoenix/jdbc/PhoenixConnection.java  |   29 +-
 .../apache/phoenix/jdbc/PhoenixStatement.java   |  213 +-
 .../phoenix/mapreduce/util/ConnectionUtil.java  |    9 +-
 .../util/PhoenixConfigurationUtil.java          |   14 +
 .../phoenix/monitoring/MetricsStopWatch.java    |    8 +-
 .../apache/phoenix/parse/AddJarsStatement.java  |   38 +
 .../org/apache/phoenix/parse/CastParseNode.java |    2 +-
 .../phoenix/parse/CreateFunctionStatement.java  |    7 +-
 .../phoenix/parse/DeleteJarStatement.java       |   19 +
 .../apache/phoenix/parse/ListJarsStatement.java |   34 +
 .../org/apache/phoenix/parse/PFunction.java     |   28 +-
 .../apache/phoenix/parse/ParseNodeFactory.java  |   22 +-
 .../apache/phoenix/parse/SelectStatement.java   |    6 +-
 .../phoenix/parse/SequenceValueParseNode.java   |    8 +-
 .../phoenix/query/ConnectionQueryServices.java  |    5 +-
 .../query/ConnectionQueryServicesImpl.java      |  128 +-
 .../query/ConnectionlessQueryServicesImpl.java  |   20 +-
 .../query/DelegateConnectionQueryServices.java  |   13 +-
 .../java/org/apache/phoenix/query/KeyRange.java |   27 +-
 .../apache/phoenix/query/QueryConstants.java    |    3 +
 .../phoenix/query/QueryServicesOptions.java     |    2 +-
 .../apache/phoenix/schema/DelegateTable.java    |    5 +
 .../apache/phoenix/schema/MetaDataClient.java   |   99 +-
 .../java/org/apache/phoenix/schema/PTable.java  |    9 +
 .../org/apache/phoenix/schema/PTableImpl.java   |   79 +-
 .../org/apache/phoenix/schema/RowKeySchema.java |   44 +-
 .../phoenix/schema/RowKeyValueAccessor.java     |   12 +-
 .../org/apache/phoenix/schema/Sequence.java     |  107 +-
 .../phoenix/schema/SequenceAllocation.java      |   48 +
 .../org/apache/phoenix/schema/TableRef.java     |    2 +
 .../org/apache/phoenix/schema/ValueSchema.java  |   30 +-
 .../phoenix/schema/stats/StatisticsUtil.java    |    4 +-
 .../phoenix/schema/types/PArrayDataType.java    |  726 ++++---
 .../phoenix/schema/types/PBinaryArray.java      |  122 +-
 .../phoenix/schema/types/PBooleanArray.java     |  112 +-
 .../apache/phoenix/schema/types/PCharArray.java |  128 +-
 .../apache/phoenix/schema/types/PDataType.java  | 2037 +++++++++---------
 .../apache/phoenix/schema/types/PDateArray.java |  131 +-
 .../phoenix/schema/types/PDecimalArray.java     |  126 +-
 .../phoenix/schema/types/PDoubleArray.java      |  128 +-
 .../phoenix/schema/types/PFloatArray.java       |  130 +-
 .../phoenix/schema/types/PIntegerArray.java     |  130 +-
 .../apache/phoenix/schema/types/PLongArray.java |  130 +-
 .../phoenix/schema/types/PSmallintArray.java    |  130 +-
 .../apache/phoenix/schema/types/PTimeArray.java |  133 +-
 .../phoenix/schema/types/PTimestampArray.java   |  132 +-
 .../phoenix/schema/types/PTinyintArray.java     |  130 +-
 .../schema/types/PUnsignedDateArray.java        |  128 +-
 .../schema/types/PUnsignedDoubleArray.java      |  136 +-
 .../schema/types/PUnsignedFloatArray.java       |  130 +-
 .../phoenix/schema/types/PUnsignedIntArray.java |  130 +-
 .../schema/types/PUnsignedLongArray.java        |  130 +-
 .../schema/types/PUnsignedSmallintArray.java    |  132 +-
 .../schema/types/PUnsignedTimeArray.java        |  132 +-
 .../schema/types/PUnsignedTimestampArray.java   |  134 +-
 .../schema/types/PUnsignedTinyintArray.java     |  132 +-
 .../phoenix/schema/types/PVarbinaryArray.java   |  130 +-
 .../phoenix/schema/types/PVarcharArray.java     |  130 +-
 .../java/org/apache/phoenix/util/ByteUtil.java  |   12 +-
 .../org/apache/phoenix/util/MetaDataUtil.java   |   16 +-
 .../org/apache/phoenix/util/PhoenixRuntime.java |  153 +-
 .../apache/phoenix/util/PhoenixStopWatch.java   |   81 +
 .../java/org/apache/phoenix/util/QueryUtil.java |    4 +-
 .../java/org/apache/phoenix/util/ScanUtil.java  |   24 +-
 .../org/apache/phoenix/util/SchemaUtil.java     |   41 +-
 .../org/apache/phoenix/util/SequenceUtil.java   |   47 +-
 .../java/org/apache/phoenix/util/TupleUtil.java |    7 +-
 .../org/apache/phoenix/util/UpgradeUtil.java    |  730 +++++--
 .../phoenix/compile/QueryCompilerTest.java      |  156 +-
 .../expression/ArrayAppendFunctionTest.java     |   76 +-
 .../expression/ArrayConcatFunctionTest.java     |  129 ++
 .../expression/ArrayPrependFunctionTest.java    |   61 +
 .../expression/ArrayToStringFunctionTest.java   |  376 ++++
 .../util/PhoenixConfigurationUtilTest.java      |   28 +-
 .../apache/phoenix/parse/CastParseNodeTest.java |   57 +
 .../apache/phoenix/parse/QueryParserTest.java   |   19 +-
 .../org/apache/phoenix/query/OrderByTest.java   |   84 +
 .../phoenix/schema/SequenceAllocationTest.java  |   59 +
 .../schema/types/PDataTypeForArraysTest.java    |   70 +-
 .../phoenix/schema/types/PDataTypeTest.java     |   12 +
 .../org/apache/phoenix/util/QueryUtilTest.java  |   36 +-
 .../apache/phoenix/util/SequenceUtilTest.java   |   54 +
 phoenix-pherf/config/log4j.properties           |   58 +
 phoenix-pherf/pom.xml                           |    2 +-
 .../org/apache/phoenix/pherf/DataIngestIT.java  |    8 +
 .../test/resources/datamodel/test_schema.sql    |    2 +-
 .../phoenix/pig/PhoenixHBaseLoaderIT.java       |  141 +-
 .../phoenix/pig/PhoenixHBaseStorerIT.java       |    4 +-
 .../phoenix/pig/udf/ReserveNSequenceTestIT.java |  262 +++
 .../apache/phoenix/pig/PhoenixHBaseStorage.java |   51 +-
 .../phoenix/pig/udf/ReserveNSequence.java       |   88 +
 phoenix-protocol/src/main/MetaDataService.proto |   14 +-
 phoenix-protocol/src/main/PFunction.proto       |    1 +
 phoenix-protocol/src/main/PTable.proto          |    2 +
 phoenix-spark/pom.xml                           |    2 +-
 phoenix-spark/src/it/resources/setup.sql        |    4 +-
 .../apache/phoenix/spark/PhoenixSparkIT.scala   |   58 +-
 .../org/apache/phoenix/spark/PhoenixRDD.scala   |   24 +-
 .../apache/phoenix/spark/PhoenixRelation.scala  |    7 +-
 159 files changed, 12789 insertions(+), 4879 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/597e0013/phoenix-core/src/main/java/org/apache/phoenix/compile/OrderByCompiler.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/phoenix/blob/597e0013/phoenix-core/src/main/java/org/apache/phoenix/execute/ScanPlan.java
----------------------------------------------------------------------


[32/50] [abbrv] phoenix git commit: PHOENIX-2067 Sort order incorrect for variable length DESC columns

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PArrayDataType.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PArrayDataType.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PArrayDataType.java
index 4e32cc0..dd11569 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PArrayDataType.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PArrayDataType.java
@@ -20,6 +20,7 @@ package org.apache.phoenix.schema.types;
 import java.io.DataOutputStream;
 import java.io.IOException;
 import java.nio.ByteBuffer;
+import java.sql.Types;
 import java.text.Format;
 import java.util.LinkedList;
 import java.util.List;
@@ -34,61 +35,88 @@ import org.apache.phoenix.schema.SortOrder;
 import org.apache.phoenix.schema.ValueSchema;
 import org.apache.phoenix.schema.tuple.Tuple;
 import org.apache.phoenix.util.ByteUtil;
+import org.apache.phoenix.util.SchemaUtil;
 import org.apache.phoenix.util.TrustedByteArrayOutputStream;
 
 import com.google.common.base.Objects;
 import com.google.common.base.Preconditions;
 
 /**
- * The datatype for PColummns that are Arrays. Any variable length array would follow the below order. 
- * Every element would be seperated by a seperator byte '0'. Null elements are counted and once a first 
- * non null element appears we write the count of the nulls prefixed with a seperator byte.
- * Trailing nulls are not taken into account. The last non null element is followed by two seperator bytes. 
- * For eg a, b, null, null, c, null -> 65 0 66 0 0 2 67 0 0 0 
- * a null null null b c null d -> 65 0 0 3 66 0 67 0 0 1 68 0 0 0.
- * The reason we use this serialization format is to allow the
- * byte array of arrays of the same type to be directly comparable against each other. 
- * This prevents a costly deserialization on compare and allows an array column to be used as the last column in a primary key constraint.
+ * The datatype for PColummns that are Arrays. Any variable length array would follow the below order. Every element
+ * would be seperated by a seperator byte '0'. Null elements are counted and once a first non null element appears we
+ * write the count of the nulls prefixed with a seperator byte. Trailing nulls are not taken into account. The last non
+ * null element is followed by two seperator bytes. For eg a, b, null, null, c, null -> 65 0 66 0 0 2 67 0 0 0 a null
+ * null null b c null d -> 65 0 0 3 66 0 67 0 0 1 68 0 0 0. The reason we use this serialization format is to allow the
+ * byte array of arrays of the same type to be directly comparable against each other. This prevents a costly
+ * deserialization on compare and allows an array column to be used as the last column in a primary key constraint.
  */
 public abstract class PArrayDataType<T> extends PDataType<T> {
 
+    @Override
+    public final int getResultSetSqlType() {
+      return Types.ARRAY;
+    }
+
+    @Override
+    public final void coerceBytes(ImmutableBytesWritable ptr, Object object, PDataType actualType,
+        Integer maxLength, Integer scale, SortOrder actualModifer, Integer desiredMaxLength,
+        Integer desiredScale, SortOrder desiredModifier, boolean expectedRowKeyOrderOptimizable) {
+      coerceBytes(ptr, object, actualType, maxLength, scale, desiredMaxLength, desiredScale,
+          this, actualModifer, desiredModifier, expectedRowKeyOrderOptimizable);
+    }
+
+    @Override
+    public final void coerceBytes(ImmutableBytesWritable ptr, Object object, PDataType actualType,
+        Integer maxLength, Integer scale, SortOrder actualModifer, Integer desiredMaxLength,
+        Integer desiredScale, SortOrder desiredModifier) {
+      coerceBytes(ptr, object, actualType, maxLength, scale, desiredMaxLength, desiredScale,
+          this, actualModifer, desiredModifier, true);
+    }
+
     public static final byte ARRAY_SERIALIZATION_VERSION = 1;
 
-  protected PArrayDataType(String sqlTypeName, int sqlType, Class clazz, PDataCodec codec, int ordinal) {
-    super(sqlTypeName, sqlType, clazz, codec, ordinal);
-  }
+    protected PArrayDataType(String sqlTypeName, int sqlType, Class clazz, PDataCodec codec, int ordinal) {
+        super(sqlTypeName, sqlType, clazz, codec, ordinal);
+    }
+
+    private static byte getSeparatorByte(boolean rowKeyOrderOptimizable, SortOrder sortOrder) {
+        return SchemaUtil.getSeparatorByte(rowKeyOrderOptimizable, false, sortOrder);
+    }
 
-  public byte[] toBytes(Object object, PDataType baseType, SortOrder sortOrder) {
-		if(object == null) {
-			throw new ConstraintViolationException(this + " may not be null");
-		}
-		PhoenixArray arr = ((PhoenixArray)object);
+    public byte[] toBytes(Object object, PDataType baseType, SortOrder sortOrder) {
+        return toBytes(object, baseType, sortOrder, true);
+    }
+    
+    public byte[] toBytes(Object object, PDataType baseType, SortOrder sortOrder, boolean rowKeyOrderOptimizable) {
+        if (object == null) { throw new ConstraintViolationException(this + " may not be null"); }
+        PhoenixArray arr = ((PhoenixArray)object);
         int noOfElements = arr.numElements;
-        if(noOfElements == 0) {
-        	return ByteUtil.EMPTY_BYTE_ARRAY;
-        }
+        if (noOfElements == 0) { return ByteUtil.EMPTY_BYTE_ARRAY; }
         TrustedByteArrayOutputStream byteStream = null;
-		if (!baseType.isFixedWidth()) {
-	        Pair<Integer, Integer> nullsVsNullRepeationCounter = new Pair<>();
-	        int size = estimateByteSize(object, nullsVsNullRepeationCounter,
-	                PDataType.fromTypeId((baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE)));
-		    size += ((2 * Bytes.SIZEOF_BYTE) + (noOfElements - nullsVsNullRepeationCounter.getFirst()) * Bytes.SIZEOF_BYTE)
-		                                + (nullsVsNullRepeationCounter.getSecond() * 2 * Bytes.SIZEOF_BYTE);
-		    // Assume an offset array that fit into Short.MAX_VALUE.  Also not considering nulls that could be > 255
-		    // In both of these cases, finally an array copy would happen
-		    int capacity = noOfElements * Bytes.SIZEOF_SHORT;
-		    // Here the int for noofelements, byte for the version, int for the offsetarray position and 2 bytes for the end seperator
-            byteStream = new TrustedByteArrayOutputStream(size + capacity + Bytes.SIZEOF_INT + Bytes.SIZEOF_BYTE +  Bytes.SIZEOF_INT);
-		} else {
-		    int size = arr.getMaxLength() * noOfElements;
-		    // Here the int for noofelements, byte for the version
-		    byteStream = new TrustedByteArrayOutputStream(size);
-		}
-		DataOutputStream oStream = new DataOutputStream(byteStream);
-		// Handles bit inversion also
-		return createArrayBytes(byteStream, oStream, (PhoenixArray)object, noOfElements, baseType, sortOrder);
-	}
-	
+        if (!baseType.isFixedWidth()) {
+            Pair<Integer, Integer> nullsVsNullRepeationCounter = new Pair<>();
+            int size = estimateByteSize(object, nullsVsNullRepeationCounter,
+                    PDataType.fromTypeId((baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE)));
+            size += ((2 * Bytes.SIZEOF_BYTE) + (noOfElements - nullsVsNullRepeationCounter.getFirst())
+                    * Bytes.SIZEOF_BYTE)
+                    + (nullsVsNullRepeationCounter.getSecond() * 2 * Bytes.SIZEOF_BYTE);
+            // Assume an offset array that fit into Short.MAX_VALUE. Also not considering nulls that could be > 255
+            // In both of these cases, finally an array copy would happen
+            int capacity = noOfElements * Bytes.SIZEOF_SHORT;
+            // Here the int for noofelements, byte for the version, int for the offsetarray position and 2 bytes for the
+            // end seperator
+            byteStream = new TrustedByteArrayOutputStream(size + capacity + Bytes.SIZEOF_INT + Bytes.SIZEOF_BYTE
+                    + Bytes.SIZEOF_INT);
+        } else {
+            int size = arr.getMaxLength() * noOfElements;
+            // Here the int for noofelements, byte for the version
+            byteStream = new TrustedByteArrayOutputStream(size);
+        }
+        DataOutputStream oStream = new DataOutputStream(byteStream);
+        // Handles bit inversion also
+        return createArrayBytes(byteStream, oStream, (PhoenixArray)object, noOfElements, baseType, sortOrder, rowKeyOrderOptimizable);
+    }
+
     public static int serializeNulls(DataOutputStream oStream, int nulls) throws IOException {
         // We need to handle 3 different cases here
         // 1) Arrays with repeating nulls in the middle which is less than 255
@@ -97,12 +125,15 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
         // Take a case where we have two arrays that has the following elements
         // Array 1 - size : 240, elements = abc, bcd, null, null, bcd,null,null......,null, abc
         // Array 2 - size : 16 : elements = abc, bcd, null, null, bcd, null, null...null, abc
-        // In both case the elements and the value array will be the same but the Array 1 is actually smaller because it has more nulls.
-        // Now we should have mechanism to show that we treat arrays with more nulls as lesser.  Hence in the above case as 
+        // In both case the elements and the value array will be the same but the Array 1 is actually smaller because it
+        // has more nulls.
+        // Now we should have mechanism to show that we treat arrays with more nulls as lesser. Hence in the above case
+        // as
         // 240 > Bytes.MAX_VALUE, by always inverting the number of nulls we would get a +ve value
-        // For Array 2, by inverting we would get a -ve value.  On comparison Array 2 > Array 1.
-        // Now for cases where the number of nulls is greater than 255, we would write an those many (byte)1, it is bigger than 255.
-        // This would ensure that we don't compare with triple zero which is used as an end  byte
+        // For Array 2, by inverting we would get a -ve value. On comparison Array 2 > Array 1.
+        // Now for cases where the number of nulls is greater than 255, we would write an those many (byte)1, it is
+        // bigger than 255.
+        // This would ensure that we don't compare with triple zero which is used as an end byte
         if (nulls > 0) {
             oStream.write(QueryConstants.SEPARATOR_BYTE);
             int nMultiplesOver255 = nulls / 255;
@@ -110,61 +141,65 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
                 // Don't write a zero byte, as we need to ensure that the only triple zero
                 // byte occurs at the end of the array (i.e. the terminator byte for the
                 // element plus the double zero byte at the end of the array).
-                oStream.write((byte)1); 
+                oStream.write((byte)1);
             }
             int nRemainingNulls = nulls % 255; // From 0 to 254
             // Write a byte for the remaining null elements
             if (nRemainingNulls > 0) {
                 // Remaining null elements is from 1 to 254.
-                // Subtract one and invert so that more remaining nulls becomes smaller than less 
-                // remaining nulls and min byte value is always greater than 1, the repeating value  
+                // Subtract one and invert so that more remaining nulls becomes smaller than less
+                // remaining nulls and min byte value is always greater than 1, the repeating value
                 // used for arrays with more than 255 repeating null elements.
-                // The reason we invert is that  an array with less null elements has a non
+                // The reason we invert is that an array with less null elements has a non
                 // null element sooner than an array with more null elements. Thus, the more
                 // null elements you have, the smaller the array becomes.
-                byte nNullByte = SortOrder.invert((byte)(nRemainingNulls-1));
+                byte nNullByte = SortOrder.invert((byte)(nRemainingNulls - 1));
                 oStream.write(nNullByte); // Single byte for repeating nulls
             }
         }
         return 0;
     }
 
-    public static int serializeNulls(byte[] bytes, int position, int nulls){
+    public static int serializeNulls(byte[] bytes, int position, int nulls) {
         int nMultiplesOver255 = nulls / 255;
         while (nMultiplesOver255-- > 0) {
             bytes[position++] = 1;
         }
         int nRemainingNulls = nulls % 255;
         if (nRemainingNulls > 0) {
-            byte nNullByte = SortOrder.invert((byte)(nRemainingNulls-1));
+            byte nNullByte = SortOrder.invert((byte)(nRemainingNulls - 1));
             bytes[position++] = nNullByte;
         }
         return position;
     }
- 
-    public static void writeEndSeperatorForVarLengthArray(DataOutputStream oStream) throws IOException {
-        oStream.write(QueryConstants.SEPARATOR_BYTE);
-        oStream.write(QueryConstants.SEPARATOR_BYTE);
+
+    public static void writeEndSeperatorForVarLengthArray(DataOutputStream oStream, SortOrder sortOrder) throws IOException {
+        writeEndSeperatorForVarLengthArray(oStream, sortOrder, true);
+    }
+    
+    private static void writeEndSeperatorForVarLengthArray(DataOutputStream oStream, SortOrder sortOrder, boolean rowKeyOrderOptimizable)
+            throws IOException {
+        byte sepByte = getSeparatorByte(rowKeyOrderOptimizable, sortOrder);
+        oStream.write(sepByte);
+        oStream.write(sepByte);
     }
 
-	public static boolean useShortForOffsetArray(int maxOffset) {
-		// If the max offset is less than Short.MAX_VALUE then offset array can use short
-		if (maxOffset <= (2 * Short.MAX_VALUE)) {
-			return true;
-		}
-		// else offset array can use Int
-		return false;
-	}
-
-	public int toBytes(Object object, byte[] bytes, int offset) {
-	    PhoenixArray array = (PhoenixArray)object;
-        if (array == null || array.baseType == null) {
-            return 0;
-        }
-        return estimateByteSize(object, null, PDataType.fromTypeId((array.baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE)));
-	}
+    public static boolean useShortForOffsetArray(int maxOffset) {
+        // If the max offset is less than Short.MAX_VALUE then offset array can use short
+        if (maxOffset <= (2 * Short.MAX_VALUE)) { return true; }
+        // else offset array can use Int
+        return false;
+    }
 
-	// Estimates the size of the given array and also calculates the number of nulls and its repetition factor
+    @Override
+    public int toBytes(Object object, byte[] bytes, int offset) {
+        PhoenixArray array = (PhoenixArray)object;
+        if (array == null || array.baseType == null) { return 0; }
+        return estimateByteSize(object, null,
+                PDataType.fromTypeId((array.baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE)));
+    }
+
+    // Estimates the size of the given array and also calculates the number of nulls and its repetition factor
     public int estimateByteSize(Object o, Pair<Integer, Integer> nullsVsNullRepeationCounter, PDataType baseType) {
         if (baseType.isFixedWidth()) { return baseType.getByteSize(); }
         if (baseType.isArrayType()) {
@@ -201,43 +236,41 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
         // Non fixed width types must override this
         throw new UnsupportedOperationException();
     }
-    
-	public boolean isCoercibleTo(PDataType targetType, Object value) {
-	    return targetType.isCoercibleTo(targetType, value);
-	}
-	
-	public boolean isCoercibleTo(PDataType targetType, PDataType expectedTargetType) {
-		if(!targetType.isArrayType()) {
-			return false;
-		} else {
-			PDataType targetElementType = PDataType.fromTypeId(targetType.getSqlType()
-					- PDataType.ARRAY_TYPE_BASE);
-			PDataType expectedTargetElementType = PDataType.fromTypeId(expectedTargetType
-					.getSqlType() - PDataType.ARRAY_TYPE_BASE);
-			return expectedTargetElementType.isCoercibleTo(targetElementType);
-		}
+
+    @Override
+    public boolean isCoercibleTo(PDataType targetType, Object value) {
+        return targetType.isCoercibleTo(targetType, value);
     }
 
-  @Override
-	public boolean isSizeCompatible(ImmutableBytesWritable ptr, Object value,
-      PDataType srcType, Integer maxLength, Integer scale,
-      Integer desiredMaxLength, Integer desiredScale) {
-    if (value == null) return true;
-		PhoenixArray pArr = (PhoenixArray) value;
-    PDataType baseType = PDataType.fromTypeId(srcType.getSqlType() - PDataType.ARRAY_TYPE_BASE);
-    for (int i = 0 ; i < pArr.numElements; i++) {
-      Object val = pArr.getElement(i);
-      if (!baseType.isSizeCompatible(ptr, val, baseType, srcType.getMaxLength(val),
-          scale, desiredMaxLength, desiredScale)) {
-        return false;
-      }
-		}
-		return true;
-	}
-
-    public void coerceBytes(ImmutableBytesWritable ptr, Object value, PDataType actualType, Integer maxLength,
-        Integer scale, Integer desiredMaxLength, Integer desiredScale, PDataType desiredType,
-            SortOrder actualModifer, SortOrder expectedModifier) {
+    public boolean isCoercibleTo(PDataType targetType, PDataType expectedTargetType) {
+        if (!targetType.isArrayType()) {
+            return false;
+        } else {
+            PDataType targetElementType = PDataType.fromTypeId(targetType.getSqlType() - PDataType.ARRAY_TYPE_BASE);
+            PDataType expectedTargetElementType = PDataType.fromTypeId(expectedTargetType.getSqlType()
+                    - PDataType.ARRAY_TYPE_BASE);
+            return expectedTargetElementType.isCoercibleTo(targetElementType);
+        }
+    }
+
+    @Override
+    public boolean isSizeCompatible(ImmutableBytesWritable ptr, Object value, PDataType srcType, Integer maxLength,
+            Integer scale, Integer desiredMaxLength, Integer desiredScale) {
+        if (value == null) return true;
+        PhoenixArray pArr = (PhoenixArray)value;
+        PDataType baseType = PDataType.fromTypeId(srcType.getSqlType() - PDataType.ARRAY_TYPE_BASE);
+        for (int i = 0; i < pArr.numElements; i++) {
+            Object val = pArr.getElement(i);
+            if (!baseType.isSizeCompatible(ptr, val, baseType, srcType.getMaxLength(val), scale, desiredMaxLength,
+                    desiredScale)) { return false; }
+        }
+        return true;
+    }
+
+    private void coerceBytes(ImmutableBytesWritable ptr, Object value, PDataType actualType, Integer maxLength,
+            Integer scale, Integer desiredMaxLength, Integer desiredScale, PDataType desiredType,
+            SortOrder actualSortOrder, SortOrder desiredSortOrder, 
+            boolean expectedRowKeyOrderOptimizable) {
         if (ptr.getLength() == 0) { // a zero length ptr means null which will not be coerced to anything different
             return;
         }
@@ -245,42 +278,62 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
         PDataType desiredBaseType = PDataType.fromTypeId(desiredType.getSqlType() - PDataType.ARRAY_TYPE_BASE);
         if ((Objects.equal(maxLength, desiredMaxLength) || maxLength == null || desiredMaxLength == null)
                 && actualType.isBytesComparableWith(desiredType)
-                && baseType.isFixedWidth() == desiredBaseType.isFixedWidth() && actualModifer == expectedModifier) { 
+                && baseType.isFixedWidth() == desiredBaseType.isFixedWidth() 
+                && actualSortOrder == desiredSortOrder
+                && (desiredSortOrder == SortOrder.ASC || desiredBaseType.isFixedWidth() || isRowKeyOrderOptimized(actualType, actualSortOrder, ptr) == expectedRowKeyOrderOptimizable)) {
             return; 
         }
+        PhoenixArray pArr;
         if (value == null || actualType != desiredType) {
-            value = toObject(ptr.get(), ptr.getOffset(), ptr.getLength(), baseType, actualModifer, maxLength,
+            value = toObject(ptr.get(), ptr.getOffset(), ptr.getLength(), baseType, actualSortOrder, maxLength,
                     desiredScale, desiredBaseType);
-            PhoenixArray pArr = (PhoenixArray)value;
+            pArr = (PhoenixArray)value;
             // VARCHAR <=> CHAR
-            if(baseType.isFixedWidth() != desiredBaseType.isFixedWidth()) {
+            if (baseType.isFixedWidth() != desiredBaseType.isFixedWidth()) {
                 if (!pArr.isPrimitiveType()) {
                     pArr = new PhoenixArray(pArr, desiredMaxLength);
                 }
             }
-            //Coerce to new max length when only max lengths differ
-            if(actualType == desiredType && !pArr.isPrimitiveType() && maxLength != null && maxLength != desiredMaxLength){
-               pArr = new PhoenixArray(pArr, desiredMaxLength);
+            // Coerce to new max length when only max lengths differ
+            if (actualType == desiredType && !pArr.isPrimitiveType() && maxLength != null
+                    && maxLength != desiredMaxLength) {
+                pArr = new PhoenixArray(pArr, desiredMaxLength);
             }
             baseType = desiredBaseType;
-            ptr.set(toBytes(pArr, baseType, expectedModifier));
         } else {
-            PhoenixArray pArr = (PhoenixArray)value;
+            pArr = (PhoenixArray)value;
             pArr = new PhoenixArray(pArr, desiredMaxLength);
-            ptr.set(toBytes(pArr, baseType, expectedModifier));
         }
+        ptr.set(toBytes(pArr, baseType, desiredSortOrder, expectedRowKeyOrderOptimizable));
     }
 
+    public static boolean isRowKeyOrderOptimized(PDataType type, SortOrder sortOrder, ImmutableBytesWritable ptr) {
+        return isRowKeyOrderOptimized(type, sortOrder, ptr.get(), ptr.getOffset(), ptr.getLength());
+    }
+    
+    public static boolean isRowKeyOrderOptimized(PDataType type, SortOrder sortOrder, byte[] buf, int offset, int length) {
+        PDataType baseType = PDataType.fromTypeId(type.getSqlType() - PDataType.ARRAY_TYPE_BASE);
+        return isRowKeyOrderOptimized(baseType.isFixedWidth(), sortOrder, buf, offset, length);
+    }
+    
+    private static boolean isRowKeyOrderOptimized(boolean isFixedWidth, SortOrder sortOrder, byte[] buf, int offset, int length) {
+        if (length == 0 || sortOrder == SortOrder.ASC || isFixedWidth) {
+            return true;
+        }
+        int offsetToHeaderOffset = offset + length - Bytes.SIZEOF_BYTE - Bytes.SIZEOF_INT * 2;
+        int offsetToSeparatorByte = Bytes.readAsInt(buf, offsetToHeaderOffset, Bytes.SIZEOF_INT) - 1;
+        return buf[offsetToSeparatorByte] == QueryConstants.DESC_SEPARATOR_BYTE;
+    }
 
+    @Override
     public Object toObject(String value) {
-		throw new IllegalArgumentException("This operation is not suppported");
-	}
+        throw new IllegalArgumentException("This operation is not suppported");
+    }
 
-	public Object toObject(byte[] bytes, int offset, int length, PDataType baseType, 
-			SortOrder sortOrder, Integer maxLength, Integer scale, PDataType desiredDataType) {
-		return createPhoenixArray(bytes, offset, length, sortOrder,
-				baseType, maxLength, desiredDataType);
-	}
+    public Object toObject(byte[] bytes, int offset, int length, PDataType baseType, SortOrder sortOrder,
+            Integer maxLength, Integer scale, PDataType desiredDataType) {
+        return createPhoenixArray(bytes, offset, length, sortOrder, baseType, maxLength, desiredDataType);
+    }
 
     public static boolean positionAtArrayElement(Tuple tuple, ImmutableBytesWritable ptr, int index,
             Expression arrayExpr, PDataType pDataType, Integer maxLen) {
@@ -293,13 +346,14 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
         positionAtArrayElement(ptr, index - 1, pDataType, maxLen);
         return true;
     }
+
     public static void positionAtArrayElement(ImmutableBytesWritable ptr, int arrayIndex, PDataType baseDataType,
-        Integer byteSize) {
+            Integer byteSize) {
         byte[] bytes = ptr.get();
         int initPos = ptr.getOffset();
         if (!baseDataType.isFixedWidth()) {
-            int noOfElements = Bytes.toInt(bytes, (ptr.getOffset() + ptr.getLength() - (Bytes.SIZEOF_BYTE + Bytes.SIZEOF_INT)),
-                    Bytes.SIZEOF_INT);
+            int noOfElements = Bytes.toInt(bytes,
+                    (ptr.getOffset() + ptr.getLength() - (Bytes.SIZEOF_BYTE + Bytes.SIZEOF_INT)), Bytes.SIZEOF_INT);
             boolean useShort = true;
             if (noOfElements < 0) {
                 noOfElements = -noOfElements;
@@ -312,7 +366,7 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
 
             int indexOffset = Bytes.toInt(bytes,
                     (ptr.getOffset() + ptr.getLength() - (Bytes.SIZEOF_BYTE + 2 * Bytes.SIZEOF_INT))) + ptr.getOffset();
-            if(arrayIndex >= noOfElements) {
+            if (arrayIndex >= noOfElements) {
                 ptr.set(ByteUtil.EMPTY_BYTE_ARRAY);
             } else {
                 // Skip those many offsets as given in the arrayIndex
@@ -343,9 +397,9 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
             }
         }
     }
-    
+
     public static void positionAtArrayElement(ImmutableBytesWritable ptr, int arrayIndex, PDataType baseDataType,
-        Integer byteSize, int offset, int length, int noOfElements, boolean first) {
+            Integer byteSize, int offset, int length, int noOfElements, boolean first) {
         byte[] bytes = ptr.get();
         if (!baseDataType.isFixedWidth()) {
             int indexOffset = Bytes.toInt(bytes, (offset + length - (Bytes.SIZEOF_BYTE + 2 * Bytes.SIZEOF_INT)))
@@ -401,10 +455,10 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
             return Bytes.toInt(bytes, offset, Bytes.SIZEOF_INT);
         }
     }
-    
-    private static int getOffset(ByteBuffer indexBuffer, int arrayIndex, boolean useShort, int indexOffset ) {
+
+    private static int getOffset(ByteBuffer indexBuffer, int arrayIndex, boolean useShort, int indexOffset) {
         int offset;
-        if(useShort) {
+        if (useShort) {
             offset = indexBuffer.getShort() + Short.MAX_VALUE;
         } else {
             offset = indexBuffer.getInt();
@@ -412,20 +466,22 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
         return offset;
     }
 
-	public Object toObject(Object object, PDataType actualType) {
-		return object;
-	}
-
-	public Object toObject(Object object, PDataType actualType, SortOrder sortOrder) {
-		// How to use the sortOrder ? Just reverse the elements
-		return toObject(object, actualType);
-	}
-	
-	/**
-	 * creates array bytes
-	 */
+    @Override
+    public Object toObject(Object object, PDataType actualType) {
+        return object;
+    }
+
+    public Object toObject(Object object, PDataType actualType, SortOrder sortOrder) {
+        // How to use the sortOrder ? Just reverse the elements
+        return toObject(object, actualType);
+    }
+
+    /**
+     * creates array bytes
+     * @param rowKeyOrderOptimizable TODO
+     */
     private byte[] createArrayBytes(TrustedByteArrayOutputStream byteStream, DataOutputStream oStream,
-            PhoenixArray array, int noOfElements, PDataType baseType, SortOrder sortOrder) {
+            PhoenixArray array, int noOfElements, PDataType baseType, SortOrder sortOrder, boolean rowKeyOrderOptimizable) {
         try {
             if (!baseType.isFixedWidth()) {
                 int[] offsetPos = new int[noOfElements];
@@ -442,11 +498,11 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
                             SortOrder.invert(bytes, 0, bytes, 0, bytes.length);
                         }
                         oStream.write(bytes, 0, bytes.length);
-                        oStream.write(QueryConstants.SEPARATOR_BYTE);
+                        oStream.write(getSeparatorByte(rowKeyOrderOptimizable, sortOrder));
                     }
                 }
                 // Double seperator byte to show end of the non null array
-                PArrayDataType.writeEndSeperatorForVarLengthArray(oStream);
+                writeEndSeperatorForVarLengthArray(oStream, sortOrder, rowKeyOrderOptimizable);
                 noOfElements = PArrayDataType.serailizeOffsetArrayIntoStream(oStream, byteStream, noOfElements,
                         offsetPos[offsetPos.length - 1], offsetPos);
                 serializeHeaderInfoIntoStream(oStream, noOfElements);
@@ -475,7 +531,8 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
         return null;
     }
 
-    public static boolean appendItemToArray(ImmutableBytesWritable ptr, int length, int offset, byte[] arrayBytes, PDataType baseType, int arrayLength, Integer maxLength, SortOrder sortOrder) {
+    public static boolean appendItemToArray(ImmutableBytesWritable ptr, int length, int offset, byte[] arrayBytes,
+            PDataType baseType, int arrayLength, Integer maxLength, SortOrder sortOrder) {
         if (ptr.getLength() == 0) {
             ptr.set(arrayBytes, offset, length);
             return true;
@@ -483,7 +540,7 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
 
         int elementLength = maxLength == null ? ptr.getLength() : maxLength;
 
-        //padding
+        // padding
         if (elementLength > ptr.getLength()) {
             baseType.pad(ptr, elementLength, sortOrder);
         }
@@ -494,10 +551,12 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
         byte[] newArray;
         if (!baseType.isFixedWidth()) {
 
-            int offsetArrayPosition = Bytes.toInt(arrayBytes, offset + length - Bytes.SIZEOF_INT - Bytes.SIZEOF_INT - Bytes.SIZEOF_BYTE, Bytes.SIZEOF_INT);
-            int offsetArrayLength = length - offsetArrayPosition - Bytes.SIZEOF_INT - Bytes.SIZEOF_INT - Bytes.SIZEOF_BYTE;
+            int offsetArrayPosition = Bytes.toInt(arrayBytes, offset + length - Bytes.SIZEOF_INT - Bytes.SIZEOF_INT
+                    - Bytes.SIZEOF_BYTE, Bytes.SIZEOF_INT);
+            int offsetArrayLength = length - offsetArrayPosition - Bytes.SIZEOF_INT - Bytes.SIZEOF_INT
+                    - Bytes.SIZEOF_BYTE;
 
-            //checks whether offset array consists of shorts or integers
+            // checks whether offset array consists of shorts or integers
             boolean useInt = offsetArrayLength / Math.abs(arrayLength) == Bytes.SIZEOF_INT;
             boolean convertToInt = false;
 
@@ -507,7 +566,8 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
                 if (PArrayDataType.useShortForOffsetArray(newElementPosition)) {
                     newArray = new byte[length + elementLength + Bytes.SIZEOF_SHORT + Bytes.SIZEOF_BYTE];
                 } else {
-                    newArray = new byte[length + elementLength + arrayLength * Bytes.SIZEOF_SHORT + Bytes.SIZEOF_INT + Bytes.SIZEOF_BYTE];
+                    newArray = new byte[length + elementLength + arrayLength * Bytes.SIZEOF_SHORT + Bytes.SIZEOF_INT
+                            + Bytes.SIZEOF_BYTE];
                     convertToInt = true;
                 }
             } else {
@@ -517,24 +577,35 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
             int newOffsetArrayPosition = newElementPosition + elementLength + 3 * Bytes.SIZEOF_BYTE;
 
             System.arraycopy(arrayBytes, offset, newArray, 0, newElementPosition);
+            // Write separator explicitly, as it may not be 0
+            byte sepByte = getSeparatorByte(isRowKeyOrderOptimized(false, sortOrder, arrayBytes, offset, length), sortOrder);
+            newArray[newOffsetArrayPosition-3] = sepByte; // Separator for new value
+            newArray[newOffsetArrayPosition-2] = sepByte; // Double byte separator
+            newArray[newOffsetArrayPosition-1] = sepByte;
             System.arraycopy(elementBytes, elementOffset, newArray, newElementPosition, elementLength);
 
-            arrayLength = (Math.abs(arrayLength) + 1) * (int) Math.signum(arrayLength);
+            arrayLength = (Math.abs(arrayLength) + 1) * (int)Math.signum(arrayLength);
             if (useInt) {
-                System.arraycopy(arrayBytes, offset + offsetArrayPosition, newArray, newOffsetArrayPosition, offsetArrayLength);
+                System.arraycopy(arrayBytes, offset + offsetArrayPosition, newArray, newOffsetArrayPosition,
+                        offsetArrayLength);
                 Bytes.putInt(newArray, newOffsetArrayPosition + offsetArrayLength, newElementPosition);
 
-                writeEndBytes(newArray, newOffsetArrayPosition, offsetArrayLength, arrayLength, arrayBytes[offset + length - 1], true);
+                writeEndBytes(newArray, newOffsetArrayPosition, offsetArrayLength, arrayLength, arrayBytes[offset
+                        + length - 1], true);
             } else {
                 if (!convertToInt) {
-                    System.arraycopy(arrayBytes, offset + offsetArrayPosition, newArray, newOffsetArrayPosition, offsetArrayLength);
-                    Bytes.putShort(newArray, newOffsetArrayPosition + offsetArrayLength, (short) (newElementPosition - Short.MAX_VALUE));
+                    System.arraycopy(arrayBytes, offset + offsetArrayPosition, newArray, newOffsetArrayPosition,
+                            offsetArrayLength);
+                    Bytes.putShort(newArray, newOffsetArrayPosition + offsetArrayLength,
+                            (short)(newElementPosition - Short.MAX_VALUE));
 
-                    writeEndBytes(newArray, newOffsetArrayPosition, offsetArrayLength, arrayLength, arrayBytes[offset + length - 1], false);
+                    writeEndBytes(newArray, newOffsetArrayPosition, offsetArrayLength, arrayLength, arrayBytes[offset
+                            + length - 1], false);
                 } else {
                     int off = newOffsetArrayPosition;
                     for (int arrayIndex = 0; arrayIndex < Math.abs(arrayLength) - 1; arrayIndex++) {
-                        Bytes.putInt(newArray, off, getOffset(arrayBytes, arrayIndex, true, offsetArrayPosition + offset));
+                        Bytes.putInt(newArray, off,
+                                getOffset(arrayBytes, arrayIndex, true, offsetArrayPosition + offset));
                         off += Bytes.SIZEOF_INT;
                     }
 
@@ -557,7 +628,8 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
         return true;
     }
 
-    private static void writeEndBytes(byte[] array, int newOffsetArrayPosition, int offsetArrayLength, int arrayLength, byte header, boolean useInt) {
+    private static void writeEndBytes(byte[] array, int newOffsetArrayPosition, int offsetArrayLength, int arrayLength,
+            byte header, boolean useInt) {
         int byteSize = useInt ? Bytes.SIZEOF_INT : Bytes.SIZEOF_SHORT;
 
         Bytes.putInt(array, newOffsetArrayPosition + offsetArrayLength + byteSize, newOffsetArrayPosition);
@@ -565,12 +637,13 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
         Bytes.putByte(array, newOffsetArrayPosition + offsetArrayLength + byteSize + 2 * Bytes.SIZEOF_INT, header);
     }
 
-    public static boolean prependItemToArray(ImmutableBytesWritable ptr, int length, int offset, byte[] arrayBytes, PDataType baseType, int arrayLength, Integer maxLength, SortOrder sortOrder) {
+    public static boolean prependItemToArray(ImmutableBytesWritable ptr, int length, int offset, byte[] arrayBytes,
+            PDataType baseType, int arrayLength, Integer maxLength, SortOrder sortOrder) {
         int elementLength = maxLength == null ? ptr.getLength() : maxLength;
         if (ptr.getLength() == 0) {
             elementLength = 0;
         }
-        //padding
+        // padding
         if (elementLength > ptr.getLength()) {
             baseType.pad(ptr, elementLength, sortOrder);
         }
@@ -579,28 +652,31 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
 
         byte[] newArray;
         if (!baseType.isFixedWidth()) {
-            int offsetArrayPosition = Bytes.toInt(arrayBytes, offset + length - Bytes.SIZEOF_INT - Bytes.SIZEOF_INT - Bytes.SIZEOF_BYTE, Bytes.SIZEOF_INT);
-            int offsetArrayLength = length - offsetArrayPosition - Bytes.SIZEOF_INT - Bytes.SIZEOF_INT - Bytes.SIZEOF_BYTE;
+            int offsetArrayPosition = Bytes.toInt(arrayBytes, offset + length - Bytes.SIZEOF_INT - Bytes.SIZEOF_INT
+                    - Bytes.SIZEOF_BYTE, Bytes.SIZEOF_INT);
+            int offsetArrayLength = length - offsetArrayPosition - Bytes.SIZEOF_INT - Bytes.SIZEOF_INT
+                    - Bytes.SIZEOF_BYTE;
             arrayLength = Math.abs(arrayLength);
 
-            //checks whether offset array consists of shorts or integers
+            // checks whether offset array consists of shorts or integers
             boolean useInt = offsetArrayLength / arrayLength == Bytes.SIZEOF_INT;
             boolean convertToInt = false;
-            int endElementPosition = getOffset(arrayBytes, arrayLength - 1, !useInt, offsetArrayPosition + offset) + elementLength + Bytes.SIZEOF_BYTE;
+            int endElementPosition = getOffset(arrayBytes, arrayLength - 1, !useInt, offsetArrayPosition + offset)
+                    + elementLength + Bytes.SIZEOF_BYTE;
             int newOffsetArrayPosition;
             int lengthIncrease;
             int firstNonNullElementPosition = 0;
             int currentPosition = 0;
-            //handle the case where prepended element is null
+            // handle the case where prepended element is null
             if (elementLength == 0) {
                 int nulls = 1;
-                //counts the number of nulls which are already at the beginning of the array
+                // counts the number of nulls which are already at the beginning of the array
                 for (int index = 0; index < arrayLength; index++) {
                     int currOffset = getOffset(arrayBytes, index, !useInt, offsetArrayPosition + offset);
                     if (arrayBytes[offset + currOffset] == QueryConstants.SEPARATOR_BYTE) {
                         nulls++;
                     } else {
-                        //gets the offset of the first element after nulls at the beginning
+                        // gets the offset of the first element after nulls at the beginning
                         firstNonNullElementPosition = currOffset;
                         break;
                     }
@@ -609,26 +685,31 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
                 int nMultiplesOver255 = nulls / 255;
                 int nRemainingNulls = nulls % 255;
 
-                //Calculates the increase in length due to prepending the null
-                //There is a length increase only when nRemainingNulls == 1
-                //nRemainingNulls == 1 and nMultiplesOver255 == 0 means there were no nulls at the beginning previously.
-                //At that case we need to increase the length by two bytes, one for separator byte and one for null count.
-                //ex: initial array - 65 0 66 0 0 0 after prepending null - 0 1(inverted) 65 0 66 0 0 0
-                //nRemainingNulls == 1 and nMultiplesOver255 != 0 means there were null at the beginning previously.
-                //In this case due to prepending nMultiplesOver255 is increased by 1.
-                //We need to increase the length by one byte to store increased that.
-                //ex: initial array - 0 1 65 0 66 0 0 0 after prepending null - 0 1 1(inverted) 65 0 66 0 0 0
-                //nRemainingNulls == 0 case.
-                //ex: initial array - 0 254(inverted) 65 0 66 0 0 0 after prepending null - 0 1 65 0 66 0 0 0
-                //nRemainingNulls > 1 case.
-                //ex: initial array - 0 45(inverted) 65 0 66 0 0 0 after prepending null - 0 46(inverted) 65 0 66 0 0 0
-                lengthIncrease = nRemainingNulls == 1 ? (nMultiplesOver255 == 0 ? 2 * Bytes.SIZEOF_BYTE : Bytes.SIZEOF_BYTE) : 0;
-                endElementPosition = getOffset(arrayBytes, arrayLength - 1, !useInt, offsetArrayPosition + offset) + lengthIncrease;
+                // Calculates the increase in length due to prepending the null
+                // There is a length increase only when nRemainingNulls == 1
+                // nRemainingNulls == 1 and nMultiplesOver255 == 0 means there were no nulls at the beginning
+                // previously.
+                // At that case we need to increase the length by two bytes, one for separator byte and one for null
+                // count.
+                // ex: initial array - 65 0 66 0 0 0 after prepending null - 0 1(inverted) 65 0 66 0 0 0
+                // nRemainingNulls == 1 and nMultiplesOver255 != 0 means there were null at the beginning previously.
+                // In this case due to prepending nMultiplesOver255 is increased by 1.
+                // We need to increase the length by one byte to store increased that.
+                // ex: initial array - 0 1 65 0 66 0 0 0 after prepending null - 0 1 1(inverted) 65 0 66 0 0 0
+                // nRemainingNulls == 0 case.
+                // ex: initial array - 0 254(inverted) 65 0 66 0 0 0 after prepending null - 0 1 65 0 66 0 0 0
+                // nRemainingNulls > 1 case.
+                // ex: initial array - 0 45(inverted) 65 0 66 0 0 0 after prepending null - 0 46(inverted) 65 0 66 0 0 0
+                lengthIncrease = nRemainingNulls == 1 ? (nMultiplesOver255 == 0 ? 2 * Bytes.SIZEOF_BYTE
+                        : Bytes.SIZEOF_BYTE) : 0;
+                endElementPosition = getOffset(arrayBytes, arrayLength - 1, !useInt, offsetArrayPosition + offset)
+                        + lengthIncrease;
                 if (!useInt) {
                     if (PArrayDataType.useShortForOffsetArray(endElementPosition)) {
                         newArray = new byte[length + Bytes.SIZEOF_SHORT + lengthIncrease];
                     } else {
-                        newArray = new byte[length + arrayLength * Bytes.SIZEOF_SHORT + Bytes.SIZEOF_INT + lengthIncrease];
+                        newArray = new byte[length + arrayLength * Bytes.SIZEOF_SHORT + Bytes.SIZEOF_INT
+                                + lengthIncrease];
                         convertToInt = true;
                     }
                 } else {
@@ -638,14 +719,15 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
                 currentPosition++;
 
                 newOffsetArrayPosition = offsetArrayPosition + lengthIncrease;
-                //serialize nulls at the beginning
+                // serialize nulls at the beginning
                 currentPosition = serializeNulls(newArray, currentPosition, nulls);
             } else {
                 if (!useInt) {
                     if (PArrayDataType.useShortForOffsetArray(endElementPosition)) {
                         newArray = new byte[length + elementLength + Bytes.SIZEOF_SHORT + Bytes.SIZEOF_BYTE];
                     } else {
-                        newArray = new byte[length + elementLength + arrayLength * Bytes.SIZEOF_SHORT + Bytes.SIZEOF_INT + Bytes.SIZEOF_BYTE];
+                        newArray = new byte[length + elementLength + arrayLength * Bytes.SIZEOF_SHORT
+                                + Bytes.SIZEOF_INT + Bytes.SIZEOF_BYTE];
                         convertToInt = true;
                     }
                 } else {
@@ -655,17 +737,22 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
 
                 lengthIncrease = elementLength + Bytes.SIZEOF_BYTE;
                 System.arraycopy(elementBytes, elementOffset, newArray, 0, elementLength);
+                // Explicitly set separator byte since for DESC it won't be 0.
+                newArray[elementLength] = getSeparatorByte(isRowKeyOrderOptimized(false, sortOrder, arrayBytes, offset, length), sortOrder);
                 currentPosition += elementLength + Bytes.SIZEOF_BYTE;
             }
 
-            System.arraycopy(arrayBytes, firstNonNullElementPosition + offset, newArray, currentPosition, offsetArrayPosition);
+            System.arraycopy(arrayBytes, firstNonNullElementPosition + offset, newArray, currentPosition,
+                    offsetArrayPosition);
 
             arrayLength = arrayLength + 1;
-            //writes the new offset and changes the previous offsets
+            // writes the new offset and changes the previous offsets
             if (useInt || convertToInt) {
-                writeNewOffsets(arrayBytes, newArray, false, !useInt, newOffsetArrayPosition, arrayLength, offsetArrayPosition, offset, lengthIncrease, length);
+                writeNewOffsets(arrayBytes, newArray, false, !useInt, newOffsetArrayPosition, arrayLength,
+                        offsetArrayPosition, offset, lengthIncrease, length);
             } else {
-                writeNewOffsets(arrayBytes, newArray, true, true, newOffsetArrayPosition, arrayLength, offsetArrayPosition, offset, lengthIncrease, length);
+                writeNewOffsets(arrayBytes, newArray, true, true, newOffsetArrayPosition, arrayLength,
+                        offsetArrayPosition, offset, lengthIncrease, length);
             }
         } else {
             newArray = new byte[length + elementLength];
@@ -678,11 +765,13 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
         return true;
     }
 
-    private static void writeNewOffsets(byte[] arrayBytes, byte[] newArray, boolean useShortNew, boolean useShortPrevious, int newOffsetArrayPosition, int arrayLength, int offsetArrayPosition, int offset, int offsetShift, int length) {
+    private static void writeNewOffsets(byte[] arrayBytes, byte[] newArray, boolean useShortNew,
+            boolean useShortPrevious, int newOffsetArrayPosition, int arrayLength, int offsetArrayPosition, int offset,
+            int offsetShift, int length) {
         int currentPosition = newOffsetArrayPosition;
         int offsetArrayElementSize = useShortNew ? Bytes.SIZEOF_SHORT : Bytes.SIZEOF_INT;
         if (useShortNew) {
-            Bytes.putShort(newArray, currentPosition, (short) (0 - Short.MAX_VALUE));
+            Bytes.putShort(newArray, currentPosition, (short)(0 - Short.MAX_VALUE));
         } else {
             Bytes.putInt(newArray, currentPosition, 0);
         }
@@ -693,13 +782,13 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
             int oldOffset = getOffset(arrayBytes, arrayIndex, useShortPrevious, offsetArrayPosition + offset);
             if (arrayBytes[offset + oldOffset] == QueryConstants.SEPARATOR_BYTE && nullsAtBeginning) {
                 if (useShortNew) {
-                    Bytes.putShort(newArray, currentPosition, (short) (oldOffset - Short.MAX_VALUE));
+                    Bytes.putShort(newArray, currentPosition, (short)(oldOffset - Short.MAX_VALUE));
                 } else {
                     Bytes.putInt(newArray, currentPosition, oldOffset);
                 }
             } else {
                 if (useShortNew) {
-                    Bytes.putShort(newArray, currentPosition, (short) (oldOffset + offsetShift - Short.MAX_VALUE));
+                    Bytes.putShort(newArray, currentPosition, (short)(oldOffset + offsetShift - Short.MAX_VALUE));
                 } else {
                     Bytes.putInt(newArray, currentPosition, oldOffset + offsetShift);
                 }
@@ -715,7 +804,8 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
         Bytes.putByte(newArray, currentPosition, arrayBytes[offset + length - 1]);
     }
 
-    public static boolean concatArrays(ImmutableBytesWritable ptr, int array1BytesLength, int array1BytesOffset, byte[] array1Bytes, PDataType baseType, int actualLengthOfArray1, int actualLengthOfArray2) {
+    public static boolean concatArrays(ImmutableBytesWritable ptr, int array1BytesLength, int array1BytesOffset,
+            byte[] array1Bytes, PDataType baseType, int actualLengthOfArray1, int actualLengthOfArray2) {
         int array2BytesLength = ptr.getLength();
         int array2BytesOffset = ptr.getOffset();
         byte[] array2Bytes = ptr.get();
@@ -723,18 +813,22 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
         byte[] newArray;
 
         if (!baseType.isFixedWidth()) {
-            int offsetArrayPositionArray1 = Bytes.toInt(array1Bytes, array1BytesOffset + array1BytesLength - Bytes.SIZEOF_INT - Bytes.SIZEOF_INT - Bytes.SIZEOF_BYTE, Bytes.SIZEOF_INT);
-            int offsetArrayPositionArray2 = Bytes.toInt(array2Bytes, array2BytesOffset + array2BytesLength - Bytes.SIZEOF_INT - Bytes.SIZEOF_INT - Bytes.SIZEOF_BYTE, Bytes.SIZEOF_INT);
-            int offsetArrayLengthArray1 = array1BytesLength - offsetArrayPositionArray1 - Bytes.SIZEOF_INT - Bytes.SIZEOF_INT - Bytes.SIZEOF_BYTE;
-            int offsetArrayLengthArray2 = array2BytesLength - offsetArrayPositionArray2 - Bytes.SIZEOF_INT - Bytes.SIZEOF_INT - Bytes.SIZEOF_BYTE;
+            int offsetArrayPositionArray1 = Bytes.toInt(array1Bytes, array1BytesOffset + array1BytesLength
+                    - Bytes.SIZEOF_INT - Bytes.SIZEOF_INT - Bytes.SIZEOF_BYTE, Bytes.SIZEOF_INT);
+            int offsetArrayPositionArray2 = Bytes.toInt(array2Bytes, array2BytesOffset + array2BytesLength
+                    - Bytes.SIZEOF_INT - Bytes.SIZEOF_INT - Bytes.SIZEOF_BYTE, Bytes.SIZEOF_INT);
+            int offsetArrayLengthArray1 = array1BytesLength - offsetArrayPositionArray1 - Bytes.SIZEOF_INT
+                    - Bytes.SIZEOF_INT - Bytes.SIZEOF_BYTE;
+            int offsetArrayLengthArray2 = array2BytesLength - offsetArrayPositionArray2 - Bytes.SIZEOF_INT
+                    - Bytes.SIZEOF_INT - Bytes.SIZEOF_BYTE;
             int newArrayLength = actualLengthOfArray1 + actualLengthOfArray2;
             int nullsAtTheEndOfArray1 = 0;
             int nullsAtTheBeginningOfArray2 = 0;
-            //checks whether offset array consists of shorts or integers
+            // checks whether offset array consists of shorts or integers
             boolean useIntArray1 = offsetArrayLengthArray1 / actualLengthOfArray1 == Bytes.SIZEOF_INT;
             boolean useIntArray2 = offsetArrayLengthArray2 / actualLengthOfArray2 == Bytes.SIZEOF_INT;
             boolean useIntNewArray = false;
-            //count nulls at the end of array 1
+            // count nulls at the end of array 1
             for (int index = actualLengthOfArray1 - 1; index > -1; index--) {
                 int offset = getOffset(array1Bytes, index, !useIntArray1, array1BytesOffset + offsetArrayPositionArray1);
                 if (array1Bytes[array1BytesOffset + offset] == QueryConstants.SEPARATOR_BYTE) {
@@ -743,7 +837,7 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
                     break;
                 }
             }
-            //count nulls at the beginning of the array 2
+            // count nulls at the beginning of the array 2
             int array2FirstNonNullElementOffset = 0;
             int array2FirstNonNullIndex = 0;
             for (int index = 0; index < actualLengthOfArray2; index++) {
@@ -757,75 +851,95 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
                 }
             }
             int nullsInMiddleAfterConcat = nullsAtTheEndOfArray1 + nullsAtTheBeginningOfArray2;
-            int bytesForNullsBefore = nullsAtTheBeginningOfArray2 / 255 + (nullsAtTheBeginningOfArray2 % 255 == 0 ? 0 : 1);
+            int bytesForNullsBefore = nullsAtTheBeginningOfArray2 / 255
+                    + (nullsAtTheBeginningOfArray2 % 255 == 0 ? 0 : 1);
             int bytesForNullsAfter = nullsInMiddleAfterConcat / 255 + (nullsInMiddleAfterConcat % 255 == 0 ? 0 : 1);
-            //Increase of length required to store nulls
+            // Increase of length required to store nulls
             int lengthIncreaseForNulls = bytesForNullsAfter - bytesForNullsBefore;
-            //Length increase incremented by one when there were no nulls at the beginning of array and when there are
-            //nulls at the end of array 1 as we need to allocate a byte for separator byte in this case.
-            lengthIncreaseForNulls += nullsAtTheBeginningOfArray2 == 0 && nullsAtTheEndOfArray1 != 0 ? Bytes.SIZEOF_BYTE : 0;
-            int newOffsetArrayPosition = offsetArrayPositionArray1 + offsetArrayPositionArray2 + lengthIncreaseForNulls - 2 * Bytes.SIZEOF_BYTE;
-            int endElementPositionOfArray2 = getOffset(array2Bytes, actualLengthOfArray2 - 1, !useIntArray2, array2BytesOffset + offsetArrayPositionArray2);
-            int newEndElementPosition = lengthIncreaseForNulls + endElementPositionOfArray2 + offsetArrayPositionArray1 - 2 * Bytes.SIZEOF_BYTE;
-            //Creates a byre array to store the concatenated array
+            // Length increase incremented by one when there were no nulls at the beginning of array and when there are
+            // nulls at the end of array 1 as we need to allocate a byte for separator byte in this case.
+            lengthIncreaseForNulls += nullsAtTheBeginningOfArray2 == 0 && nullsAtTheEndOfArray1 != 0 ? Bytes.SIZEOF_BYTE
+                    : 0;
+            int newOffsetArrayPosition = offsetArrayPositionArray1 + offsetArrayPositionArray2 + lengthIncreaseForNulls
+                    - 2 * Bytes.SIZEOF_BYTE;
+            int endElementPositionOfArray2 = getOffset(array2Bytes, actualLengthOfArray2 - 1, !useIntArray2,
+                    array2BytesOffset + offsetArrayPositionArray2);
+            int newEndElementPosition = lengthIncreaseForNulls + endElementPositionOfArray2 + offsetArrayPositionArray1
+                    - 2 * Bytes.SIZEOF_BYTE;
+            // Creates a byte array to store the concatenated array
             if (PArrayDataType.useShortForOffsetArray(newEndElementPosition)) {
-                newArray = new byte[newOffsetArrayPosition + newArrayLength * Bytes.SIZEOF_SHORT + Bytes.SIZEOF_INT + Bytes.SIZEOF_INT + Bytes.SIZEOF_BYTE];
+                newArray = new byte[newOffsetArrayPosition + newArrayLength * Bytes.SIZEOF_SHORT + Bytes.SIZEOF_INT
+                        + Bytes.SIZEOF_INT + Bytes.SIZEOF_BYTE];
             } else {
                 useIntNewArray = true;
-                newArray = new byte[newOffsetArrayPosition + newArrayLength * Bytes.SIZEOF_INT + Bytes.SIZEOF_INT + Bytes.SIZEOF_INT + Bytes.SIZEOF_BYTE];
+                newArray = new byte[newOffsetArrayPosition + newArrayLength * Bytes.SIZEOF_INT + Bytes.SIZEOF_INT
+                        + Bytes.SIZEOF_INT + Bytes.SIZEOF_BYTE];
             }
 
             int currentPosition = 0;
-            //Copies all the elements from array 1 to new array
-            System.arraycopy(array1Bytes, array1BytesOffset, newArray, currentPosition, offsetArrayPositionArray1 - 2 * Bytes.SIZEOF_BYTE);
+            // Copies all the elements from array 1 to new array
+            System.arraycopy(array1Bytes, array1BytesOffset, newArray, currentPosition, offsetArrayPositionArray1 - 2
+                    * Bytes.SIZEOF_BYTE);
             currentPosition = offsetArrayPositionArray1 - 2 * Bytes.SIZEOF_BYTE;
             int array2StartingPosition = currentPosition;
             currentPosition += nullsInMiddleAfterConcat != 0 ? 1 : 0;
-            //Writes nulls in the middle of the array.
+            // Writes nulls in the middle of the array.
             currentPosition = serializeNulls(newArray, currentPosition, nullsInMiddleAfterConcat);
-            //Copies the elements from array 2 beginning from the first non null element.
-            System.arraycopy(array2Bytes, array2BytesOffset + array2FirstNonNullElementOffset, newArray, currentPosition, offsetArrayPositionArray2 - array2FirstNonNullElementOffset);
+            // Copies the elements from array 2 beginning from the first non null element.
+            System.arraycopy(array2Bytes, array2BytesOffset + array2FirstNonNullElementOffset, newArray,
+                    currentPosition, offsetArrayPositionArray2 - array2FirstNonNullElementOffset);
             currentPosition += offsetArrayPositionArray2 - array2FirstNonNullElementOffset;
 
-            //Writing offset arrays
+            // Writing offset arrays
             if (useIntNewArray) {
-                //offsets for the elements from array 1. Simply copied.
+                // offsets for the elements from array 1. Simply copied.
                 for (int index = 0; index < actualLengthOfArray1; index++) {
-                    int offset = getOffset(array1Bytes, index, !useIntArray1, array1BytesOffset + offsetArrayPositionArray1);
+                    int offset = getOffset(array1Bytes, index, !useIntArray1, array1BytesOffset
+                            + offsetArrayPositionArray1);
                     Bytes.putInt(newArray, currentPosition, offset);
                     currentPosition += Bytes.SIZEOF_INT;
                 }
-                //offsets for nulls in the middle
+                // offsets for nulls in the middle
                 for (int index = 0; index < array2FirstNonNullIndex; index++) {
-                    int offset = getOffset(array2Bytes, index, !useIntArray2, array2BytesOffset + offsetArrayPositionArray2);
+                    int offset = getOffset(array2Bytes, index, !useIntArray2, array2BytesOffset
+                            + offsetArrayPositionArray2);
                     Bytes.putInt(newArray, currentPosition, offset + array2StartingPosition);
                     currentPosition += Bytes.SIZEOF_INT;
                 }
-                //offsets for the elements from the first non null element from array 2
-                int part2NonNullStartingPosition = array2StartingPosition + bytesForNullsAfter + (bytesForNullsAfter == 0 ? 0 : Bytes.SIZEOF_BYTE);
+                // offsets for the elements from the first non null element from array 2
+                int part2NonNullStartingPosition = array2StartingPosition + bytesForNullsAfter
+                        + (bytesForNullsAfter == 0 ? 0 : Bytes.SIZEOF_BYTE);
                 for (int index = array2FirstNonNullIndex; index < actualLengthOfArray2; index++) {
-                    int offset = getOffset(array2Bytes, index, !useIntArray2, array2BytesOffset + offsetArrayPositionArray2);
-                    Bytes.putInt(newArray, currentPosition, offset - array2FirstNonNullElementOffset + part2NonNullStartingPosition);
+                    int offset = getOffset(array2Bytes, index, !useIntArray2, array2BytesOffset
+                            + offsetArrayPositionArray2);
+                    Bytes.putInt(newArray, currentPosition, offset - array2FirstNonNullElementOffset
+                            + part2NonNullStartingPosition);
                     currentPosition += Bytes.SIZEOF_INT;
                 }
             } else {
-                //offsets for the elements from array 1. Simply copied.
+                // offsets for the elements from array 1. Simply copied.
                 for (int index = 0; index < actualLengthOfArray1; index++) {
-                    int offset = getOffset(array1Bytes, index, !useIntArray1, array1BytesOffset + offsetArrayPositionArray1);
-                    Bytes.putShort(newArray, currentPosition, (short) (offset - Short.MAX_VALUE));
+                    int offset = getOffset(array1Bytes, index, !useIntArray1, array1BytesOffset
+                            + offsetArrayPositionArray1);
+                    Bytes.putShort(newArray, currentPosition, (short)(offset - Short.MAX_VALUE));
                     currentPosition += Bytes.SIZEOF_SHORT;
                 }
-                //offsets for nulls in the middle
+                // offsets for nulls in the middle
                 for (int index = 0; index < array2FirstNonNullIndex; index++) {
-                    int offset = getOffset(array2Bytes, index, !useIntArray2, array2BytesOffset + offsetArrayPositionArray2);
-                    Bytes.putShort(newArray, currentPosition, (short) (offset + array2StartingPosition - Short.MAX_VALUE));
+                    int offset = getOffset(array2Bytes, index, !useIntArray2, array2BytesOffset
+                            + offsetArrayPositionArray2);
+                    Bytes.putShort(newArray, currentPosition,
+                            (short)(offset + array2StartingPosition - Short.MAX_VALUE));
                     currentPosition += Bytes.SIZEOF_SHORT;
                 }
-                //offsets for the elements from the first non null element from array 2
-                int part2NonNullStartingPosition = array2StartingPosition + bytesForNullsAfter + (bytesForNullsAfter == 0 ? 0 : Bytes.SIZEOF_BYTE);
+                // offsets for the elements from the first non null element from array 2
+                int part2NonNullStartingPosition = array2StartingPosition + bytesForNullsAfter
+                        + (bytesForNullsAfter == 0 ? 0 : Bytes.SIZEOF_BYTE);
                 for (int index = array2FirstNonNullIndex; index < actualLengthOfArray2; index++) {
-                    int offset = getOffset(array2Bytes, index, !useIntArray2, array2BytesOffset + offsetArrayPositionArray2);
-                    Bytes.putShort(newArray, currentPosition, (short) (offset - array2FirstNonNullElementOffset + part2NonNullStartingPosition - Short.MAX_VALUE));
+                    int offset = getOffset(array2Bytes, index, !useIntArray2, array2BytesOffset
+                            + offsetArrayPositionArray2);
+                    Bytes.putShort(newArray, currentPosition, (short)(offset - array2FirstNonNullElementOffset
+                            + part2NonNullStartingPosition - Short.MAX_VALUE));
                     currentPosition += Bytes.SIZEOF_SHORT;
                 }
             }
@@ -856,7 +970,7 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
             noOfElements = -noOfElements;
         }
         int off = 0;
-        if(useInt) {
+        if (useInt) {
             for (int pos : offsetPos) {
                 Bytes.putInt(offsetArr, off, pos);
                 off += Bytes.SIZEOF_INT;
@@ -886,9 +1000,9 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
         oStream.write(ARRAY_SERIALIZATION_VERSION);
     }
 
-	public static int initOffsetArray(int noOfElements, int baseSize) {
-		// for now create an offset array equal to the noofelements
-		return noOfElements * baseSize;
+    public static int initOffsetArray(int noOfElements, int baseSize) {
+        // for now create an offset array equal to the noofelements
+        return noOfElements * baseSize;
     }
 
     // Any variable length array would follow the below order
@@ -898,11 +1012,11 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
     // Trailing nulls are not taken into account
     // The last non null element is followed by two seperator bytes
     // For eg
-    // a, b, null, null, c, null would be 
+    // a, b, null, null, c, null would be
     // 65 0 66 0 0 2 67 0 0 0
     // a null null null b c null d would be
     // 65 0 0 3 66 0 67 0 0 1 68 0 0 0
-	// Follow the above example to understand how this works
+    // Follow the above example to understand how this works
     private Object createPhoenixArray(byte[] bytes, int offset, int length, SortOrder sortOrder,
             PDataType baseDataType, Integer maxLength, PDataType desiredDataType) {
         if (bytes == null || length == 0) { return null; }
@@ -990,45 +1104,43 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
                 }
             }
         }
-        if(baseDataType == desiredDataType) {
+        if (baseDataType == desiredDataType) {
             return PArrayDataType.instantiatePhoenixArray(baseDataType, elements);
         } else {
             return PArrayDataType.instantiatePhoenixArray(desiredDataType, elements);
         }
     }
-	
+
     public static PhoenixArray instantiatePhoenixArray(PDataType actualType, Object[] elements) {
         return PDataType.instantiatePhoenixArray(actualType, elements);
     }
-	
-	public int compareTo(Object lhs, Object rhs) {
-		PhoenixArray lhsArr = (PhoenixArray) lhs;
-		PhoenixArray rhsArr = (PhoenixArray) rhs;
-		if(lhsArr.equals(rhsArr)) {
-			return 0;
-		}
-		return 1;
-	}
-
-	public static int getArrayLength(ImmutableBytesWritable ptr,
-			PDataType baseType, Integer maxLength) {
-		byte[] bytes = ptr.get();
-		if(baseType.isFixedWidth()) {
-		    int elemLength = maxLength == null ? baseType.getByteSize() : maxLength;
-			return (ptr.getLength() / elemLength);
-		}
-		return Bytes.toInt(bytes, (ptr.getOffset() + ptr.getLength() - (Bytes.SIZEOF_BYTE + Bytes.SIZEOF_INT)));
-	}
+
+    @Override
+    public int compareTo(Object lhs, Object rhs) {
+        PhoenixArray lhsArr = (PhoenixArray)lhs;
+        PhoenixArray rhsArr = (PhoenixArray)rhs;
+        if (lhsArr.equals(rhsArr)) { return 0; }
+        return 1;
+    }
+
+    public static int getArrayLength(ImmutableBytesWritable ptr, PDataType baseType, Integer maxLength) {
+        byte[] bytes = ptr.get();
+        if (baseType.isFixedWidth()) {
+            int elemLength = maxLength == null ? baseType.getByteSize() : maxLength;
+            return (ptr.getLength() / elemLength);
+        }
+        return Bytes.toInt(bytes, (ptr.getOffset() + ptr.getLength() - (Bytes.SIZEOF_BYTE + Bytes.SIZEOF_INT)));
+    }
 
     public static int estimateSize(int size, PDataType baseType) {
-        if(baseType.isFixedWidth()) {
+        if (baseType.isFixedWidth()) {
             return baseType.getByteSize() * size;
         } else {
             return size * ValueSchema.ESTIMATED_VARIABLE_LENGTH_SIZE;
         }
-        
+
     }
-    
+
     public Object getSampleValue(PDataType baseType, Integer arrayLength, Integer elemLength) {
         Preconditions.checkArgument(arrayLength == null || arrayLength >= 0);
         if (arrayLength == null) {
@@ -1047,17 +1159,18 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
         PhoenixArray array = (PhoenixArray)o;
         PDataType baseType = PDataType.arrayBaseType(this);
         int len = array.getDimensions();
-        if (len != 0)  {
+        if (len != 0) {
             for (int i = 0; i < len; i++) {
                 buf.append(baseType.toStringLiteral(array.getElement(i), null));
                 buf.append(',');
             }
-            buf.setLength(buf.length()-1);
+            buf.setLength(buf.length() - 1);
         }
         buf.append(']');
         return buf.toString();
     }
 
+    // FIXME: remove this duplicate code
     static public class PArrayDataTypeBytesArrayBuilder<T> {
         static private final int BYTE_ARRAY_DEFAULT_SIZE = 128;
 
@@ -1083,8 +1196,7 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
                 if (oStream != null) oStream.close();
                 byteStream = null;
                 oStream = null;
-            } catch (IOException ioe) {
-            }
+            } catch (IOException ioe) {}
         }
 
         public boolean appendElem(byte[] bytes) {
@@ -1105,7 +1217,7 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
                             SortOrder.invert(bytes, offset, bytes, offset, len);
                         }
                         oStream.write(bytes, offset, len);
-                        oStream.write(QueryConstants.SEPARATOR_BYTE);
+                        oStream.write(getSeparatorByte(true, sortOrder));
                     }
                 } else {
                     if (sortOrder == SortOrder.DESC) {
@@ -1114,12 +1226,11 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
                     oStream.write(bytes, offset, len);
                 }
                 return true;
-            } catch (IOException e) {
-            }
+            } catch (IOException e) {}
             return false;
         }
 
-        public byte[] getBytesAndClose() {
+        public byte[] getBytesAndClose(SortOrder sortOrder) {
             try {
                 if (!baseType.isFixedWidth()) {
                     int noOfElements = offsetPos.size();
@@ -1129,18 +1240,15 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
                         offsetPosArray[index] = i;
                         ++index;
                     }
-                    PArrayDataType.writeEndSeperatorForVarLengthArray(oStream);
-                    noOfElements =
-                            PArrayDataType.serailizeOffsetArrayIntoStream(oStream, byteStream,
-                                noOfElements, offsetPosArray[offsetPosArray.length - 1],
-                                offsetPosArray);
+                    PArrayDataType.writeEndSeperatorForVarLengthArray(oStream, sortOrder);
+                    noOfElements = PArrayDataType.serailizeOffsetArrayIntoStream(oStream, byteStream, noOfElements,
+                            offsetPosArray[offsetPosArray.length - 1], offsetPosArray);
                     serializeHeaderInfoIntoStream(oStream, noOfElements);
                 }
                 ImmutableBytesWritable ptr = new ImmutableBytesWritable();
                 ptr.set(byteStream.getBuffer(), 0, byteStream.size());
                 return ByteUtil.copyKeyBytesIfNecessary(ptr);
-            } catch (IOException e) {
-            } finally {
+            } catch (IOException e) {} finally {
                 close();
             }
             return null;

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBinaryArray.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBinaryArray.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBinaryArray.java
index 16c6485..523f774 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBinaryArray.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBinaryArray.java
@@ -17,92 +17,70 @@
  */
 package org.apache.phoenix.schema.types;
 
-import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.phoenix.schema.SortOrder;
 
-import java.sql.Types;
-
 public class PBinaryArray extends PArrayDataType<byte[][]> {
 
-  public static final PBinaryArray INSTANCE = new PBinaryArray();
-
-  private PBinaryArray() {
-    super("BINARY ARRAY", PDataType.ARRAY_TYPE_BASE + PBinary.INSTANCE.getSqlType(),
-        PhoenixArray.class, null, 28);
-  }
-
-  @Override
-  public boolean isArrayType() {
-    return true;
-  }
-
-  @Override
-  public boolean isFixedWidth() {
-    return false;
-  }
+    public static final PBinaryArray INSTANCE = new PBinaryArray();
 
-  @Override
-  public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
-    return compareTo(lhs, rhs);
-  }
+    private PBinaryArray() {
+        super("BINARY ARRAY", PDataType.ARRAY_TYPE_BASE + PBinary.INSTANCE.getSqlType(), PhoenixArray.class, null, 28);
+    }
 
-  @Override
-  public Integer getByteSize() {
-    return null;
-  }
+    @Override
+    public boolean isArrayType() {
+        return true;
+    }
 
-  @Override
-  public byte[] toBytes(Object object) {
-    return toBytes(object, SortOrder.ASC);
-  }
+    @Override
+    public boolean isFixedWidth() {
+        return false;
+    }
 
-  @Override
-  public byte[] toBytes(Object object, SortOrder sortOrder) {
-    return toBytes(object, PBinary.INSTANCE, sortOrder);
-  }
+    @Override
+    public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
+        return compareTo(lhs, rhs);
+    }
 
-  @Override
-  public Object toObject(byte[] bytes, int offset, int length,
-      PDataType actualType, SortOrder sortOrder, Integer maxLength, Integer scale) {
-    return toObject(bytes, offset, length, PBinary.INSTANCE, sortOrder, maxLength, scale,
-        PBinary.INSTANCE);
-  }
+    @Override
+    public Integer getByteSize() {
+        return null;
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType) {
-    return isCoercibleTo(targetType, this);
-  }
+    @Override
+    public byte[] toBytes(Object object) {
+        return toBytes(object, SortOrder.ASC);
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType, Object value) {
-    if (value == null) {
-      return true;
+    @Override
+    public byte[] toBytes(Object object, SortOrder sortOrder) {
+        return toBytes(object, PBinary.INSTANCE, sortOrder);
     }
-    PhoenixArray pArr = (PhoenixArray) value;
-    Object[] charArr = (Object[]) pArr.array;
-    for (Object i : charArr) {
-      if (!super.isCoercibleTo(PBinary.INSTANCE, i)) {
-        return false;
-      }
+
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length, PDataType actualType, SortOrder sortOrder,
+            Integer maxLength, Integer scale) {
+        return toObject(bytes, offset, length, PBinary.INSTANCE, sortOrder, maxLength, scale, PBinary.INSTANCE);
     }
-    return true;
-  }
 
-  @Override
-  public void coerceBytes(ImmutableBytesWritable ptr, Object object, PDataType actualType,
-      Integer maxLength, Integer scale, SortOrder actualModifer, Integer desiredMaxLength,
-      Integer desiredScale, SortOrder desiredModifier) {
-    coerceBytes(ptr, object, actualType, maxLength, scale, desiredMaxLength, desiredScale,
-        this, actualModifer, desiredModifier);
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType) {
+        return isCoercibleTo(targetType, this);
+    }
 
-  @Override
-  public int getResultSetSqlType() {
-    return Types.ARRAY;
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType, Object value) {
+        if (value == null) { return true; }
+        PhoenixArray pArr = (PhoenixArray)value;
+        Object[] charArr = (Object[])pArr.array;
+        for (Object i : charArr) {
+            if (!super.isCoercibleTo(PBinary.INSTANCE, i)) { return false; }
+        }
+        return true;
+    }
 
-  @Override
-  public Object getSampleValue(Integer maxLength, Integer arrayLength) {
-    return getSampleValue(PBinary.INSTANCE, arrayLength, maxLength);
-  }
+    @Override
+    public Object getSampleValue(Integer maxLength, Integer arrayLength) {
+        return getSampleValue(PBinary.INSTANCE, arrayLength, maxLength);
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBooleanArray.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBooleanArray.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBooleanArray.java
index 87bf9b4..742b0de 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBooleanArray.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBooleanArray.java
@@ -17,93 +17,71 @@
  */
 package org.apache.phoenix.schema.types;
 
-import java.sql.Types;
-
-import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.phoenix.schema.SortOrder;
 import org.apache.phoenix.schema.types.PhoenixArray.PrimitiveBooleanPhoenixArray;
 
 public class PBooleanArray extends PArrayDataType<boolean[]> {
 
-  public static final PBooleanArray INSTANCE = new PBooleanArray();
+    public static final PBooleanArray INSTANCE = new PBooleanArray();
 
-  private PBooleanArray() {
-    super("BOOLEAN ARRAY", PDataType.ARRAY_TYPE_BASE + PBoolean.INSTANCE.getSqlType(),
-        PhoenixArray.class, null, 25);
-  }
+    private PBooleanArray() {
+        super("BOOLEAN ARRAY", PDataType.ARRAY_TYPE_BASE + PBoolean.INSTANCE.getSqlType(), PhoenixArray.class, null, 25);
+    }
 
-  @Override
-  public boolean isArrayType() {
-    return true;
-  }
+    @Override
+    public boolean isArrayType() {
+        return true;
+    }
 
-  @Override
-  public boolean isFixedWidth() {
-    return false;
-  }
+    @Override
+    public boolean isFixedWidth() {
+        return false;
+    }
 
-  @Override
-  public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
-    return compareTo(lhs, rhs);
-  }
+    @Override
+    public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
+        return compareTo(lhs, rhs);
+    }
 
-  @Override
-  public Integer getByteSize() {
-    return null;
-  }
+    @Override
+    public Integer getByteSize() {
+        return null;
+    }
 
-  @Override
-  public byte[] toBytes(Object object) {
-    return toBytes(object, SortOrder.ASC);
-  }
+    @Override
+    public byte[] toBytes(Object object) {
+        return toBytes(object, SortOrder.ASC);
+    }
 
-  @Override
-  public byte[] toBytes(Object object, SortOrder sortOrder) {
-    return toBytes(object, PBoolean.INSTANCE, sortOrder);
-  }
+    @Override
+    public byte[] toBytes(Object object, SortOrder sortOrder) {
+        return toBytes(object, PBoolean.INSTANCE, sortOrder);
+    }
 
-  @Override
-  public Object toObject(byte[] bytes, int offset, int length,
-      PDataType actualType, SortOrder sortOrder, Integer maxLength, Integer scale) {
-    return toObject(bytes, offset, length, PBoolean.INSTANCE, sortOrder, maxLength, scale,
-        PBoolean.INSTANCE);
-  }
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length, PDataType actualType, SortOrder sortOrder,
+            Integer maxLength, Integer scale) {
+        return toObject(bytes, offset, length, PBoolean.INSTANCE, sortOrder, maxLength, scale, PBoolean.INSTANCE);
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType) {
-    return isCoercibleTo(targetType, this);
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType) {
+        return isCoercibleTo(targetType, this);
+    }
 
     @Override
     public boolean isCoercibleTo(PDataType targetType, Object value) {
-        if (value == null) {
-            return true;
-        }
-        PrimitiveBooleanPhoenixArray pArr = (PrimitiveBooleanPhoenixArray) value;
-        boolean[] booleanArr = (boolean[]) pArr.array;
+        if (value == null) { return true; }
+        PrimitiveBooleanPhoenixArray pArr = (PrimitiveBooleanPhoenixArray)value;
+        boolean[] booleanArr = (boolean[])pArr.array;
         for (boolean b : booleanArr) {
-            if (!super.isCoercibleTo(PInteger.INSTANCE, b)) {
-                return false;
-            }
+            if (!super.isCoercibleTo(PInteger.INSTANCE, b)) { return false; }
         }
         return true;
     }
 
-  @Override
-  public int getResultSetSqlType() {
-    return Types.ARRAY;
-  }
-
-  @Override
-  public void coerceBytes(ImmutableBytesWritable ptr, Object object, PDataType actualType,
-      Integer maxLength, Integer scale, SortOrder actualModifer, Integer desiredMaxLength,
-      Integer desiredScale, SortOrder desiredModifier) {
-    coerceBytes(ptr, object, actualType, maxLength, scale, desiredMaxLength, desiredScale,
-        this, actualModifer, desiredModifier);
-  }
-
-  @Override
-  public Object getSampleValue(Integer maxLength, Integer arrayLength) {
-    return getSampleValue(PBoolean.INSTANCE, arrayLength, maxLength);
-  }
+    @Override
+    public Object getSampleValue(Integer maxLength, Integer arrayLength) {
+        return getSampleValue(PBoolean.INSTANCE, arrayLength, maxLength);
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PCharArray.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PCharArray.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PCharArray.java
index 89ec5db..a740c7f 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PCharArray.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PCharArray.java
@@ -17,92 +17,76 @@
  */
 package org.apache.phoenix.schema.types;
 
-import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.phoenix.schema.SortOrder;
 
-import java.sql.Types;
-
 public class PCharArray extends PArrayDataType<String[]> {
 
-  public static final PCharArray INSTANCE = new PCharArray();
-
-  private PCharArray() {
-    super("CHAR ARRAY", PDataType.ARRAY_TYPE_BASE + PChar.INSTANCE.getSqlType(), PhoenixArray.class,
-        null, 29);
-  }
-
-  @Override
-  public boolean isArrayType() {
-    return true;
-  }
-
-  @Override
-  public boolean isFixedWidth() {
-    return false;
-  }
+    public static final PCharArray INSTANCE = new PCharArray();
 
-  @Override
-  public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
-    return compareTo(lhs, rhs);
-  }
+    private PCharArray() {
+        super("CHAR ARRAY", PDataType.ARRAY_TYPE_BASE + PChar.INSTANCE.getSqlType(), PhoenixArray.class,
+                null, 29);
+    }
 
-  @Override
-  public Integer getByteSize() {
-    return null;
-  }
+    @Override
+    public boolean isArrayType() {
+        return true;
+    }
 
-  @Override
-  public byte[] toBytes(Object object) {
-    return toBytes(object, SortOrder.ASC);
-  }
+    @Override
+    public boolean isFixedWidth() {
+        return false;
+    }
 
-  @Override
-  public byte[] toBytes(Object object, SortOrder sortOrder) {
-    return toBytes(object, PChar.INSTANCE, sortOrder);
-  }
+    @Override
+    public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
+        return compareTo(lhs, rhs);
+    }
 
-  @Override
-  public Object toObject(byte[] bytes, int offset, int length,
-      PDataType actualType, SortOrder sortOrder, Integer maxLength, Integer scale) {
-    return toObject(bytes, offset, length, PChar.INSTANCE, sortOrder, maxLength, scale,
-        PChar.INSTANCE);
-  }
+    @Override
+    public Integer getByteSize() {
+        return null;
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType) {
-    return isCoercibleTo(targetType, this);
-  }
+    @Override
+    public byte[] toBytes(Object object) {
+        return toBytes(object, SortOrder.ASC);
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType, Object value) {
-    if (value == null) {
-      return true;
+    @Override
+    public byte[] toBytes(Object object, SortOrder sortOrder) {
+        return toBytes(object, PChar.INSTANCE, sortOrder);
     }
-    PhoenixArray pArr = (PhoenixArray) value;
-    Object[] charArr = (Object[]) pArr.array;
-    for (Object i : charArr) {
-      if (!super.isCoercibleTo(PChar.INSTANCE, i)) {
-        return false;
-      }
+
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length,
+            PDataType actualType, SortOrder sortOrder, Integer maxLength, Integer scale) {
+        return toObject(bytes, offset, length, PChar.INSTANCE, sortOrder, maxLength, scale,
+                PChar.INSTANCE);
     }
-    return true;
-  }
 
-  @Override
-  public void coerceBytes(ImmutableBytesWritable ptr, Object object, PDataType actualType,
-      Integer maxLength, Integer scale, SortOrder actualModifer, Integer desiredMaxLength,
-      Integer desiredScale, SortOrder desiredModifier) {
-    coerceBytes(ptr, object, actualType, maxLength, scale, desiredMaxLength, desiredScale,
-        this, actualModifer, desiredModifier);
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType) {
+        return isCoercibleTo(targetType, this);
+    }
 
-  @Override
-  public int getResultSetSqlType() {
-    return Types.ARRAY;
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType, Object value) {
+        if (value == null) {
+            return true;
+        }
+        PhoenixArray pArr = (PhoenixArray) value;
+        Object[] charArr = (Object[]) pArr.array;
+        for (Object i : charArr) {
+            if (!super.isCoercibleTo(PChar.INSTANCE, i)) {
+                return false;
+            }
+        }
+        return true;
+    }
 
-  @Override
-  public Object getSampleValue(Integer maxLength, Integer arrayLength) {
-    return getSampleValue(PChar.INSTANCE, arrayLength, maxLength);
-  }
+    @Override
+    public Object getSampleValue(Integer maxLength, Integer arrayLength) {
+        return getSampleValue(PChar.INSTANCE, arrayLength, maxLength);
+    }
 }


[36/50] [abbrv] phoenix git commit: PHOENIX-2067 Sort order incorrect for variable length DESC columns

Posted by ma...@apache.org.
PHOENIX-2067 Sort order incorrect for variable length DESC columns


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/4b99c632
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/4b99c632
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/4b99c632

Branch: refs/heads/calcite
Commit: 4b99c632c5e40251451e69fbe6d108f51e549e9e
Parents: 2620a80
Author: James Taylor <jt...@salesforce.com>
Authored: Tue Jul 14 13:40:58 2015 -0700
Committer: James Taylor <jt...@salesforce.com>
Committed: Tue Jul 14 13:40:58 2015 -0700

----------------------------------------------------------------------
 .../org/apache/phoenix/util/UpgradeUtil.java     | 19 ++++++++++++++++---
 1 file changed, 16 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/4b99c632/phoenix-core/src/main/java/org/apache/phoenix/util/UpgradeUtil.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/UpgradeUtil.java b/phoenix-core/src/main/java/org/apache/phoenix/util/UpgradeUtil.java
index e59ea98..0ad6b9d 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/util/UpgradeUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/util/UpgradeUtil.java
@@ -80,12 +80,11 @@ import org.apache.phoenix.schema.PTableType;
 import org.apache.phoenix.schema.SaltingUtil;
 import org.apache.phoenix.schema.SortOrder;
 import org.apache.phoenix.schema.types.PBoolean;
+import org.apache.phoenix.schema.types.PDataType;
 import org.apache.phoenix.schema.types.PDecimal;
-import org.apache.phoenix.schema.types.PDecimalArray;
 import org.apache.phoenix.schema.types.PInteger;
 import org.apache.phoenix.schema.types.PLong;
 import org.apache.phoenix.schema.types.PVarchar;
-import org.apache.phoenix.schema.types.PVarcharArray;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -878,6 +877,20 @@ public class UpgradeUtil {
         }
         return otherTables;
     }
+    
+    // Return all types that are not fixed width that may need upgrading due to PHOENIX-2067
+    // We exclude VARBINARY as we no longer support DESC for it.
+    private static String getAffectedDataTypes() {
+        StringBuilder buf = new StringBuilder("(" + PVarchar.INSTANCE.getSqlType() + "," + PDecimal.INSTANCE.getSqlType() + ",");
+        for (PDataType type : PDataType.values()) {
+            if (type.isArrayType()) {
+                buf.append(type.getSqlType());
+                buf.append(',');
+            }
+        }
+        buf.setCharAt(buf.length()-1, ')');
+        return buf.toString();
+    }
     /**
      * Identify the tables that need to be upgraded due to PHOENIX-2067
      */
@@ -890,7 +903,7 @@ public class UpgradeUtil {
                 "WHERE COLUMN_NAME IS NOT NULL\n" + 
                 "AND COLUMN_FAMILY IS NULL\n" + 
                 "AND SORT_ORDER = " + SortOrder.DESC.getSystemValue() + "\n" + 
-                "AND DATA_TYPE IN (" + PVarchar.INSTANCE.getSqlType() + "," + PDecimal.INSTANCE.getSqlType() + "," + PVarcharArray.INSTANCE.getSqlType() + "," + PDecimalArray.INSTANCE.getSqlType() + ")\n" +
+                "AND DATA_TYPE IN " + getAffectedDataTypes() + "\n" +
                 "GROUP BY TENANT_ID,TABLE_SCHEM,TABLE_NAME");
         Set<String> physicalTables = Sets.newHashSetWithExpectedSize(1024);
         List<String> remainingTableNames = addPhysicalTables(conn, rs, PTableType.INDEX, physicalTables);


[13/50] [abbrv] phoenix git commit: PHOENIX-2058 Check for existence and compatibility of columns being added in view

Posted by ma...@apache.org.
PHOENIX-2058 Check for existence and compatibility of columns being added in view


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/8c9a6b8c
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/8c9a6b8c
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/8c9a6b8c

Branch: refs/heads/calcite
Commit: 8c9a6b8cb94c0380c8e99aeee18d3b7d8f6e37ef
Parents: 61f1900
Author: Thomas D'Silva <td...@salesforce.com>
Authored: Thu Jul 2 21:19:11 2015 -0700
Committer: Thomas D'Silva <td...@salesforce.com>
Committed: Wed Jul 8 15:36:00 2015 -0700

----------------------------------------------------------------------
 .../apache/phoenix/end2end/AlterTableIT.java    | 221 +++++++++++++++++--
 .../coprocessor/MetaDataEndpointImpl.java       | 108 ++++++++-
 .../apache/phoenix/schema/MetaDataClient.java   |   4 +-
 3 files changed, 302 insertions(+), 31 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/8c9a6b8c/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java
index 56bba9b..fbaded0 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java
@@ -64,6 +64,7 @@ import org.apache.phoenix.util.PropertiesUtil;
 import org.apache.phoenix.util.ReadOnlyProps;
 import org.apache.phoenix.util.SchemaUtil;
 import org.junit.BeforeClass;
+import org.junit.Ignore;
 import org.junit.Test;
 
 import com.google.common.base.Objects;
@@ -2009,7 +2010,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
     }
     
     @Test
-    public void testAddColumnToTableWithViews() throws Exception {
+    public void testAddNewColumnToBaseTableWithViews() throws Exception {
         Connection conn = DriverManager.getConnection(getUrl());
         try {       
             conn.createStatement().execute("CREATE TABLE IF NOT EXISTS TABLEWITHVIEW ("
@@ -2020,12 +2021,176 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
                     + " )");
             assertTableDefinition(conn, "TABLEWITHVIEW", PTableType.TABLE, null, 0, 3, -1, "ID", "COL1", "COL2");
             
-            conn.createStatement().execute("CREATE VIEW VIEWOFTABLE ( VIEW_COL1 SMALLINT ) AS SELECT * FROM TABLEWITHVIEW");
-            assertTableDefinition(conn, "VIEWOFTABLE", PTableType.VIEW, "TABLEWITHVIEW", 0, 4, 3, "ID", "COL1", "COL2", "VIEW_COL1");
+            conn.createStatement().execute("CREATE VIEW VIEWOFTABLE ( VIEW_COL1 DECIMAL(10,2), VIEW_COL2 VARCHAR ) AS SELECT * FROM TABLEWITHVIEW");
+            assertTableDefinition(conn, "VIEWOFTABLE", PTableType.VIEW, "TABLEWITHVIEW", 0, 5, 3, "ID", "COL1", "COL2", "VIEW_COL1", "VIEW_COL2");
             
             conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD COL3 char(10)");
-            assertTableDefinition(conn, "VIEWOFTABLE", PTableType.VIEW, "TABLEWITHVIEW", 1, 5, 4, "ID", "COL1", "COL2", "COL3", "VIEW_COL1");
+            assertTableDefinition(conn, "VIEWOFTABLE", PTableType.VIEW, "TABLEWITHVIEW", 1, 6, 4, "ID", "COL1", "COL2", "COL3", "VIEW_COL1", "VIEW_COL2");
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testAddExistingViewColumnToBaseTableWithViews() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        try {       
+            conn.createStatement().execute("CREATE TABLE IF NOT EXISTS TABLEWITHVIEW ("
+                    + " ID char(10) NOT NULL,"
+                    + " COL1 integer NOT NULL,"
+                    + " COL2 bigint NOT NULL,"
+                    + " CONSTRAINT NAME_PK PRIMARY KEY (ID, COL1, COL2)"
+                    + " )");
+            assertTableDefinition(conn, "TABLEWITHVIEW", PTableType.TABLE, null, 0, 3, -1, "ID", "COL1", "COL2");
+            
+            conn.createStatement().execute("CREATE VIEW VIEWOFTABLE ( VIEW_COL1 DECIMAL(10,2), VIEW_COL2 VARCHAR(256), VIEW_COL3 VARCHAR, VIEW_COL4 DECIMAL ) AS SELECT * FROM TABLEWITHVIEW");
+            assertTableDefinition(conn, "VIEWOFTABLE", PTableType.VIEW, "TABLEWITHVIEW", 0, 7, 3, "ID", "COL1", "COL2", "VIEW_COL1", "VIEW_COL2", "VIEW_COL3", "VIEW_COL4");
+            
+            // upsert single row into view
+            String dml = "UPSERT INTO VIEWOFTABLE VALUES(?,?,?,?,?, ?, ?)";
+            PreparedStatement stmt = conn.prepareStatement(dml);
+            stmt.setString(1, "view1");
+            stmt.setInt(2, 12);
+            stmt.setInt(3, 13);
+            stmt.setInt(4, 14);
+            stmt.setString(5, "view5");
+            stmt.setString(6, "view6");
+            stmt.setInt(7, 17);
+            stmt.execute();
+            conn.commit();
+            
+            try {
+            	// should fail because there is already a view column with same name of different type
+            	conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD VIEW_COL1 char(10)");
+            	fail();
+            }
+            catch (SQLException e) {
+            	assertEquals("Unexpected exception", CANNOT_MUTATE_TABLE.getErrorCode(), e.getErrorCode());
+            }           
+            
+            try {
+            	// should fail because there is already a view column with same name with a higher scale
+            	conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD VIEW_COL1 DECIMAL(10,1)");
+            	fail();
+            }
+            catch (SQLException e) {
+            	assertEquals("Unexpected exception", CANNOT_MUTATE_TABLE.getErrorCode(), e.getErrorCode());
+            } 
+            
+            try {
+            	// should fail because there is already a view column with same name with a greater length
+            	conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD VIEW_COL1 VARCHAR(256)");
+            	fail();
+            }
+            catch (SQLException e) {
+            	assertEquals("Unexpected exception", CANNOT_MUTATE_TABLE.getErrorCode(), e.getErrorCode());
+            } 
+            
+            try {
+            	// should fail because there is already a view column with null length
+            	conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD VIEW_COL2 DECIMAL(9,2)");
+            	fail();
+            }
+            catch (SQLException e) {
+            	assertEquals("Unexpected exception", CANNOT_MUTATE_TABLE.getErrorCode(), e.getErrorCode());
+            }
+            
+            // validate that there were no columns added to the table or view
+            assertTableDefinition(conn, "TABLEWITHVIEW", PTableType.TABLE, null, 0, 3, -1, "ID", "COL1", "COL2");
+            assertTableDefinition(conn, "VIEWOFTABLE", PTableType.VIEW, "TABLEWITHVIEW", 0, 7, 3, "ID", "COL1", "COL2", "VIEW_COL1", "VIEW_COL2", "VIEW_COL3", "VIEW_COL4");
+            
+            // should succeed because even though there already is a view column with same name,
+            // the column being added has a scale and max length that is equal to or greater
+            conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD VIEW_COL2 VARCHAR, VIEW_COL4 DECIMAL");
+            assertTableDefinition(conn, "TABLEWITHVIEW", PTableType.TABLE, null, 1, 4, -1, "ID", "COL1", "COL2", "VIEW_COL2", "VIEW_COL4");
+            assertTableDefinition(conn, "VIEWOFTABLE", PTableType.VIEW, "TABLEWITHVIEW", 1, 7, 5, "ID", "COL1", "COL2", "VIEW_COL1", "VIEW_COL2", "VIEW_COL3", "VIEW_COL4");
+            
+            // query table
+            ResultSet rs = stmt.executeQuery("SELECT * FROM TABLEWITHVIEW");
+            assertTrue(rs.next());
+            assertEquals(rs.getString(1), "view1");
+            assertEquals(rs.getInt(2), 12);
+            assertEquals(rs.getInt(3), 13);
+            assertEquals(rs.getString(4), "view5");
+            assertEquals(rs.getInt(5), 17);
+            assertFalse(rs.next());
+
+            // query view
+            rs = stmt.executeQuery("SELECT * FROM VIEWOFTABLE");
+            assertTrue(rs.next());
+            assertEquals(rs.getString(1), "view1");
+            assertEquals(rs.getInt(2), 12);
+            assertEquals(rs.getInt(3), 13);
+            assertEquals(rs.getInt(4), 14);
+            assertEquals(rs.getString(5),"view5");
+            assertEquals(rs.getString(6),"view6");
+            assertEquals(rs.getInt(7), 17);
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    @Ignore
+    // enable this test after https://issues.apache.org/jira/browse/PHOENIX-978 is fixed 
+    public void testAddExistingViewPkColumnToBaseTableWithViews() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        try {       
+            conn.createStatement().execute("CREATE TABLE IF NOT EXISTS TABLEWITHVIEW ("
+                    + " ID char(10) NOT NULL,"
+                    + " COL1 integer NOT NULL,"
+                    + " COL2 bigint NOT NULL,"
+                    + " CONSTRAINT NAME_PK PRIMARY KEY (ID, COL1, COL2)"
+                    + " )");
+            assertTableDefinition(conn, "TABLEWITHVIEW", PTableType.TABLE, null, 0, 3, -1, "ID", "COL1", "COL2");
+            
+            conn.createStatement().execute("CREATE VIEW VIEWOFTABLE ( VIEW_COL1 DECIMAL(10,2), VIEW_COL2 VARCHAR(256) CONSTRAINT pk PRIMARY KEY (VIEW_COL1, VIEW_COL2)) AS SELECT * FROM TABLEWITHVIEW");
+            assertTableDefinition(conn, "VIEWOFTABLE", PTableType.VIEW, "TABLEWITHVIEW", 0, 5, 3, "ID", "COL1", "COL2", "VIEW_COL1", "VIEW_COL2");
             
+            // upsert single row into view
+            String dml = "UPSERT INTO VIEWOFTABLE VALUES(?,?,?,?,?)";
+            PreparedStatement stmt = conn.prepareStatement(dml);
+            stmt.setString(1, "view1");
+            stmt.setInt(2, 12);
+            stmt.setInt(3, 13);
+            stmt.setInt(4, 14);
+            stmt.setString(5, "view5");
+            stmt.execute();
+            conn.commit();
+            
+            try {
+            	// should fail because there is already a pk view column with same name is a different slot 
+            	conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD VIEW_COL2 VARCHAR PRIMARY KEY");
+            	fail();
+            }
+            catch (SQLException e) {
+            	assertEquals("Unexpected exception", CANNOT_MUTATE_TABLE.getErrorCode(), e.getErrorCode());
+            }
+            
+            // add the pk column of the view to the base table
+            conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD VIEW_COL1 DECIMAL PRIMARY KEY");
+            assertTableDefinition(conn, "TABLEWITHVIEW", PTableType.TABLE, null, 1, 4, -1, "ID", "COL1", "COL2", "VIEW_COL1");
+            assertTableDefinition(conn, "VIEWOFTABLE", PTableType.VIEW, "TABLEWITHVIEW", 1, 5, 4, "ID", "COL1", "COL2", "VIEW_COL1", "VIEW_COL2");
+            
+            // query table
+            ResultSet rs = stmt.executeQuery("SELECT * FROM TABLEWITHVIEW");
+            assertTrue(rs.next());
+            assertEquals(rs.getString(1), "view1");
+            assertEquals(rs.getInt(2), 12);
+            assertEquals(rs.getInt(3), 13);
+            assertEquals(rs.getInt(4), 14);
+            assertFalse(rs.next());
+
+            // query view
+            rs = stmt.executeQuery("SELECT * FROM VIEWOFTABLE");
+            assertTrue(rs.next());
+            assertEquals(rs.getString(1), "view1");
+            assertEquals(rs.getInt(2), 12);
+            assertEquals(rs.getInt(3), 13);
+            assertEquals(rs.getInt(4), 14);
+            assertEquals(rs.getString(5),"view5");
+            assertFalse(rs.next());
         } finally {
             conn.close();
         }
@@ -2045,34 +2210,46 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
         ResultSet parentTableColumnsRs = null; 
         if (parentTableName != null) {
             parentTableColumnsRs = conn.getMetaData().getColumns(null, null, parentTableName, null);
+            parentTableColumnsRs.next();
         }
         
-        rs = conn.getMetaData().getColumns(null, null, tableName, null);
+        ResultSet viewColumnsRs = conn.getMetaData().getColumns(null, null, tableName, null);
         for (int i = 0; i < columnName.length; i++) {
             if (columnName[i] != null) {
-                assertTrue(rs.next());
-                assertEquals(getSystemCatalogEntriesForTable(conn, tableName, "Mismatch in columnName: i=" + i), columnName[i], rs.getString("COLUMN_NAME"));
-                assertEquals(getSystemCatalogEntriesForTable(conn, tableName, "Mismatch in ordinalPosition: i=" + i), i+1, rs.getInt("ORDINAL_POSITION"));
-                if (i < baseColumnCount && parentTableColumnsRs != null) {
-                    assertTrue(parentTableColumnsRs.next());
-                    ResultSetMetaData md = parentTableColumnsRs.getMetaData();
-                    assertEquals(md.getColumnCount(), rs.getMetaData().getColumnCount());
-                    for (int columnIndex = 1; columnIndex < md.getColumnCount(); columnIndex++) {
-                        String viewColumnValue = rs.getString(columnIndex);
+                assertTrue(viewColumnsRs.next());
+                assertEquals(getSystemCatalogEntriesForTable(conn, tableName, "Mismatch in columnName: i=" + i), columnName[i], viewColumnsRs.getString(PhoenixDatabaseMetaData.COLUMN_NAME));
+                assertEquals(getSystemCatalogEntriesForTable(conn, tableName, "Mismatch in ordinalPosition: i=" + i), i+1, viewColumnsRs.getInt(PhoenixDatabaseMetaData.ORDINAL_POSITION));
+                // validate that all the columns in the base table are present in the view   
+                if (parentTableColumnsRs != null && !parentTableColumnsRs.isAfterLast()) {
+                    ResultSetMetaData parentTableColumnsMetadata = parentTableColumnsRs.getMetaData();
+                    assertEquals(parentTableColumnsMetadata.getColumnCount(), viewColumnsRs.getMetaData().getColumnCount());
+                    
+                    // if you add a non-pk column that already exists in the view
+                    if (!viewColumnsRs.getString(PhoenixDatabaseMetaData.COLUMN_NAME).equals(parentTableColumnsRs.getString(PhoenixDatabaseMetaData.COLUMN_NAME))) {
+                    	continue;
+                    }
+                    
+                    for (int columnIndex = 1; columnIndex < parentTableColumnsMetadata.getColumnCount(); columnIndex++) {
+                        String viewColumnValue = viewColumnsRs.getString(columnIndex);
                         String parentTableColumnValue = parentTableColumnsRs.getString(columnIndex);
                         if (!Objects.equal(viewColumnValue, parentTableColumnValue)) {
-                            if (md.getColumnName(columnIndex).equals("TABLE_NAME")) {
+                            if (parentTableColumnsMetadata.getColumnName(columnIndex).equals(PhoenixDatabaseMetaData.TABLE_NAME)) {
                                 assertEquals(parentTableName, parentTableColumnValue);
                                 assertEquals(tableName, viewColumnValue);
-                            } else {
-                                fail(md.getColumnName(columnIndex) + "=" + parentTableColumnValue);
+                            } 
+                            else if (parentTableColumnsMetadata.getColumnName(columnIndex).equals(PhoenixDatabaseMetaData.ORDINAL_POSITION) && parentTableColumnsRs.getString("COLUMN_FAMILY")!=null) {
+                            	// its ok if the ordinal positions don't match for non-pk columns 
+                            }
+                            else {
+                                fail(parentTableColumnsMetadata.getColumnName(columnIndex) + " of base table " + parentTableColumnValue + " does not match view "+viewColumnValue) ;
                             }
                         }
                     }
+                    parentTableColumnsRs.next();
                 }
             }
         }
-        assertFalse(getSystemCatalogEntriesForTable(conn, tableName, ""), rs.next());
+        assertFalse(getSystemCatalogEntriesForTable(conn, tableName, ""), viewColumnsRs.next());
     }
     
     private String getSystemCatalogEntriesForTable(Connection conn, String tableName, String message) throws Exception {
@@ -2207,7 +2384,13 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
             conn.createStatement().execute(dropColumn);
             
             String alterBaseTable = "ALTER TABLE " + baseTable + " ADD V3 VARCHAR";
-            conn.createStatement().execute(alterBaseTable);
+            try {
+            	conn.createStatement().execute(alterBaseTable);
+	            fail();
+	        }
+	        catch (SQLException e) {
+	        	assertEquals("Unexpected exception", CANNOT_MUTATE_TABLE.getErrorCode(), e.getErrorCode());
+	        }
             
             // Column V3 shouldn't have propagated to the divorced view.
             String sql = "SELECT V3 FROM " + viewName;

http://git-wip-us.apache.org/repos/asf/phoenix/blob/8c9a6b8c/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 1fc1d7f..b27beb4 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
@@ -39,6 +39,7 @@ import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.IS_ARRAY_BYTES;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.IS_CONSTANT_BYTES;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.IS_VIEW_REFERENCED_BYTES;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.JAR_PATH_BYTES;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.KEY_SEQ_BYTES;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.LINK_TYPE_BYTES;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.MAX_VALUE_BYTES;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.MIN_VALUE_BYTES;
@@ -1580,8 +1581,18 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
             return null; // impossible
         }
     }
+    
+    private boolean isValidLengthOrScale(Integer viewColVal, Cell cell) {
+    	Integer baseColValue = cell.getValueArray()==null ? null :
+			PInteger.INSTANCE.getCodec().decodeInt(cell.getValueArray(), cell.getValueOffset(), SortOrder.getDefault());
+    	if ( (baseColValue!=null && viewColVal!=null && baseColValue<viewColVal) || // if the base column has a scale/length less then the view column
+    			(viewColVal==null && baseColValue!=null)) {  // or if the view column has no scale/length, but the base column has a scale/length
+    		return false;
+    	}
+    	return true;
+    }
 
-    private void addRowsToChildViews(List<Mutation> tableMetadata, List<Mutation> mutationsForAddingColumnsToViews, byte[] schemaName, byte[] tableName,
+    private MetaDataMutationResult addRowsToChildViews(List<Mutation> tableMetadata, List<Mutation> mutationsForAddingColumnsToViews, byte[] schemaName, byte[] tableName,
             List<ImmutableBytesPtr> invalidateList, long clientTimeStamp, TableViewFinderResult childViewsResult,
             Region region, List<RowLock> locks) throws IOException, SQLException {
         for (Result viewResult : childViewsResult.getResults()) {
@@ -1597,18 +1608,28 @@ 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.
-                return;
+            	return new MetaDataMutationResult(MutationCode.UNALLOWED_TABLE_MUTATION, EnvironmentEdgeManager.currentTimeMillis(), null);
             }
-            int deltaNumberOfColumns = 0;
+            int numColsAddedToBaseTable = 0;
+            int numColsAddedToView = 0;
             short deltaNumPkColsSoFar = 0;
+            PColumn existingViewColumn = null;
             for (Mutation m : tableMetadata) {
                 byte[][] rkmd = new byte[5][];
                 int pkCount = getVarChars(m.getRow(), rkmd);
                 if (m instanceof Put && pkCount > COLUMN_NAME_INDEX
                         && Bytes.compareTo(schemaName, rkmd[SCHEMA_NAME_INDEX]) == 0
                         && Bytes.compareTo(tableName, rkmd[TABLE_NAME_INDEX]) == 0) {
+                	try {
+                        // Maybe deserving of a new SchemaUtil.getColumnByName(byte[] familyName, String columnName) function 
+                        String columnName = Bytes.toString(rkmd[COLUMN_NAME_INDEX]);
+                        existingViewColumn = rkmd[FAMILY_NAME_INDEX] == null 
+                            ? view.getPKColumn(columnName) 
+                            : view.getColumnFamily(rkmd[FAMILY_NAME_INDEX]).getColumn(columnName);
+                    } catch (ColumnNotFoundException e) {
+                    } // Ignore - means column family or column name don't exist
+                	
                     Put p = (Put)m;
-
                     byte[] columnKey = ByteUtil.concat(viewKey, QueryConstants.SEPARATOR_BYTE_ARRAY, rkmd[COLUMN_NAME_INDEX]);
                     if (rkmd[FAMILY_NAME_INDEX] != null) {
                         columnKey = ByteUtil.concat(columnKey, QueryConstants.SEPARATOR_BYTE_ARRAY, rkmd[FAMILY_NAME_INDEX]);
@@ -1619,7 +1640,70 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
                                 CellUtil.cloneQualifier(cell), cell.getTimestamp(), cell.getTypeByte(),
                                 CellUtil.cloneValue(cell)));
                     }
-                    deltaNumberOfColumns++;
+                    
+                    // if there is already a view column with the same name as the base table column we are trying to add
+                	if (existingViewColumn!=null) {
+                		List<Cell> dataTypes = viewColumnDefinitionPut
+                                .get(PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
+                                        PhoenixDatabaseMetaData.DATA_TYPE_BYTES);
+                		if (dataTypes != null && dataTypes.size() > 0) {
+                            Cell cell = dataTypes.get(0);
+                            int typeId = PInteger.INSTANCE.getCodec().decodeInt(cell.getValueArray(), cell.getValueOffset(), SortOrder.getDefault());
+                    		PDataType dataType = PDataType.fromTypeId(typeId);
+                    		if (!existingViewColumn.getDataType().equals(dataType)) {
+                    			 return new MetaDataMutationResult(MutationCode.UNALLOWED_TABLE_MUTATION, EnvironmentEdgeManager.currentTimeMillis(), null);
+                    		}
+                		}
+                		if (rkmd[FAMILY_NAME_INDEX] == null && rkmd[COLUMN_NAME_INDEX] != null) {
+	                        List<Cell> keySeqCells = viewColumnDefinitionPut.get(
+	                                PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
+	                                PhoenixDatabaseMetaData.KEY_SEQ_BYTES);
+	                        if (keySeqCells != null && keySeqCells.size() > 0) {
+	                        	Cell cell = keySeqCells.get(0);
+	                        	int keySeq = PSmallint.INSTANCE.getCodec().decodeInt(cell.getValueArray(), cell.getValueOffset(), SortOrder.getDefault());
+	                     		int pkPosition = SchemaUtil.getPKPosition(view, existingViewColumn)+1;
+	                     		if (pkPosition!=keySeq) {
+	                     			return new MetaDataMutationResult(MutationCode.UNALLOWED_TABLE_MUTATION, EnvironmentEdgeManager.currentTimeMillis(), null);
+	                     		}
+	                        }
+                        }
+                		// if there is an existing view column that matches the column being added to the base table and if the column being added has a null
+                    	// scale or maxLength, we need to explicity do a put to set the scale or maxLength to null (in case the view column has the scale or 
+                    	// max length set)
+                		List<Cell> columnSizes = viewColumnDefinitionPut.get(
+                                PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
+                                PhoenixDatabaseMetaData.COLUMN_SIZE_BYTES);
+                        if (columnSizes != null && columnSizes.size() > 0) {
+                            Cell cell = columnSizes.get(0);
+                            if (!isValidLengthOrScale(existingViewColumn.getMaxLength(),cell)) {
+                            	return new MetaDataMutationResult(MutationCode.UNALLOWED_TABLE_MUTATION, EnvironmentEdgeManager.currentTimeMillis(), null); 
+                            }
+                        }
+                        else if (existingViewColumn.getMaxLength()!=null) {
+                        	viewColumnDefinitionPut.add(PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
+                                    PhoenixDatabaseMetaData.COLUMN_SIZE_BYTES, null);
+                        }
+                        List<Cell> decimalDigits = viewColumnDefinitionPut.get(
+                                PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
+                                PhoenixDatabaseMetaData.DECIMAL_DIGITS_BYTES);
+                        if (decimalDigits != null && decimalDigits.size() > 0) {
+                            Cell cell = decimalDigits.get(0);
+                            if (!isValidLengthOrScale(existingViewColumn.getScale(),cell)) {
+                            	return new MetaDataMutationResult(MutationCode.UNALLOWED_TABLE_MUTATION, EnvironmentEdgeManager.currentTimeMillis(), null); 
+                            }
+                        }
+                        else if (existingViewColumn.getScale()!=null) {
+                        	viewColumnDefinitionPut.add(PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
+                                    PhoenixDatabaseMetaData.DECIMAL_DIGITS_BYTES, null);
+                        }
+                	}
+                	else {
+                    	// if we are adding a column that already exists in the view, no need to updates the base table or view table column count
+                    	numColsAddedToView++;
+                    } 
+                    	
+                	
+                    numColsAddedToBaseTable++;
                     mutationsForAddingColumnsToViews.add(viewColumnDefinitionPut);
                     if (rkmd[FAMILY_NAME_INDEX] == null && rkmd[COLUMN_NAME_INDEX] != null) {
                         /*
@@ -1743,9 +1827,9 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
 
             Put viewHeaderRowPut = new Put(viewKey, clientTimeStamp);
             byte[] baseColumnCountPtr = new byte[PInteger.INSTANCE.getByteSize()];
-            PInteger.INSTANCE.getCodec().encodeInt(oldBaseColumnCount + deltaNumberOfColumns, baseColumnCountPtr, 0);
+            PInteger.INSTANCE.getCodec().encodeInt(oldBaseColumnCount + numColsAddedToBaseTable, baseColumnCountPtr, 0);
             byte[] columnCountPtr = new byte[PInteger.INSTANCE.getByteSize()];
-            PInteger.INSTANCE.getCodec().encodeInt(view.getColumns().size() + deltaNumberOfColumns, columnCountPtr, 0);
+            PInteger.INSTANCE.getCodec().encodeInt(view.getColumns().size() + numColsAddedToView, columnCountPtr, 0);
             byte[] viewSequencePtr = new byte[PLong.INSTANCE.getByteSize()];
             PLong.INSTANCE.getCodec().encodeLong(view.getSequenceNumber() + 1, viewSequencePtr, 0);
             viewHeaderRowPut.addColumn(PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
@@ -1759,7 +1843,7 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
             // Update positions of view columns
             for (PColumn column : view.getColumns()) {
                 if (column.getPosition() >= oldBaseColumnCount) {
-                    int newPosition = column.getPosition() + deltaNumberOfColumns + 1;
+                    int newPosition = column.getPosition() + numColsAddedToView + 1;
 
                     byte[] k = ByteUtil.concat(viewKey, QueryConstants.SEPARATOR_BYTE_ARRAY, column.getName()
                             .getBytes());
@@ -1777,6 +1861,7 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
             }
             invalidateList.add(new ImmutableBytesPtr(viewKey));
         }
+        return null;
     }
     
     private byte[] getViewIndexHeaderRowKey(PTable index) {
@@ -1820,9 +1905,12 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso
                                         EnvironmentEdgeManager.currentTimeMillis(), null);
                             } else {
                                 mutationsForAddingColumnsToViews = new ArrayList<>(childViewsResult.getResults().size() * tableMetaData.size());
-                                addRowsToChildViews(tableMetaData, mutationsForAddingColumnsToViews, schemaName, tableName, invalidateList, clientTimeStamp,
+                                MetaDataMutationResult mutationResult = addRowsToChildViews(tableMetaData, mutationsForAddingColumnsToViews, schemaName, tableName, invalidateList, clientTimeStamp,
                                         childViewsResult, region, locks);
-                            }
+                                // return if we were not able to add the column successfully
+                                if (mutationResult!=null)
+                                	return mutationResult;
+                            } 
                         }
                     }
                     for (Mutation m : tableMetaData) {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/8c9a6b8c/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
index f24da44..8a1c14b 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
@@ -2204,10 +2204,10 @@ public class MetaDataClient {
             String familyName = null;
             String msg = null;
             // TODO: better to return error code
-            if (result.getColumnName() != null) {
+            if (result.getColumnName() != null && result.getColumnName() != null) {
                 familyName = result.getFamilyName() == null ? null : Bytes.toString(result.getFamilyName());
                 columnName = Bytes.toString(result.getColumnName());
-                msg = "Cannot drop column referenced by VIEW";
+                msg = "Cannot add/drop column referenced by VIEW";
             }
             throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_MUTATE_TABLE)
                 .setSchemaName(schemaName).setTableName(tableName).setFamilyName(familyName).setColumnName(columnName).setMessage(msg).build().buildException();


[09/50] [abbrv] phoenix git commit: PHOENIX-2025 - Phoenix-core's hbase-default.xml prevents HBaseTestingUtility from starting up in client apps (Thomas D'Silva)

Posted by ma...@apache.org.
PHOENIX-2025 - Phoenix-core's hbase-default.xml prevents HBaseTestingUtility from starting up in client apps (Thomas D'Silva)


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/973bccb7
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/973bccb7
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/973bccb7

Branch: refs/heads/calcite
Commit: 973bccb7019bfb0c15a9fd5259d7b01f37859b52
Parents: dee7a02
Author: ravimagham <ra...@apache.org>
Authored: Tue Jul 7 22:10:59 2015 -0700
Committer: ravimagham <ra...@apache.org>
Committed: Tue Jul 7 22:10:59 2015 -0700

----------------------------------------------------------------------
 .../java/org/apache/phoenix/mapreduce/util/ConnectionUtil.java | 4 ++--
 .../phoenix/mapreduce/util/PhoenixConfigurationUtil.java       | 6 ++++--
 2 files changed, 6 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/973bccb7/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/util/ConnectionUtil.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/util/ConnectionUtil.java b/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/util/ConnectionUtil.java
index 000ce59..b4724d8 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/util/ConnectionUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/util/ConnectionUtil.java
@@ -88,9 +88,9 @@ public class ConnectionUtil {
      * @param clientPort a ZooKeeper client port
      * @return a Phoenix connection to the given connection string
      */
-    private static Connection getConnection(final String quorum, final int clientPort, Properties props) throws SQLException {
+    private static Connection getConnection(final String quorum, final Integer clientPort, Properties props) throws SQLException {
         Preconditions.checkNotNull(quorum);
-        return DriverManager.getConnection(QueryUtil.getUrl(quorum, clientPort), props);
+        return DriverManager.getConnection(clientPort!=null? QueryUtil.getUrl(quorum, clientPort) :  QueryUtil.getUrl(quorum), props);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/973bccb7/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/util/PhoenixConfigurationUtil.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/util/PhoenixConfigurationUtil.java b/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/util/PhoenixConfigurationUtil.java
index bba96ac..e26f988 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/util/PhoenixConfigurationUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/mapreduce/util/PhoenixConfigurationUtil.java
@@ -27,6 +27,7 @@ import com.google.common.base.Joiner;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Splitter;
 import com.google.common.collect.Lists;
+
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.hadoop.conf.Configuration;
@@ -371,9 +372,10 @@ public final class PhoenixConfigurationUtil {
      * @param configuration
      * @return
      */
-    public static int getClientPort(final Configuration configuration) {
+    public static Integer getClientPort(final Configuration configuration) {
         Preconditions.checkNotNull(configuration);
-        return Integer.parseInt(configuration.get(HBASE_ZOOKEEPER_CLIENT_PORT));
+        String clientPortString = configuration.get(HBASE_ZOOKEEPER_CLIENT_PORT);
+        return clientPortString==null ? null : Integer.parseInt(clientPortString);
     }
 
     public static void loadHBaseConfiguration(Job job) throws IOException {


[12/50] [abbrv] phoenix git commit: PHOENIX-2096 Tweak criteria for when round robin iterator is used

Posted by ma...@apache.org.
PHOENIX-2096 Tweak criteria for when round robin iterator is used


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/61f1900d
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/61f1900d
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/61f1900d

Branch: refs/heads/calcite
Commit: 61f1900ddec5eb5c06245c999711e1fd0c67af53
Parents: 69e55df
Author: James Taylor <jt...@salesforce.com>
Authored: Thu Jul 2 09:48:42 2015 -0700
Committer: James Taylor <jt...@salesforce.com>
Committed: Wed Jul 8 11:57:40 2015 -0700

----------------------------------------------------------------------
 .../apache/phoenix/end2end/ReverseScanIT.java   | 29 ++++++++++++++++----
 .../org/apache/phoenix/execute/ScanPlan.java    |  4 +--
 .../java/org/apache/phoenix/util/ScanUtil.java  |  7 ++---
 3 files changed, 28 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/61f1900d/phoenix-core/src/it/java/org/apache/phoenix/end2end/ReverseScanIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ReverseScanIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ReverseScanIT.java
index 5481d80..eca183b 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ReverseScanIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ReverseScanIT.java
@@ -19,6 +19,9 @@ package org.apache.phoenix.end2end;
 
 import static org.apache.phoenix.util.TestUtil.ROW2;
 import static org.apache.phoenix.util.TestUtil.ROW3;
+import static org.apache.phoenix.util.TestUtil.ROW4;
+import static org.apache.phoenix.util.TestUtil.ROW5;
+import static org.apache.phoenix.util.TestUtil.ROW6;
 import static org.apache.phoenix.util.TestUtil.ROW7;
 import static org.apache.phoenix.util.TestUtil.ROW8;
 import static org.apache.phoenix.util.TestUtil.ROW9;
@@ -31,6 +34,7 @@ import java.sql.Connection;
 import java.sql.DriverManager;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
+import java.sql.Statement;
 import java.util.Map;
 import java.util.Properties;
 
@@ -39,6 +43,7 @@ import org.apache.hadoop.hbase.client.HBaseAdmin;
 import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.phoenix.jdbc.PhoenixConnection;
 import org.apache.phoenix.util.PropertiesUtil;
+import org.apache.phoenix.util.QueryUtil;
 import org.apache.phoenix.util.ReadOnlyProps;
 import org.apache.phoenix.util.TestUtil;
 import org.junit.BeforeClass;
@@ -81,11 +86,11 @@ public class ReverseScanIT extends BaseHBaseManagedTimeIT {
         initATableValues(tenantId, getSplitsAtRowKeys(tenantId), getUrl());
         Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
         Connection conn = DriverManager.getConnection(getUrl(), props);
-        String query = "SELECT entity_id FROM aTable WHERE entity_id >= ? ORDER BY organization_id DESC, entity_id DESC";
+        String query = "SELECT entity_id FROM aTable WHERE entity_id >= '" + ROW3 + "' ORDER BY organization_id DESC, entity_id DESC";
         try {
-            PreparedStatement statement = conn.prepareStatement(query);
-            statement.setString(1, ROW7);
-            ResultSet rs = statement.executeQuery();
+            Statement stmt = conn.createStatement();
+            stmt.setFetchSize(2);
+            ResultSet rs = stmt.executeQuery(query);
 
             assertTrue (rs.next());
             assertEquals(ROW9,rs.getString(1));
@@ -93,10 +98,24 @@ public class ReverseScanIT extends BaseHBaseManagedTimeIT {
             assertEquals(ROW8,rs.getString(1));
             assertTrue (rs.next());
             assertEquals(ROW7,rs.getString(1));
+            assertTrue (rs.next());
+            assertEquals(ROW6,rs.getString(1));
+            assertTrue (rs.next());
+            assertEquals(ROW5,rs.getString(1));
+            assertTrue (rs.next());
+            assertEquals(ROW4,rs.getString(1));
+            assertTrue (rs.next());
+            assertEquals(ROW3,rs.getString(1));
 
             assertFalse(rs.next());
             
-            statement = conn.prepareStatement("SELECT entity_id FROM aTable WHERE organization_id = ? AND entity_id >= ? ORDER BY organization_id DESC, entity_id DESC");
+            rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+            assertEquals(
+                    "CLIENT PARALLEL 1-WAY REVERSE FULL SCAN OVER ATABLE\n" + 
+                    "    SERVER FILTER BY FIRST KEY ONLY AND ENTITY_ID >= '00A323122312312'",
+                    QueryUtil.getExplainPlan(rs));
+            
+            PreparedStatement statement = conn.prepareStatement("SELECT entity_id FROM aTable WHERE organization_id = ? AND entity_id >= ? ORDER BY organization_id DESC, entity_id DESC");
             statement.setString(1, tenantId);
             statement.setString(2, ROW7);
             rs = statement.executeQuery();

http://git-wip-us.apache.org/repos/asf/phoenix/blob/61f1900d/phoenix-core/src/main/java/org/apache/phoenix/execute/ScanPlan.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/execute/ScanPlan.java b/phoenix-core/src/main/java/org/apache/phoenix/execute/ScanPlan.java
index b9dd2f2..fd9f8ad 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/execute/ScanPlan.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/execute/ScanPlan.java
@@ -18,8 +18,6 @@
 package org.apache.phoenix.execute;
 
 
-import static org.apache.phoenix.util.ScanUtil.shouldRowsBeInRowKeyOrder;
-
 import java.sql.SQLException;
 import java.util.List;
 
@@ -187,7 +185,7 @@ public class ScanPlan extends BaseQueryPlan {
         if (isOrdered) {
             scanner = new MergeSortTopNResultIterator(iterators, limit, orderBy.getOrderByExpressions());
         } else {
-            if ((isSalted || table.getIndexType() == IndexType.LOCAL) && shouldRowsBeInRowKeyOrder(orderBy, context)) { 
+            if ((isSalted || table.getIndexType() == IndexType.LOCAL) && ScanUtil.forceRowKeyOrder(context)) { 
                 scanner = new MergeSortRowKeyResultIterator(iterators, isSalted ? SaltingUtil.NUM_SALTING_BYTES : 0, orderBy == OrderBy.REV_ROW_KEY_ORDER_BY);
             } else if (useRoundRobinIterator()) {
                 scanner = new RoundRobinResultIterator(iterators, this);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/61f1900d/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 a1473ef..3dff972 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
@@ -672,12 +672,11 @@ public class ScanUtil {
          * not even row key order. Also no point doing round robin of scanners if fetch size
          * is 1.
          */
-        return fetchSize > 1 && !shouldRowsBeInRowKeyOrder(orderBy, context) && orderBy.getOrderByExpressions().isEmpty();
+        return fetchSize > 1 && !forceRowKeyOrder(context) && (orderBy.getOrderByExpressions().isEmpty() && orderBy != FWD_ROW_KEY_ORDER_BY && orderBy != REV_ROW_KEY_ORDER_BY);
     }
     
-    public static boolean shouldRowsBeInRowKeyOrder(OrderBy orderBy, StatementContext context) {
-        boolean forceRowKeyOrder = context.getConnection().getQueryServices().getProps()
+    public static boolean forceRowKeyOrder(StatementContext context) {
+        return context.getConnection().getQueryServices().getProps()
                 .getBoolean(QueryServices.FORCE_ROW_KEY_ORDER_ATTRIB, QueryServicesOptions.DEFAULT_FORCE_ROW_KEY_ORDER);
-        return forceRowKeyOrder || orderBy == FWD_ROW_KEY_ORDER_BY || orderBy == REV_ROW_KEY_ORDER_BY;
     }
 }
\ No newline at end of file


[35/50] [abbrv] phoenix git commit: PHOENIX-2067 Sort order incorrect for variable length DESC columns

Posted by ma...@apache.org.
PHOENIX-2067 Sort order incorrect for variable length DESC columns


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/2620a80c
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/2620a80c
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/2620a80c

Branch: refs/heads/calcite
Commit: 2620a80c1e35c0d214f06a1b16e99da5415a1a2c
Parents: 01b4f60
Author: James Taylor <jt...@salesforce.com>
Authored: Mon Jul 13 11:17:37 2015 -0700
Committer: James Taylor <jt...@salesforce.com>
Committed: Tue Jul 14 10:55:27 2015 -0700

----------------------------------------------------------------------
 dev/eclipse_prefs_phoenix.epf                   |    2 +-
 .../org/apache/phoenix/end2end/ArrayIT.java     |   59 +
 .../org/apache/phoenix/end2end/IsNullIT.java    |   52 +-
 .../apache/phoenix/end2end/LpadFunctionIT.java  |   24 +
 .../apache/phoenix/end2end/ReverseScanIT.java   |   30 +
 .../phoenix/end2end/RowValueConstructorIT.java  |    7 +-
 .../apache/phoenix/end2end/SortOrderFIT.java    |  563 -----
 .../org/apache/phoenix/end2end/SortOrderIT.java |  572 +++++
 .../apache/phoenix/compile/FromCompiler.java    |    3 +-
 .../apache/phoenix/compile/JoinCompiler.java    |    8 +-
 .../apache/phoenix/compile/OrderByCompiler.java |    4 +-
 .../phoenix/compile/OrderPreservingTracker.java |    7 +-
 .../org/apache/phoenix/compile/ScanRanges.java  |    5 +-
 .../compile/TupleProjectionCompiler.java        |    4 +-
 .../apache/phoenix/compile/UnionCompiler.java   |    5 +-
 .../apache/phoenix/compile/UpsertCompiler.java  |   16 +-
 .../apache/phoenix/compile/WhereOptimizer.java  |   53 +-
 .../coprocessor/BaseScannerRegionObserver.java  |    2 +
 .../coprocessor/MetaDataEndpointImpl.java       |   73 +-
 .../UngroupedAggregateRegionObserver.java       |  108 +-
 .../coprocessor/generated/PTableProtos.java     |  105 +-
 .../phoenix/exception/SQLExceptionCode.java     |    1 +
 .../apache/phoenix/execute/BaseQueryPlan.java   |   14 +-
 .../DescVarLengthFastByteComparisons.java       |  219 ++
 .../expression/ArrayConstructorExpression.java  |    2 +-
 .../phoenix/expression/OrderByExpression.java   |   13 +-
 .../RowValueConstructorExpression.java          |    8 +-
 .../function/ArrayConcatFunction.java           |   11 +-
 .../function/ArrayModifierFunction.java         |    3 +-
 .../expression/function/LpadFunction.java       |    8 +-
 .../expression/util/regex/JONIPattern.java      |    5 +-
 .../apache/phoenix/filter/SkipScanFilter.java   |    3 +-
 .../apache/phoenix/index/IndexMaintainer.java   |  127 +-
 .../phoenix/iterate/BaseResultIterators.java    |  109 +-
 .../phoenix/iterate/OrderedResultIterator.java  |   52 +-
 .../apache/phoenix/jdbc/PhoenixConnection.java  |   29 +-
 .../query/ConnectionQueryServicesImpl.java      |   17 +-
 .../java/org/apache/phoenix/query/KeyRange.java |   14 -
 .../apache/phoenix/query/QueryConstants.java    |    3 +
 .../apache/phoenix/schema/DelegateTable.java    |    5 +
 .../apache/phoenix/schema/MetaDataClient.java   |   31 +-
 .../java/org/apache/phoenix/schema/PTable.java  |    9 +
 .../org/apache/phoenix/schema/PTableImpl.java   |   78 +-
 .../org/apache/phoenix/schema/RowKeySchema.java |   44 +-
 .../phoenix/schema/RowKeyValueAccessor.java     |   12 +-
 .../org/apache/phoenix/schema/ValueSchema.java  |   30 +-
 .../phoenix/schema/stats/StatisticsUtil.java    |    4 +-
 .../phoenix/schema/types/PArrayDataType.java    |  682 +++---
 .../phoenix/schema/types/PBinaryArray.java      |  122 +-
 .../phoenix/schema/types/PBooleanArray.java     |  112 +-
 .../apache/phoenix/schema/types/PCharArray.java |  128 +-
 .../apache/phoenix/schema/types/PDataType.java  | 2037 +++++++++---------
 .../apache/phoenix/schema/types/PDateArray.java |  131 +-
 .../phoenix/schema/types/PDecimalArray.java     |  126 +-
 .../phoenix/schema/types/PDoubleArray.java      |  128 +-
 .../phoenix/schema/types/PFloatArray.java       |  130 +-
 .../phoenix/schema/types/PIntegerArray.java     |  130 +-
 .../apache/phoenix/schema/types/PLongArray.java |  130 +-
 .../phoenix/schema/types/PSmallintArray.java    |  130 +-
 .../apache/phoenix/schema/types/PTimeArray.java |  133 +-
 .../phoenix/schema/types/PTimestampArray.java   |  132 +-
 .../phoenix/schema/types/PTinyintArray.java     |  130 +-
 .../schema/types/PUnsignedDateArray.java        |  128 +-
 .../schema/types/PUnsignedDoubleArray.java      |  136 +-
 .../schema/types/PUnsignedFloatArray.java       |  130 +-
 .../phoenix/schema/types/PUnsignedIntArray.java |  130 +-
 .../schema/types/PUnsignedLongArray.java        |  130 +-
 .../schema/types/PUnsignedSmallintArray.java    |  132 +-
 .../schema/types/PUnsignedTimeArray.java        |  132 +-
 .../schema/types/PUnsignedTimestampArray.java   |  134 +-
 .../schema/types/PUnsignedTinyintArray.java     |  132 +-
 .../phoenix/schema/types/PVarbinaryArray.java   |  130 +-
 .../phoenix/schema/types/PVarcharArray.java     |  130 +-
 .../java/org/apache/phoenix/util/ByteUtil.java  |   12 +-
 .../org/apache/phoenix/util/MetaDataUtil.java   |    2 +-
 .../org/apache/phoenix/util/PhoenixRuntime.java |  154 +-
 .../java/org/apache/phoenix/util/ScanUtil.java  |   15 +-
 .../org/apache/phoenix/util/SchemaUtil.java     |   23 +
 .../java/org/apache/phoenix/util/TupleUtil.java |    7 +-
 .../org/apache/phoenix/util/UpgradeUtil.java    |  301 +++
 .../phoenix/compile/QueryCompilerTest.java      |   35 +-
 .../org/apache/phoenix/query/OrderByTest.java   |   84 +
 .../phoenix/schema/types/PDataTypeTest.java     |   12 +
 phoenix-protocol/src/main/PTable.proto          |    1 +
 84 files changed, 4997 insertions(+), 4027 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/dev/eclipse_prefs_phoenix.epf
----------------------------------------------------------------------
diff --git a/dev/eclipse_prefs_phoenix.epf b/dev/eclipse_prefs_phoenix.epf
index a218c97..65881ef 100644
--- a/dev/eclipse_prefs_phoenix.epf
+++ b/dev/eclipse_prefs_phoenix.epf
@@ -512,7 +512,7 @@ file_export_version=3.0
 /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
 /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
 /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
-/instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
+/instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
 /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error
 /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
 /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayIT.java
index 27887e4..89997f4 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayIT.java
@@ -1980,4 +1980,63 @@ public class ArrayIT extends BaseClientManagedTimeIT {
         assertEquals(arr, rs.getArray(1));
         rs.next();
     }
+    
+    @Test
+    public void testPKWithDescArray() throws Exception {
+        Connection conn;
+        PreparedStatement stmt;
+        ResultSet rs;
+        long ts = nextTimestamp();
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 10));
+        conn = DriverManager.getConnection(getUrl(), props);
+        conn.createStatement()
+                .execute(
+                        "CREATE TABLE t ( a VARCHAR ARRAY PRIMARY KEY DESC)\n");
+        conn.close();
+        
+        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 30));
+        conn = DriverManager.getConnection(getUrl(), props);
+        stmt = conn.prepareStatement("UPSERT INTO t VALUES(?)");
+        Array a1 = conn.createArrayOf("VARCHAR", new String[] { "a", "ba" });
+        stmt.setArray(1, a1);
+        stmt.execute();
+        Array a2 = conn.createArrayOf("VARCHAR", new String[] { "a", "c" });
+        stmt.setArray(1, a2);
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 40));
+        conn = DriverManager.getConnection(getUrl(), props);
+        rs = conn.createStatement().executeQuery("SELECT a FROM t ORDER BY a DESC");
+        assertTrue(rs.next());
+        assertEquals(a2, rs.getArray(1));
+        assertTrue(rs.next());
+        assertEquals(a1, rs.getArray(1));
+        assertFalse(rs.next());
+        conn.close();
+        
+        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 50));
+        conn = DriverManager.getConnection(getUrl(), props);
+        stmt = conn.prepareStatement("UPSERT INTO t VALUES(?)");
+        Array a3 = conn.createArrayOf("VARCHAR", new String[] { "a", "b" });
+        stmt.setArray(1, a3);
+        stmt.execute();
+        conn.commit();
+        conn.close();
+
+        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 60));
+        conn = DriverManager.getConnection(getUrl(), props);
+        rs = conn.createStatement().executeQuery("SELECT a FROM t ORDER BY a DESC");
+        assertTrue(rs.next());
+        assertEquals(a2, rs.getArray(1));
+        assertTrue(rs.next());
+        assertEquals(a1, rs.getArray(1));
+        assertTrue(rs.next());
+        assertEquals(a3, rs.getArray(1));
+        assertFalse(rs.next());
+        conn.close();
+    }
+    
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/it/java/org/apache/phoenix/end2end/IsNullIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/IsNullIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/IsNullIT.java
index 9c60914..fb49378 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/IsNullIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/IsNullIT.java
@@ -19,26 +19,22 @@ package org.apache.phoenix.end2end;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import java.sql.Connection;
 import java.sql.DriverManager;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
-import java.util.Properties;
 
-import org.apache.phoenix.util.PhoenixRuntime;
 import org.junit.Test;
 
 
-public class IsNullIT extends BaseClientManagedTimeIT {
+public class IsNullIT extends BaseHBaseManagedTimeIT {
     @Test
     public void testIsNullInPk() throws Exception {
-        long ts = nextTimestamp();
-        ensureTableCreated(getUrl(),"IntIntKeyTest",null, ts-2);
-        Properties props = new Properties();
-        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 1)); // Execute at timestamp 1
-        Connection conn = DriverManager.getConnection(getUrl(), props);
+        ensureTableCreated(getUrl(),"IntIntKeyTest");
+        Connection conn = DriverManager.getConnection(getUrl());
         String upsert = "UPSERT INTO IntIntKeyTest VALUES(4,2)";
         PreparedStatement upsertStmt = conn.prepareStatement(upsert);
         int rowsInserted = upsertStmt.executeUpdate();
@@ -48,10 +44,7 @@ public class IsNullIT extends BaseClientManagedTimeIT {
         rowsInserted = upsertStmt.executeUpdate();
         assertEquals(1, rowsInserted);
         conn.commit();
-        conn.close();
         
-        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 2)); // Execute at timestamp 1
-        conn = DriverManager.getConnection(getUrl(), props);
         String select = "SELECT i/j FROM IntIntKeyTest WHERE j IS NULL";
         ResultSet rs;
         rs = conn.createStatement().executeQuery(select);
@@ -67,23 +60,38 @@ public class IsNullIT extends BaseClientManagedTimeIT {
     }
     
     @Test
+    public void testIsNullWithLastPKColDesc() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        conn.createStatement().execute("CREATE TABLE T(k1 VARCHAR NOT NULL, k2 VARCHAR, k3 VARCHAR, CONSTRAINT pk PRIMARY KEY (k1, k2, k3 DESC))");
+        conn.createStatement().execute("UPSERT INTO T VALUES ('a')");
+        conn.createStatement().execute("UPSERT INTO T VALUES ('b')");
+        conn.createStatement().execute("UPSERT INTO T VALUES ('b',null,'c')");
+        conn.createStatement().execute("UPSERT INTO T VALUES ('ba', null, 'd')");
+        conn.commit();
+        
+        ResultSet rs = conn.createStatement().executeQuery("SELECT k1,k2,k3 FROM T WHERE k1='b' AND k2 IS NULL");
+        assertTrue(rs.next());
+        assertEquals("b",rs.getString(1));
+        assertNull(rs.getString(2));
+        assertNull(rs.getString(3));
+
+        assertTrue(rs.next());
+        assertEquals("b",rs.getString(1));
+        assertNull(rs.getString(2));
+        assertEquals("c",rs.getString(3));
+        
+        assertFalse(rs.next());
+        conn.close();
+    }
+    
+    @Test
     public void testIsNullInCompositeKey() throws Exception {
-        long ts = nextTimestamp();
-        Properties props = new Properties();
-        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 10));
-        Connection conn = DriverManager.getConnection(getUrl(), props);
+        Connection conn = DriverManager.getConnection(getUrl());
         conn.createStatement().execute("CREATE TABLE T(k1 VARCHAR, k2 VARCHAR, CONSTRAINT pk PRIMARY KEY (k1,k2))");
-        conn.close();
-        
-        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 20));
-        conn = DriverManager.getConnection(getUrl(), props);
         conn.createStatement().execute("UPSERT INTO T VALUES (null,'a')");
         conn.createStatement().execute("UPSERT INTO T VALUES ('a','a')");
         conn.commit();
-        conn.close();
         
-        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 30));
-        conn = DriverManager.getConnection(getUrl(), props);
         ResultSet rs = conn.createStatement().executeQuery("SELECT count(*) FROM T");
         assertTrue(rs.next());
         assertEquals(2,rs.getInt(1));

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/it/java/org/apache/phoenix/end2end/LpadFunctionIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/LpadFunctionIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/LpadFunctionIT.java
index 0a9bedf..4aa66c2 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/LpadFunctionIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/LpadFunctionIT.java
@@ -28,6 +28,7 @@ import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.phoenix.util.TestUtil;
+import org.junit.Ignore;
 import org.junit.Test;
 
 import com.google.common.collect.Lists;
@@ -106,6 +107,29 @@ public class LpadFunctionIT extends BaseHBaseManagedTimeIT {
         testLpad(conn, inputList, length, fillStringList, "pk", expectedOutputList);
     }
 
+    @Ignore
+    @Test
+    public void testCharPadding() throws Exception {
+        ResultSet rs;
+        Connection conn = DriverManager.getConnection(getUrl());
+
+        conn.createStatement().execute("CREATE TABLE tdesc (k CHAR(3) PRIMARY KEY DESC)");
+        conn.createStatement().execute("UPSERT INTO tdesc VALUES('a')");
+        conn.commit();
+        rs = conn.createStatement().executeQuery("SELECT * FROM tdesc");
+        assertTrue(rs.next());
+        assertEquals("a", rs.getString(1));
+        assertFalse(rs.next());
+        
+        conn.createStatement().execute("CREATE TABLE t (k CHAR(3) PRIMARY KEY)");
+        conn.createStatement().execute("UPSERT INTO t VALUES('a')");
+        conn.commit();
+        rs = conn.createStatement().executeQuery("SELECT * FROM t");
+        assertTrue(rs.next());
+        assertEquals("a", rs.getString(1));
+        assertFalse(rs.next());
+    }
+
     @Test
     public void testNullInputStringSB() throws Exception {
         Connection conn = DriverManager.getConnection(getUrl());

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/it/java/org/apache/phoenix/end2end/ReverseScanIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ReverseScanIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ReverseScanIT.java
index eca183b..35a8025 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ReverseScanIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ReverseScanIT.java
@@ -53,6 +53,8 @@ import com.google.common.collect.Maps;
 
 
 public class ReverseScanIT extends BaseHBaseManagedTimeIT {
+    protected static final String ATABLE_INDEX_NAME = "ATABLE_IDX";
+
     @BeforeClass
     @Shadower(classBeingShadowed = BaseHBaseManagedTimeIT.class)
     public static void doSetup() throws Exception {
@@ -165,4 +167,32 @@ public class ReverseScanIT extends BaseHBaseManagedTimeIT {
         }
     }
 
+    @Test
+    public void testReverseScanIndex() throws Exception {
+        String tenantId = getOrganizationId();
+        initATableValues(tenantId, getSplitsAtRowKeys(tenantId), getUrl());
+        
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        String ddl = "CREATE INDEX " + ATABLE_INDEX_NAME + " ON aTable (a_integer DESC) INCLUDE ("
+        + "    A_STRING, " + "    B_STRING, " + "    A_DATE)";
+        conn.createStatement().execute(ddl);
+        
+        String query = 
+                "SELECT a_integer FROM atable where a_integer is not null order by a_integer nulls last limit 1";
+
+        PreparedStatement statement = conn.prepareStatement(query);
+        ResultSet rs=statement.executeQuery();
+        assertTrue(rs.next());
+        assertEquals(1,rs.getInt(1));
+        assertFalse(rs.next());
+        
+        rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+        assertEquals(
+                "CLIENT SERIAL 1-WAY REVERSE RANGE SCAN OVER ATABLE_IDX [not null]\n" + 
+                "    SERVER FILTER BY FIRST KEY ONLY\n" + 
+                "    SERVER 1 ROW LIMIT\n" + 
+                "CLIENT 1 ROW LIMIT",QueryUtil.getExplainPlan(rs));
+    }
+    
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java
index b9e50a4..e5cfeb0 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java
@@ -1232,8 +1232,7 @@ public class RowValueConstructorIT extends BaseClientManagedTimeIT {
     @Test
     public void testForceSkipScan() throws Exception {
         String tempTableWithCompositePK = "TEMP_TABLE_COMPOSITE_PK";
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
+        Connection conn = nextConnection(getUrl());
         try {
             conn.createStatement().execute("CREATE TABLE " + tempTableWithCompositePK
                     + "   (col0 INTEGER NOT NULL, "
@@ -1242,7 +1241,9 @@ public class RowValueConstructorIT extends BaseClientManagedTimeIT {
                     + "    col3 INTEGER "
                     + "   CONSTRAINT pk PRIMARY KEY (col0, col1, col2)) "
                     + "   SALT_BUCKETS=4");
+            conn.close();
 
+            conn = nextConnection(getUrl());
             PreparedStatement upsertStmt = conn.prepareStatement(
                     "upsert into " + tempTableWithCompositePK + "(col0, col1, col2, col3) " + "values (?, ?, ?, ?)");
             for (int i = 0; i < 3; i++) {
@@ -1253,7 +1254,9 @@ public class RowValueConstructorIT extends BaseClientManagedTimeIT {
                 upsertStmt.execute();
             }
             conn.commit();
+            conn.close();
 
+            conn = nextConnection(getUrl());
             String query = "SELECT * FROM " + tempTableWithCompositePK + " WHERE (col0, col1) in ((2, 3), (3, 4), (4, 5))";
             PreparedStatement statement = conn.prepareStatement(query);
             ResultSet rs = statement.executeQuery();

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/it/java/org/apache/phoenix/end2end/SortOrderFIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SortOrderFIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SortOrderFIT.java
deleted file mode 100644
index 9fb7ea3..0000000
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SortOrderFIT.java
+++ /dev/null
@@ -1,563 +0,0 @@
-/*
- * 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.end2end;
-
-import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
-
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.sql.Connection;
-import java.sql.Date;
-import java.sql.DriverManager;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.util.Calendar;
-import java.util.GregorianCalendar;
-import java.util.List;
-import java.util.Properties;
-
-import org.apache.commons.lang.ArrayUtils;
-import org.apache.phoenix.util.PropertiesUtil;
-import org.junit.Assert;
-import org.junit.Test;
-
-import com.google.common.collect.Lists;
-
-/**
- * @since 1.2
- */
-
-
-public class SortOrderFIT extends BaseHBaseManagedTimeIT {
-    
-    private static final String TABLE = "DescColumnSortOrderTest";
-
-    @Test
-    public void noOrder() throws Exception {
-        String ddl = "CREATE TABLE " + TABLE + " (pk VARCHAR NOT NULL PRIMARY KEY)";
-        runQueryTest(ddl, "pk", new Object[][]{{"a"}, {"b"}, {"c"}}, new Object[][]{{"a"}, {"b"}, {"c"}});
-    }                                                           
-
-    @Test
-    public void noOrderCompositePK() throws Exception {
-        String ddl = "CREATE TABLE " + TABLE + " (oid CHAR(2) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid, code))";
-        Object[][] rows = new Object[][]{{"o1", 1}, {"o2", 2}, {"o3", 3}};
-        runQueryTest(ddl, upsert("oid", "code"), rows, rows);
-    }
-    
-    @Test
-    public void ascOrderInlinePK() throws Exception {
-        String ddl = "CREATE TABLE " + TABLE + " (pk VARCHAR NOT NULL PRIMARY KEY ASC)";
-        runQueryTest(ddl, "pk", new Object[][]{{"a"}, {"b"}, {"c"}}, new Object[][]{{"a"}, {"b"}, {"c"}});
-    }
-    
-    @Test
-    public void ascOrderCompositePK() throws Exception {
-        String ddl = "CREATE TABLE " + TABLE + " (oid CHAR(2) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid ASC, code DESC))";
-        Object[][] insertedRows = new Object[][]{{"o1", 1}, {"o1", 2}, {"o1", 3}};
-        Object[][] expectedRows = new Object[][]{{"o1", 3}, {"o1", 2}, {"o1", 1}};
-        runQueryTest(ddl, upsert("oid", "code"), insertedRows, expectedRows);        
-    }
-
-    @Test
-    public void descOrderInlinePK() throws Exception {
-        for (String type : new String[]{"CHAR(2)", "VARCHAR"}) {
-            String ddl = "CREATE TABLE " + TABLE + " (pk ${type} NOT NULL PRIMARY KEY DESC)".replace("${type}", type);
-            runQueryTest(ddl, "pk", new Object[][]{{"aa"}, {"bb"}, {"cc"}}, new Object[][]{{"cc"}, {"bb"}, {"aa"}});
-        }
-    }
-    
-    @Test
-    public void descOrderCompositePK1() throws Exception {
-        String ddl = "CREATE TABLE " + TABLE + " (oid CHAR(2) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code))";
-        Object[][] insertedRows = new Object[][]{{"o1", 1}, {"o2", 2}, {"o3", 3}};
-        Object[][] expectedRows = new Object[][]{{"o3", 3}, {"o2", 2}, {"o1", 1}};
-        runQueryTest(ddl, upsert("oid", "code"), insertedRows, expectedRows);        
-    }
-    
-    @Test
-    public void descOrderCompositePK2() throws Exception {
-        String ddl = "CREATE TABLE " + TABLE + " (oid CHAR(2) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code DESC))";
-        Object[][] insertedRows = new Object[][]{{"o1", 1}, {"o1", 2}, {"o1", 3}};
-        Object[][] expectedRows = new Object[][]{{"o1", 3}, {"o1", 2}, {"o1", 1}};
-        runQueryTest(ddl, upsert("oid", "code"), insertedRows, expectedRows);        
-    }    
-
-    @Test
-    public void equalityDescInlinePK() throws Exception {
-        String ddl = "CREATE TABLE " + TABLE + " (pk VARCHAR NOT NULL PRIMARY KEY DESC)";
-        runQueryTest(ddl, upsert("pk"), new Object[][]{{"a"}, {"b"}, {"c"}}, new Object[][]{{"b"}}, new WhereCondition("pk", "=", "'b'"));
-    }
-    
-    @Test
-    public void equalityDescCompositePK1() throws Exception {
-        String ddl = "CREATE TABLE " + TABLE + " (oid CHAR(2) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code DESC))";
-        Object[][] insertedRows = new Object[][]{{"o1", 1}, {"o2", 2}, {"o3", 3}};
-        runQueryTest(ddl, upsert("oid", "code"), insertedRows, new Object[][]{{"o2", 2}}, new WhereCondition("oid", "=", "'o2'"));        
-    }
-    
-    @Test
-    public void equalityDescCompositePK2() throws Exception {
-        String ddl = "CREATE TABLE " + TABLE + " (oid CHAR(2) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code DESC))";
-        Object[][] insertedRows = new Object[][]{{"o1", 1}, {"o1", 2}, {"o1", 3}};
-        runQueryTest(ddl, upsert("oid", "code"), insertedRows, new Object[][]{{"o1", 2}}, new WhereCondition("code", "=", "2"));        
-    }
-    
-    @Test
-    public void inDescCompositePK1() throws Exception {
-        String ddl = "CREATE TABLE " + TABLE + " (oid CHAR(2) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code DESC))";
-        Object[][] insertedRows = new Object[][]{{"o1", 1}, {"o1", 2}, {"o1", 3}};
-        runQueryTest(ddl, upsert("oid", "code"), insertedRows, new Object[][]{{"o1", 2}}, new WhereCondition("code", "IN", "(2)"));        
-    }
-    
-    @Test
-    public void inDescCompositePK2() throws Exception {
-        String ddl = "CREATE TABLE " + TABLE + " (oid CHAR(2) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code DESC))";
-        Object[][] insertedRows = new Object[][]{{"o1", 1}, {"o2", 2}, {"o3", 3}};
-        runQueryTest(ddl, upsert("oid", "code"), insertedRows, new Object[][]{{"o2", 2}}, new WhereCondition("oid", "IN", "('o2')"));        
-    }
-    
-    @Test
-    public void likeDescCompositePK1() throws Exception {
-        String ddl = "CREATE TABLE " + TABLE + " (oid CHAR(2) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code DESC))";
-        Object[][] insertedRows = new Object[][]{{"a1", 1}, {"b2", 2}, {"c3", 3}};
-        runQueryTest(ddl, upsert("oid", "code"), insertedRows, new Object[][]{{"b2", 2}}, new WhereCondition("oid", "LIKE", "('b%')"));        
-    }
-    
-    @Test
-    public void likeDescCompositePK2() throws Exception {
-        String ddl = "CREATE TABLE " + TABLE + " (oid CHAR(2) NOT NULL, code CHAR(2) NOT NULL constraint pk primary key (oid DESC, code DESC))";
-        Object[][] insertedRows = new Object[][]{{"a1", "11"}, {"b2", "22"}, {"c3", "33"}};
-        runQueryTest(ddl, upsert("oid", "code"), insertedRows, new Object[][]{{"b2", "22"}}, new WhereCondition("code", "LIKE", "('2%')"));        
-    }
-    
-    @Test
-    public void greaterThanDescCompositePK3() throws Exception {
-        String ddl = "CREATE TABLE " + TABLE + " (oid CHAR(2) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code DESC))";
-        Object[][] insertedRows = new Object[][]{{"o1", 1}, {"o1", 2}, {"o1", 3}};
-        Object[][] expectedRows = new Object[][]{{"o1", 2}, {"o1", 1}};
-        runQueryTest(ddl, upsert("oid", "code"), insertedRows, expectedRows, new WhereCondition("code", "<", "3"));        
-    }
-    
-    @Test
-    public void substrDescCompositePK1() throws Exception {
-        String ddl = "CREATE TABLE " + TABLE + " (oid CHAR(3) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code ASC))";
-        Object[][] insertedRows = new Object[][]{{"ao1", 1}, {"bo2", 2}, {"co3", 3}};
-        Object[][] expectedRows = new Object[][]{{"co3", 3}, {"bo2", 2}};
-        runQueryTest(ddl, upsert("oid", "code"), insertedRows, expectedRows, new WhereCondition("SUBSTR(oid, 3, 1)", ">", "'1'"));
-    }
-        
-    @Test
-    public void substrDescCompositePK2() throws Exception {
-        String ddl = "CREATE TABLE " + TABLE + " (oid CHAR(4) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code ASC))";
-        Object[][] insertedRows = new Object[][]{{"aaaa", 1}, {"bbbb", 2}, {"cccd", 3}};
-        Object[][] expectedRows = new Object[][]{{"cccd", 3}};
-        runQueryTest(ddl, upsert("oid", "code"), insertedRows, expectedRows, new WhereCondition("SUBSTR(oid, 4, 1)", "=", "'d'"));
-    }    
-    
-    @Test
-    public void lTrimDescCompositePK() throws Exception {
-        String ddl = "CREATE TABLE " + TABLE + " (oid VARCHAR NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code DESC))";
-        Object[][] insertedRows = new Object[][]{{" o1 ", 1}, {"  o2", 2}, {"  o3", 3}};
-        Object[][] expectedRows = new Object[][]{{"  o2", 2}};
-        runQueryTest(ddl, upsert("oid", "code"), insertedRows, expectedRows, new WhereCondition("LTRIM(oid)", "=", "'o2'"));
-    }
-    
-    @Test
-    public void lPadDescCompositePK() throws Exception {
-        String ddl = "CREATE TABLE " + TABLE + " (oid VARCHAR NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code DESC))";
-        Object[][] insertedRows = new Object[][]{{"aaaa", 1}, {"bbbb", 2}, {"cccc", 3}};
-        Object[][] expectedRows = new Object[][]{{"bbbb", 2}};
-        runQueryTest(ddl, upsert("oid", "code"), insertedRows, expectedRows, new WhereCondition("LPAD(oid, 8, '123')", "=", "'1231bbbb'"));
-    }
-
-    @Test
-    public void countDescCompositePK() throws Exception {
-        String ddl = "CREATE TABLE " + TABLE + " (oid CHAR(2) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code ASC))";
-        Object[][] insertedRows = new Object[][]{{"o1", 1}, {"o2", 2}, {"o3", 3}};
-        Object[][] expectedRows = new Object[][]{{3l}};
-        runQueryTest(ddl, upsert("oid", "code"), select("COUNT(oid)"), insertedRows, expectedRows);
-    }
-    
-    @Test
-    public void sumDescCompositePK() throws Exception {
-        String ddl = "CREATE TABLE " + TABLE + " (n1 INTEGER NOT NULL, n2 DECIMAL(10, 2) NOT NULL, n3 BIGINT NOT NULL " + 
-            "constraint pk primary key (n1 DESC, n2 DESC, n3 DESC))";
-        Object[][] insertedRows = new Object[][]{{10, bdec(10.2), 21l}, {20, bdec(20.2), 32l}, {30, bdec(30.2), 43l}};
-        Object[][] expectedRows = new Object[][]{{60l, bdec(60.6), 96l}};
-        runQueryTest(ddl, upsert("n1", "n2", "n3"), select("SUM(n1), SUM(n2), SUM(n3)"), insertedRows, expectedRows);
-    }    
-    
-    @Test
-    public void avgDescCompositePK() throws Exception {
-        String ddl = "CREATE TABLE " + TABLE + " (n1 INTEGER NOT NULL, n2 DECIMAL(10, 2) NOT NULL, n3 BIGINT NOT NULL " + 
-            "constraint pk primary key (n1 DESC, n2 DESC, n3 DESC))";
-        Object[][] insertedRows = new Object[][]{{10, bdec(10.2), 21l}, {20, bdec(20.2), 32l}, {30, bdec(30.2), 43l}};
-        Object[][] expectedRows = new Object[][]{{new BigDecimal(bint(2), -1), bdec(20.2), BigDecimal.valueOf(32)}};
-        runQueryTest(ddl, upsert("n1", "n2", "n3"), select("AVG(n1), AVG(n2), AVG(n3)"), insertedRows, expectedRows);
-    }
-    
-    @Test
-    public void minDescCompositePK() throws Exception {
-        String ddl = "CREATE TABLE " + TABLE + " (n1 INTEGER NOT NULL, n2 DECIMAL(10, 2) NOT NULL, n3 BIGINT NOT NULL " + 
-            "constraint pk primary key (n1 DESC, n2 DESC, n3 DESC))";
-        Object[][] insertedRows = new Object[][]{{10, bdec(10.2), 21l}, {20, bdec(20.2), 32l}, {30, bdec(30.2), 43l}};
-        Object[][] expectedRows = new Object[][]{{10, bdec(10.2), 21l}};
-        runQueryTest(ddl, upsert("n1", "n2", "n3"), select("MIN(n1), MIN(n2), MIN(n3)"), insertedRows, expectedRows);
-    }
-    
-    @Test
-    public void maxDescCompositePK() throws Exception {
-        String ddl = "CREATE TABLE " + TABLE + " (n1 INTEGER NOT NULL, n2 DECIMAL(10, 2) NOT NULL, n3 BIGINT NOT NULL " + 
-            "constraint pk primary key (n1 DESC, n2 DESC, n3 DESC))";
-        Object[][] insertedRows = new Object[][]{{10, bdec(10.2), 21l}, {20, bdec(20.2), 32l}, {30, bdec(30.2), 43l}};
-        Object[][] expectedRows = new Object[][]{{30, bdec(30.2), 43l}};
-        runQueryTest(ddl, upsert("n1", "n2", "n3"), select("MAX(n1), MAX(n2), MAX(n3)"), insertedRows, expectedRows);
-    }
-    
-    @Test
-    public void havingSumDescCompositePK() throws Exception {
-        String ddl = "CREATE TABLE " + TABLE + " (name CHAR(1) NOT NULL, code INTEGER NOT NULL " + 
-            "constraint pk primary key (name DESC, code DESC))";
-        Object[][] insertedRows = new Object[][]{{"a", 10}, {"a", 20}, {"b", 100}}; 
-        Object[][] expectedRows = new Object[][]{{"a", 30l}};
-        runQueryTest(ddl, upsert("name", "code"), select("name", "SUM(code)"), insertedRows, expectedRows, 
-            new HavingCondition("name", "SUM(code) = 30"));
-    }
-    
-    @Test
-    public void queryDescDateWithExplicitOrderBy() throws Exception {
-        String ddl = "CREATE TABLE " + TABLE + " (c1 CHAR(1) NOT NULL, c2 CHAR(1) NOT NULL, d1 DATE NOT NULL, c3 CHAR(1) NOT NULL " + 
-            "constraint pk primary key (c1, c2, d1 DESC, c3))";
-        Object[] row1 = {"1", "2", date(10, 11, 2001), "3"};
-        Object[] row2 = {"1", "2", date(10, 11, 2003), "3"};
-        Object[][] insertedRows = new Object[][]{row1, row2};
-        runQueryTest(ddl, upsert("c1", "c2", "d1", "c3"), select("c1, c2, d1", "c3"), insertedRows, new Object[][]{row2, row1},
-            null, null, new OrderBy("d1", OrderBy.Direction.DESC));
-    }    
-    
-    @Test
-    public void additionOnDescCompositePK() throws Exception {
-        String ddl = "CREATE TABLE " + TABLE + " (n1 INTEGER NOT NULL, n2 DECIMAL(10, 2) NOT NULL, n3 BIGINT NOT NULL, d1 DATE NOT NULL " + 
-            "constraint pk primary key (n1 DESC, n2 DESC, n3 DESC, d1 DESC))";
-        Object[][] insertedRows = new Object[][]{
-            {10, bdec(10.2), 21l, date(1, 10, 2001)}, {20, bdec(20.2), 32l, date(2, 6, 2001)}, {30, bdec(30.2), 43l, date(3, 1, 2001)}};
-        Object[][] expectedRows = new Object[][]{
-            {31l, bdec(32.2), 46l, date(3, 5, 2001)}, {21l, bdec(22.2), 35l, date(2, 10, 2001)}, {11l, bdec(12.2), 24l, date(1, 14, 2001)}};
-        runQueryTest(ddl, upsert("n1", "n2", "n3", "d1"), select("n1+1, n2+2, n3+3", "d1+4"), insertedRows, expectedRows);
-    }
-    
-    @Test
-    public void subtractionOnDescCompositePK() throws Exception {
-        String ddl = "CREATE TABLE " + TABLE + " (n1 INTEGER NOT NULL, n2 DECIMAL(10, 2) NOT NULL, n3 BIGINT NOT NULL, d1 DATE NOT NULL " + 
-            "constraint pk primary key (n1 DESC, n2 DESC, n3 DESC, d1 DESC))";
-        Object[][] insertedRows = new Object[][]{
-            {10, bdec(10.2), 21l, date(1, 10, 2001)}, {20, bdec(20.2), 32l, date(2, 6, 2001)}, {30, bdec(30.2), 43l, date(3, 10, 2001)}};
-        Object[][] expectedRows = new Object[][]{
-            {29l, bdec(28.2), 40l, date(3, 6, 2001)}, {19l, bdec(18.2), 29l, date(2, 2, 2001)}, {9l, bdec(8.2), 18l, date(1, 6, 2001)}};
-        runQueryTest(ddl, upsert("n1", "n2", "n3", "d1"), select("n1-1, n2-2, n3-3", "d1-4"), insertedRows, expectedRows);
-    }
-    
-    @Test
-    public void lessThanLeadingDescCompositePK() throws Exception {
-        String ddl = "CREATE TABLE " + TABLE + " (id INTEGER NOT NULL, date DATE NOT NULL constraint pk primary key (id DESC, date))";
-        Object[][] insertedRows = new Object[][]{{1, date(1, 1, 2012)}, {3, date(1, 1, 2013)}, {2, date(1, 1, 2011)}};
-        Object[][] expectedRows = new Object[][]{{1, date(1, 1, 2012)}};
-        runQueryTest(ddl, upsert("id", "date"), insertedRows, expectedRows, new WhereCondition("id", "<", "2"));
-    }
-    
-    @Test
-    public void lessThanTrailingDescCompositePK() throws Exception {
-        String ddl = "CREATE TABLE " + TABLE + " (id INTEGER NOT NULL, date DATE NOT NULL constraint pk primary key (id DESC, date))";
-        Object[][] insertedRows = new Object[][]{{1, date(1, 1, 2002)}, {3, date(1, 1, 2003)}, {2, date(1, 1, 2001)}};
-        Object[][] expectedRows = new Object[][]{{2, date(1, 1, 2001)}};
-        runQueryTest(ddl, upsert("id", "date"), insertedRows, expectedRows, new WhereCondition("date", "<", "TO_DATE('02-02-2001','mm-dd-yyyy')"));
-    }
-    
-    private void runQueryTest(String ddl, String columnName, Object[][] rows, Object[][] expectedRows) throws Exception {
-        runQueryTest(ddl, new String[]{columnName}, rows, expectedRows, null);
-    }
-    
-    private void runQueryTest(String ddl, String[] columnNames, Object[][] rows, Object[][] expectedRows) throws Exception {
-        runQueryTest(ddl, columnNames, rows, expectedRows, null);
-    }
-    
-    private void runQueryTest(String ddl, String[] columnNames, Object[][] rows, Object[][] expectedRows, WhereCondition condition) throws Exception {
-        runQueryTest(ddl, columnNames, columnNames, rows, expectedRows, condition, null, null);
-    }
-    
-    private void runQueryTest(String ddl, String[] columnNames, String[] projections, Object[][] rows, Object[][] expectedRows) throws Exception {
-        runQueryTest(ddl, columnNames, projections, rows, expectedRows, null, null, null);
-    }
-    
-    private void runQueryTest(String ddl, String[] columnNames, String[] projections, Object[][] rows, Object[][] expectedRows, HavingCondition havingCondition) throws Exception {
-        runQueryTest(ddl, columnNames, projections, rows, expectedRows, null, havingCondition, null);
-    }
-    
-
-    private void runQueryTest(
-        String ddl, 
-        String[] columnNames, 
-        String[] projections, 
-        Object[][] rows, Object[][] expectedRows, 
-        WhereCondition whereCondition, 
-        HavingCondition havingCondition,
-        OrderBy orderBy) 
-        throws Exception 
-    {
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        Connection conn = DriverManager.getConnection(getUrl(), props);
-
-        try {
-
-            conn.setAutoCommit(false);
-
-            createTestTable(getUrl(), ddl);
-
-            String columns = appendColumns(columnNames);
-            String placeholders = appendPlaceholders(columnNames);
-            String dml = "UPSERT INTO " + TABLE + " (" + columns + ") VALUES(" + placeholders +")";
-            PreparedStatement stmt = conn.prepareStatement(dml);
-
-            for (int row = 0; row < rows.length; row++) {
-                for (int col = 0; col < rows[row].length; col++) {
-                    Object value = rows[row][col];
-                    stmt.setObject(col + 1, value);
-                }
-                stmt.execute();
-            }
-            conn.commit();
-            
-            String selectClause = "SELECT " + appendColumns(projections) + " FROM " + TABLE;
-            
-            for (WhereCondition whereConditionClause : new WhereCondition[]{whereCondition, WhereCondition.reverse(whereCondition)}) {
-                String query = WhereCondition.appendWhere(whereConditionClause, selectClause);
-                query = HavingCondition.appendHaving(havingCondition, query);
-                query = OrderBy.appendOrderBy(orderBy, query);
-                runQuery(conn, query, expectedRows);
-            }
-            
-            if (orderBy != null) {
-                orderBy = OrderBy.reverse(orderBy);
-                String query = WhereCondition.appendWhere(whereCondition, selectClause);
-                query = HavingCondition.appendHaving(havingCondition, query);
-                query = OrderBy.appendOrderBy(orderBy, query);
-                runQuery(conn, query, reverse(expectedRows));
-            }
-            
-        } finally {
-            conn.close();
-        }
-    }
-    
-    private String appendColumns(String[] columnNames) {
-        String appendedColumns = "";
-        for (int i = 0; i < columnNames.length; i++) {                
-            appendedColumns += columnNames[i];
-            if (i < columnNames.length - 1) {
-                appendedColumns += ",";
-            }
-        }
-        return appendedColumns;
-    }
-    
-    private String appendPlaceholders(String[] columnNames) {
-        String placeholderList = "";
-        for (int i = 0; i < columnNames.length; i++) {                
-            placeholderList += "?";
-            if (i < columnNames.length - 1) {
-                placeholderList += ",";
-            }
-        }
-        return placeholderList;        
-    }
-    
-    private static void runQuery(Connection connection, String query, Object[][] expectedValues) throws Exception {
-        PreparedStatement stmt = connection.prepareStatement(query);
-
-        ResultSet rs = stmt.executeQuery();
-        int rowCounter = 0;
-        while (rs.next()) {
-            if (rowCounter == expectedValues.length) {
-                Assert.assertEquals("Exceeded number of expected rows for query" + query, expectedValues.length, rowCounter+1);
-            }
-            Object[] cols = new Object[expectedValues[rowCounter].length];
-            for (int colCounter = 0; colCounter < expectedValues[rowCounter].length; colCounter++) {
-                cols[colCounter] = rs.getObject(colCounter+1);
-            }
-            Assert.assertArrayEquals("Unexpected result for query " + query, expectedValues[rowCounter], cols);
-            rowCounter++;
-        }
-        Assert.assertEquals("Unexpected number of rows for query " + query, expectedValues.length, rowCounter);
-    }
-    
-    private static Object[][] reverse(Object[][] rows) {
-        Object[][] reversedArray = new Object[rows.length][];
-        System.arraycopy(rows, 0, reversedArray, 0, rows.length);
-        ArrayUtils.reverse(reversedArray);
-        return reversedArray;
-    }
-    
-    private static Date date(int month, int day, int year) {
-        Calendar cal = new GregorianCalendar();
-        cal.set(Calendar.MONTH, month-1);
-        cal.set(Calendar.DAY_OF_MONTH, day);
-        cal.set(Calendar.YEAR, year);
-        cal.set(Calendar.HOUR_OF_DAY, 10);
-        cal.set(Calendar.MINUTE, 2);
-        cal.set(Calendar.SECOND, 5);
-        cal.set(Calendar.MILLISECOND, 101);
-        Date d = new Date(cal.getTimeInMillis()); 
-        return d;
-    }
-        
-    private static String[] upsert(String...args) {
-        return args;
-    }
-    
-    private static String[] select(String...args) {
-        return args;
-    }
-    
-    private static BigDecimal bdec(double d) {
-        return BigDecimal.valueOf(d);
-    }
-    
-    private static BigInteger bint(long l) {
-        return BigInteger.valueOf(l);
-    }    
-    
-    private static class WhereCondition {
-        final String lhs;
-        final String operator;
-        final String rhs;
-    
-        WhereCondition(String lhs, String operator, String rhs) {
-            this.lhs = lhs;
-            this.operator = operator;
-            this.rhs = rhs;
-        }
-        
-        static WhereCondition reverse(WhereCondition whereCondition) {
-            
-            if (whereCondition == null) {
-                return null; 
-            }
-            
-            if (whereCondition.operator.equalsIgnoreCase("IN") || whereCondition.operator.equalsIgnoreCase("LIKE")) {
-                return whereCondition;
-            } else {
-                return new WhereCondition(whereCondition.rhs, whereCondition.getReversedOperator(), whereCondition.lhs);
-            }
-        }
-        
-         static String appendWhere(WhereCondition whereCondition, String query) {
-             if (whereCondition == null) {
-                 return query;
-             }
-            return query + " WHERE " + whereCondition.lhs + " " + whereCondition.operator + " " + whereCondition.rhs;
-        }
-        
-        private String getReversedOperator() {
-            if (operator.equals("<")) {
-                return ">";
-            } else if (operator.equals(">")) {
-                return "<";
-            }
-            return operator;
-        }
-    }
-    
-    private static class HavingCondition {
-        
-        private String groupby;
-        private String having;
-        
-        HavingCondition(String groupby, String having) {
-            this.groupby = groupby;
-            this.having = having;
-        }
-        
-        static String appendHaving(HavingCondition havingCondition, String query) {
-            if (havingCondition == null) {
-                return query;
-            }
-            return query + " GROUP BY " + havingCondition.groupby + " HAVING " + havingCondition.having + " ";
-        }
-    }
-    
-    private static class OrderBy {
-        
-        enum Direction {
-            
-            ASC, DESC;
-            
-            Direction reverse() {
-                if (this == ASC) {
-                    return DESC;
-                }
-                return ASC;
-            }
-        }
-        
-        private List<String> columnNames = Lists.newArrayList();
-        private List<Direction> directions = Lists.newArrayList();
-        
-        OrderBy() {            
-        }
-        
-        OrderBy(String columnName, Direction orderBy) {
-            add(columnName, orderBy);
-        }
-        
-        void add(String columnName, Direction direction) {
-            columnNames.add(columnName);
-            directions.add(direction);
-        }
-        
-        static OrderBy reverse(OrderBy orderBy) {
-            
-            if (orderBy == null) {
-                return null;
-            }
-            
-            List<Direction> reversedDirections = Lists.newArrayList();
-            for (Direction dir : orderBy.directions) {
-                reversedDirections.add(dir.reverse());
-            }
-            OrderBy reversedOrderBy = new OrderBy();
-            reversedOrderBy.columnNames = orderBy.columnNames;
-            reversedOrderBy.directions = reversedDirections;
-            return reversedOrderBy;
-        }
-        
-        static String appendOrderBy(OrderBy orderBy, String query) {
-            if (orderBy == null || orderBy.columnNames.isEmpty()) {
-                return query;
-            }
-            query += " ORDER BY ";
-            for (int i = 0; i < orderBy.columnNames.size(); i++) {
-                query += orderBy.columnNames.get(i) + " " + orderBy.directions.get(i).toString() + " ";
-            }
-            
-            query += " LIMIT 1000 ";
-            
-            return query;
-        }        
-    }
-}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/it/java/org/apache/phoenix/end2end/SortOrderIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SortOrderIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SortOrderIT.java
new file mode 100644
index 0000000..a03f71e
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SortOrderIT.java
@@ -0,0 +1,572 @@
+/*
+ * 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.end2end;
+
+import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.sql.Connection;
+import java.sql.Date;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.commons.lang.ArrayUtils;
+import org.apache.phoenix.util.PropertiesUtil;
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.google.common.collect.Lists;
+
+/**
+ * @since 1.2
+ */
+
+
+public class SortOrderIT extends BaseHBaseManagedTimeIT {
+    
+    private static final String TABLE = "DescColumnSortOrderTest";
+
+    @Test
+    public void noOrder() throws Exception {
+        String ddl = "CREATE TABLE " + TABLE + " (pk VARCHAR NOT NULL PRIMARY KEY)";
+        runQueryTest(ddl, "pk", new Object[][]{{"a"}, {"b"}, {"c"}}, new Object[][]{{"a"}, {"b"}, {"c"}});
+    }                                                           
+
+    @Test
+    public void noOrderCompositePK() throws Exception {
+        String ddl = "CREATE TABLE " + TABLE + " (oid CHAR(2) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid, code))";
+        Object[][] rows = new Object[][]{{"o1", 1}, {"o2", 2}, {"o3", 3}};
+        runQueryTest(ddl, upsert("oid", "code"), rows, rows);
+    }
+    
+    @Test
+    public void ascOrderInlinePK() throws Exception {
+        String ddl = "CREATE TABLE " + TABLE + " (pk VARCHAR NOT NULL PRIMARY KEY ASC)";
+        runQueryTest(ddl, "pk", new Object[][]{{"a"}, {"b"}, {"c"}}, new Object[][]{{"a"}, {"b"}, {"c"}});
+    }
+    
+    @Test
+    public void ascOrderCompositePK() throws Exception {
+        String ddl = "CREATE TABLE " + TABLE + " (oid CHAR(2) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid ASC, code DESC))";
+        Object[][] insertedRows = new Object[][]{{"o1", 1}, {"o1", 2}, {"o1", 3}};
+        Object[][] expectedRows = new Object[][]{{"o1", 3}, {"o1", 2}, {"o1", 1}};
+        runQueryTest(ddl, upsert("oid", "code"), insertedRows, expectedRows);        
+    }
+
+    @Test
+    public void descOrderInlinePK() throws Exception {
+        for (String type : new String[]{"CHAR(2)", "VARCHAR"}) {
+            String ddl = "CREATE TABLE " + TABLE + " (pk ${type} NOT NULL PRIMARY KEY DESC)".replace("${type}", type);
+            runQueryTest(ddl, "pk", new Object[][]{{"aa"}, {"bb"}, {"cc"}}, new Object[][]{{"cc"}, {"bb"}, {"aa"}});
+        }
+    }
+    
+    @Test
+    public void descOrderCompositePK1() throws Exception {
+        String ddl = "CREATE TABLE " + TABLE + " (oid CHAR(2) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code))";
+        Object[][] insertedRows = new Object[][]{{"o1", 1}, {"o2", 2}, {"o3", 3}};
+        Object[][] expectedRows = new Object[][]{{"o3", 3}, {"o2", 2}, {"o1", 1}};
+        runQueryTest(ddl, upsert("oid", "code"), insertedRows, expectedRows);        
+    }
+    
+    @Test
+    public void descOrderCompositePK2() throws Exception {
+        String ddl = "CREATE TABLE " + TABLE + " (oid CHAR(2) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code DESC))";
+        Object[][] insertedRows = new Object[][]{{"o1", 1}, {"o1", 2}, {"o1", 3}};
+        Object[][] expectedRows = new Object[][]{{"o1", 3}, {"o1", 2}, {"o1", 1}};
+        runQueryTest(ddl, upsert("oid", "code"), insertedRows, expectedRows);        
+    }    
+
+    @Test
+    public void equalityDescInlinePK() throws Exception {
+        String ddl = "CREATE TABLE " + TABLE + " (pk VARCHAR NOT NULL PRIMARY KEY DESC)";
+        runQueryTest(ddl, upsert("pk"), new Object[][]{{"a"}, {"b"}, {"c"}}, new Object[][]{{"b"}}, new WhereCondition("pk", "=", "'b'"));
+    }
+    
+    @Test
+    public void equalityDescCompositePK1() throws Exception {
+        String ddl = "CREATE TABLE " + TABLE + " (oid CHAR(2) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code DESC))";
+        Object[][] insertedRows = new Object[][]{{"o1", 1}, {"o2", 2}, {"o3", 3}};
+        runQueryTest(ddl, upsert("oid", "code"), insertedRows, new Object[][]{{"o2", 2}}, new WhereCondition("oid", "=", "'o2'"));        
+    }
+    
+    @Test
+    public void equalityDescCompositePK2() throws Exception {
+        String ddl = "CREATE TABLE " + TABLE + " (oid CHAR(2) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code DESC))";
+        Object[][] insertedRows = new Object[][]{{"o1", 1}, {"o1", 2}, {"o1", 3}};
+        runQueryTest(ddl, upsert("oid", "code"), insertedRows, new Object[][]{{"o1", 2}}, new WhereCondition("code", "=", "2"));        
+    }
+    
+    @Test
+    public void inDescCompositePK1() throws Exception {
+        String ddl = "CREATE TABLE " + TABLE + " (oid CHAR(2) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code DESC))";
+        Object[][] insertedRows = new Object[][]{{"o1", 1}, {"o1", 2}, {"o1", 3}};
+        runQueryTest(ddl, upsert("oid", "code"), insertedRows, new Object[][]{{"o1", 2}}, new WhereCondition("code", "IN", "(2)"));        
+    }
+    
+    @Test
+    public void inDescCompositePK2() throws Exception {
+        String ddl = "CREATE TABLE " + TABLE + " (oid CHAR(2) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code DESC))";
+        Object[][] insertedRows = new Object[][]{{"o1", 1}, {"o2", 2}, {"o3", 3}};
+        runQueryTest(ddl, upsert("oid", "code"), insertedRows, new Object[][]{{"o2", 2}}, new WhereCondition("oid", "IN", "('o2')"));        
+    }
+    
+    @Test
+    public void likeDescCompositePK1() throws Exception {
+        String ddl = "CREATE TABLE " + TABLE + " (oid CHAR(2) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code DESC))";
+        Object[][] insertedRows = new Object[][]{{"a1", 1}, {"b2", 2}, {"c3", 3}};
+        runQueryTest(ddl, upsert("oid", "code"), insertedRows, new Object[][]{{"b2", 2}}, new WhereCondition("oid", "LIKE", "('b%')"));        
+    }
+    
+    @Test
+    public void likeDescCompositePK2() throws Exception {
+        String ddl = "CREATE TABLE " + TABLE + " (oid CHAR(2) NOT NULL, code CHAR(2) NOT NULL constraint pk primary key (oid DESC, code DESC))";
+        Object[][] insertedRows = new Object[][]{{"a1", "11"}, {"b2", "22"}, {"c3", "33"}};
+        runQueryTest(ddl, upsert("oid", "code"), insertedRows, new Object[][]{{"b2", "22"}}, new WhereCondition("code", "LIKE", "('2%')"));        
+    }
+    
+    @Test
+    public void greaterThanDescCompositePK3() throws Exception {
+        String ddl = "CREATE TABLE " + TABLE + " (oid CHAR(2) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code DESC))";
+        Object[][] insertedRows = new Object[][]{{"o1", 1}, {"o1", 2}, {"o1", 3}};
+        Object[][] expectedRows = new Object[][]{{"o1", 2}, {"o1", 1}};
+        runQueryTest(ddl, upsert("oid", "code"), insertedRows, expectedRows, new WhereCondition("code", "<", "3"));        
+    }
+    
+    @Test
+    public void substrDescCompositePK1() throws Exception {
+        String ddl = "CREATE TABLE " + TABLE + " (oid CHAR(3) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code ASC))";
+        Object[][] insertedRows = new Object[][]{{"ao1", 1}, {"bo2", 2}, {"co3", 3}};
+        Object[][] expectedRows = new Object[][]{{"co3", 3}, {"bo2", 2}};
+        runQueryTest(ddl, upsert("oid", "code"), insertedRows, expectedRows, new WhereCondition("SUBSTR(oid, 3, 1)", ">", "'1'"));
+    }
+        
+    @Test
+    public void substrDescCompositePK2() throws Exception {
+        String ddl = "CREATE TABLE " + TABLE + " (oid CHAR(4) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code ASC))";
+        Object[][] insertedRows = new Object[][]{{"aaaa", 1}, {"bbbb", 2}, {"cccd", 3}};
+        Object[][] expectedRows = new Object[][]{{"cccd", 3}};
+        runQueryTest(ddl, upsert("oid", "code"), insertedRows, expectedRows, new WhereCondition("SUBSTR(oid, 4, 1)", "=", "'d'"));
+    }    
+    
+    @Test
+    public void lTrimDescCompositePK() throws Exception {
+        String ddl = "CREATE TABLE " + TABLE + " (oid VARCHAR NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code DESC))";
+        Object[][] insertedRows = new Object[][]{{" o1 ", 1}, {"  o2", 2}, {"  o3", 3}};
+        Object[][] expectedRows = new Object[][]{{"  o2", 2}};
+        runQueryTest(ddl, upsert("oid", "code"), insertedRows, expectedRows, new WhereCondition("LTRIM(oid)", "=", "'o2'"));
+    }
+    
+    @Test
+    public void lPadDescCompositePK() throws Exception {
+        String ddl = "CREATE TABLE " + TABLE + " (oid VARCHAR NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code DESC))";
+        Object[][] insertedRows = new Object[][]{{"aaaa", 1}, {"bbbb", 2}, {"cccc", 3}};
+        Object[][] expectedRows = new Object[][]{{"bbbb", 2}};
+        runQueryTest(ddl, upsert("oid", "code"), insertedRows, expectedRows, new WhereCondition("LPAD(oid, 8, '123')", "=", "'1231bbbb'"));
+    }
+
+    @Test
+    public void countDescCompositePK() throws Exception {
+        String ddl = "CREATE TABLE " + TABLE + " (oid CHAR(2) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code ASC))";
+        Object[][] insertedRows = new Object[][]{{"o1", 1}, {"o2", 2}, {"o3", 3}};
+        Object[][] expectedRows = new Object[][]{{3l}};
+        runQueryTest(ddl, upsert("oid", "code"), select("COUNT(oid)"), insertedRows, expectedRows);
+    }
+    
+    @Test
+    public void sumDescCompositePK() throws Exception {
+        String ddl = "CREATE TABLE " + TABLE + " (n1 INTEGER NOT NULL, n2 DECIMAL(10, 2) NOT NULL, n3 BIGINT NOT NULL " + 
+            "constraint pk primary key (n1 DESC, n2 DESC, n3 DESC))";
+        Object[][] insertedRows = new Object[][]{{10, bdec(10.2), 21l}, {20, bdec(20.2), 32l}, {30, bdec(30.2), 43l}};
+        Object[][] expectedRows = new Object[][]{{60l, bdec(60.6), 96l}};
+        runQueryTest(ddl, upsert("n1", "n2", "n3"), select("SUM(n1), SUM(n2), SUM(n3)"), insertedRows, expectedRows);
+    }    
+    
+    @Test
+    public void avgDescCompositePK() throws Exception {
+        String ddl = "CREATE TABLE " + TABLE + " (n1 INTEGER NOT NULL, n2 DECIMAL(10, 2) NOT NULL, n3 BIGINT NOT NULL " + 
+            "constraint pk primary key (n1 DESC, n2 DESC, n3 DESC))";
+        Object[][] insertedRows = new Object[][]{{10, bdec(10.2), 21l}, {20, bdec(20.2), 32l}, {30, bdec(30.2), 43l}};
+        Object[][] expectedRows = new Object[][]{{new BigDecimal(bint(2), -1), bdec(20.2), BigDecimal.valueOf(32)}};
+        runQueryTest(ddl, upsert("n1", "n2", "n3"), select("AVG(n1), AVG(n2), AVG(n3)"), insertedRows, expectedRows);
+    }
+    
+    @Test
+    public void minDescCompositePK() throws Exception {
+        String ddl = "CREATE TABLE " + TABLE + " (n1 INTEGER NOT NULL, n2 DECIMAL(10, 2) NOT NULL, n3 BIGINT NOT NULL " + 
+            "constraint pk primary key (n1 DESC, n2 DESC, n3 DESC))";
+        Object[][] insertedRows = new Object[][]{{10, bdec(10.2), 21l}, {20, bdec(20.2), 32l}, {30, bdec(30.2), 43l}};
+        Object[][] expectedRows = new Object[][]{{10, bdec(10.2), 21l}};
+        runQueryTest(ddl, upsert("n1", "n2", "n3"), select("MIN(n1), MIN(n2), MIN(n3)"), insertedRows, expectedRows);
+    }
+    
+    @Test
+    public void maxDescCompositePK() throws Exception {
+        String ddl = "CREATE TABLE " + TABLE + " (n1 INTEGER NOT NULL, n2 DECIMAL(10, 2) NOT NULL, n3 BIGINT NOT NULL " + 
+            "constraint pk primary key (n1 DESC, n2 DESC, n3 DESC))";
+        Object[][] insertedRows = new Object[][]{{10, bdec(10.2), 21l}, {20, bdec(20.2), 32l}, {30, bdec(30.2), 43l}};
+        Object[][] expectedRows = new Object[][]{{30, bdec(30.2), 43l}};
+        runQueryTest(ddl, upsert("n1", "n2", "n3"), select("MAX(n1), MAX(n2), MAX(n3)"), insertedRows, expectedRows);
+    }
+    
+    @Test
+    public void havingSumDescCompositePK() throws Exception {
+        String ddl = "CREATE TABLE " + TABLE + " (name CHAR(1) NOT NULL, code INTEGER NOT NULL " + 
+            "constraint pk primary key (name DESC, code DESC))";
+        Object[][] insertedRows = new Object[][]{{"a", 10}, {"a", 20}, {"b", 100}}; 
+        Object[][] expectedRows = new Object[][]{{"a", 30l}};
+        runQueryTest(ddl, upsert("name", "code"), select("name", "SUM(code)"), insertedRows, expectedRows, 
+            new HavingCondition("name", "SUM(code) = 30"));
+    }
+    
+    @Test
+    public void queryDescDateWithExplicitOrderBy() throws Exception {
+        String ddl = "CREATE TABLE " + TABLE + " (c1 CHAR(1) NOT NULL, c2 CHAR(1) NOT NULL, d1 DATE NOT NULL, c3 CHAR(1) NOT NULL " + 
+            "constraint pk primary key (c1, c2, d1 DESC, c3))";
+        Object[] row1 = {"1", "2", date(10, 11, 2001), "3"};
+        Object[] row2 = {"1", "2", date(10, 11, 2003), "3"};
+        Object[][] insertedRows = new Object[][]{row1, row2};
+        runQueryTest(ddl, upsert("c1", "c2", "d1", "c3"), select("c1, c2, d1", "c3"), insertedRows, new Object[][]{row2, row1},
+            null, null, new OrderBy("d1", OrderBy.Direction.DESC));
+    }    
+    
+    @Test
+    public void additionOnDescCompositePK() throws Exception {
+        String ddl = "CREATE TABLE " + TABLE + " (n1 INTEGER NOT NULL, n2 DECIMAL(10, 2) NOT NULL, n3 BIGINT NOT NULL, d1 DATE NOT NULL " + 
+            "constraint pk primary key (n1 DESC, n2 DESC, n3 DESC, d1 DESC))";
+        Object[][] insertedRows = new Object[][]{
+            {10, bdec(10.2), 21l, date(1, 10, 2001)}, {20, bdec(20.2), 32l, date(2, 6, 2001)}, {30, bdec(30.2), 43l, date(3, 1, 2001)}};
+        Object[][] expectedRows = new Object[][]{
+            {31l, bdec(32.2), 46l, date(3, 5, 2001)}, {21l, bdec(22.2), 35l, date(2, 10, 2001)}, {11l, bdec(12.2), 24l, date(1, 14, 2001)}};
+        runQueryTest(ddl, upsert("n1", "n2", "n3", "d1"), select("n1+1, n2+2, n3+3", "d1+4"), insertedRows, expectedRows);
+    }
+    
+    @Test
+    public void subtractionOnDescCompositePK() throws Exception {
+        String ddl = "CREATE TABLE " + TABLE + " (n1 INTEGER NOT NULL, n2 DECIMAL(10, 2) NOT NULL, n3 BIGINT NOT NULL, d1 DATE NOT NULL " + 
+            "constraint pk primary key (n1 DESC, n2 DESC, n3 DESC, d1 DESC))";
+        Object[][] insertedRows = new Object[][]{
+            {10, bdec(10.2), 21l, date(1, 10, 2001)}, {20, bdec(20.2), 32l, date(2, 6, 2001)}, {30, bdec(30.2), 43l, date(3, 10, 2001)}};
+        Object[][] expectedRows = new Object[][]{
+            {29l, bdec(28.2), 40l, date(3, 6, 2001)}, {19l, bdec(18.2), 29l, date(2, 2, 2001)}, {9l, bdec(8.2), 18l, date(1, 6, 2001)}};
+        runQueryTest(ddl, upsert("n1", "n2", "n3", "d1"), select("n1-1, n2-2, n3-3", "d1-4"), insertedRows, expectedRows);
+    }
+    
+    @Test
+    public void lessThanLeadingDescCompositePK() throws Exception {
+        String ddl = "CREATE TABLE " + TABLE + " (id INTEGER NOT NULL, date DATE NOT NULL constraint pk primary key (id DESC, date))";
+        Object[][] insertedRows = new Object[][]{{1, date(1, 1, 2012)}, {3, date(1, 1, 2013)}, {2, date(1, 1, 2011)}};
+        Object[][] expectedRows = new Object[][]{{1, date(1, 1, 2012)}};
+        runQueryTest(ddl, upsert("id", "date"), insertedRows, expectedRows, new WhereCondition("id", "<", "2"));
+    }
+    
+    @Test
+    public void lessThanTrailingDescCompositePK() throws Exception {
+        String ddl = "CREATE TABLE " + TABLE + " (id INTEGER NOT NULL, date DATE NOT NULL constraint pk primary key (id DESC, date))";
+        Object[][] insertedRows = new Object[][]{{1, date(1, 1, 2002)}, {3, date(1, 1, 2003)}, {2, date(1, 1, 2001)}};
+        Object[][] expectedRows = new Object[][]{{2, date(1, 1, 2001)}};
+        runQueryTest(ddl, upsert("id", "date"), insertedRows, expectedRows, new WhereCondition("date", "<", "TO_DATE('02-02-2001','mm-dd-yyyy')"));
+    }
+    
+    @Test
+    public void descVarLengthPK() throws Exception {
+        String ddl = "CREATE TABLE " + TABLE + " (id VARCHAR PRIMARY KEY DESC)";
+        Object[][] insertedRows = new Object[][]{{"a"}, {"ab"}, {"abc"}};
+        Object[][] expectedRows = new Object[][]{{"abc"}, {"ab"}, {"a"}};
+        runQueryTest(ddl, upsert("id"), select("id"), insertedRows, expectedRows,
+                null, null, new OrderBy("id", OrderBy.Direction.DESC));
+    }
+    
+    private void runQueryTest(String ddl, String columnName, Object[][] rows, Object[][] expectedRows) throws Exception {
+        runQueryTest(ddl, new String[]{columnName}, rows, expectedRows, null);
+    }
+    
+    private void runQueryTest(String ddl, String[] columnNames, Object[][] rows, Object[][] expectedRows) throws Exception {
+        runQueryTest(ddl, columnNames, rows, expectedRows, null);
+    }
+    
+    private void runQueryTest(String ddl, String[] columnNames, Object[][] rows, Object[][] expectedRows, WhereCondition condition) throws Exception {
+        runQueryTest(ddl, columnNames, columnNames, rows, expectedRows, condition, null, null);
+    }
+    
+    private void runQueryTest(String ddl, String[] columnNames, String[] projections, Object[][] rows, Object[][] expectedRows) throws Exception {
+        runQueryTest(ddl, columnNames, projections, rows, expectedRows, null, null, null);
+    }
+    
+    private void runQueryTest(String ddl, String[] columnNames, String[] projections, Object[][] rows, Object[][] expectedRows, HavingCondition havingCondition) throws Exception {
+        runQueryTest(ddl, columnNames, projections, rows, expectedRows, null, havingCondition, null);
+    }
+    
+
+    private void runQueryTest(
+        String ddl, 
+        String[] columnNames, 
+        String[] projections, 
+        Object[][] rows, Object[][] expectedRows, 
+        WhereCondition whereCondition, 
+        HavingCondition havingCondition,
+        OrderBy orderBy) 
+        throws Exception 
+    {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+
+        try {
+
+            conn.setAutoCommit(false);
+
+            createTestTable(getUrl(), ddl);
+
+            String columns = appendColumns(columnNames);
+            String placeholders = appendPlaceholders(columnNames);
+            String dml = "UPSERT INTO " + TABLE + " (" + columns + ") VALUES(" + placeholders +")";
+            PreparedStatement stmt = conn.prepareStatement(dml);
+
+            for (int row = 0; row < rows.length; row++) {
+                for (int col = 0; col < rows[row].length; col++) {
+                    Object value = rows[row][col];
+                    stmt.setObject(col + 1, value);
+                }
+                stmt.execute();
+            }
+            conn.commit();
+            
+            String selectClause = "SELECT " + appendColumns(projections) + " FROM " + TABLE;
+            
+            for (WhereCondition whereConditionClause : new WhereCondition[]{whereCondition, WhereCondition.reverse(whereCondition)}) {
+                String query = WhereCondition.appendWhere(whereConditionClause, selectClause);
+                query = HavingCondition.appendHaving(havingCondition, query);
+                query = OrderBy.appendOrderBy(orderBy, query);
+                runQuery(conn, query, expectedRows);
+            }
+            
+            if (orderBy != null) {
+                orderBy = OrderBy.reverse(orderBy);
+                String query = WhereCondition.appendWhere(whereCondition, selectClause);
+                query = HavingCondition.appendHaving(havingCondition, query);
+                query = OrderBy.appendOrderBy(orderBy, query);
+                runQuery(conn, query, reverse(expectedRows));
+            }
+            
+        } finally {
+            conn.close();
+        }
+    }
+    
+    private String appendColumns(String[] columnNames) {
+        String appendedColumns = "";
+        for (int i = 0; i < columnNames.length; i++) {                
+            appendedColumns += columnNames[i];
+            if (i < columnNames.length - 1) {
+                appendedColumns += ",";
+            }
+        }
+        return appendedColumns;
+    }
+    
+    private String appendPlaceholders(String[] columnNames) {
+        String placeholderList = "";
+        for (int i = 0; i < columnNames.length; i++) {                
+            placeholderList += "?";
+            if (i < columnNames.length - 1) {
+                placeholderList += ",";
+            }
+        }
+        return placeholderList;        
+    }
+    
+    private static void runQuery(Connection connection, String query, Object[][] expectedValues) throws Exception {
+        PreparedStatement stmt = connection.prepareStatement(query);
+
+        ResultSet rs = stmt.executeQuery();
+        int rowCounter = 0;
+        while (rs.next()) {
+            if (rowCounter == expectedValues.length) {
+                Assert.assertEquals("Exceeded number of expected rows for query" + query, expectedValues.length, rowCounter+1);
+            }
+            Object[] cols = new Object[expectedValues[rowCounter].length];
+            for (int colCounter = 0; colCounter < expectedValues[rowCounter].length; colCounter++) {
+                cols[colCounter] = rs.getObject(colCounter+1);
+            }
+            Assert.assertArrayEquals("Unexpected result for query " + query, expectedValues[rowCounter], cols);
+            rowCounter++;
+        }
+        Assert.assertEquals("Unexpected number of rows for query " + query, expectedValues.length, rowCounter);
+    }
+    
+    private static Object[][] reverse(Object[][] rows) {
+        Object[][] reversedArray = new Object[rows.length][];
+        System.arraycopy(rows, 0, reversedArray, 0, rows.length);
+        ArrayUtils.reverse(reversedArray);
+        return reversedArray;
+    }
+    
+    private static Date date(int month, int day, int year) {
+        Calendar cal = new GregorianCalendar();
+        cal.set(Calendar.MONTH, month-1);
+        cal.set(Calendar.DAY_OF_MONTH, day);
+        cal.set(Calendar.YEAR, year);
+        cal.set(Calendar.HOUR_OF_DAY, 10);
+        cal.set(Calendar.MINUTE, 2);
+        cal.set(Calendar.SECOND, 5);
+        cal.set(Calendar.MILLISECOND, 101);
+        Date d = new Date(cal.getTimeInMillis()); 
+        return d;
+    }
+        
+    private static String[] upsert(String...args) {
+        return args;
+    }
+    
+    private static String[] select(String...args) {
+        return args;
+    }
+    
+    private static BigDecimal bdec(double d) {
+        return BigDecimal.valueOf(d);
+    }
+    
+    private static BigInteger bint(long l) {
+        return BigInteger.valueOf(l);
+    }    
+    
+    private static class WhereCondition {
+        final String lhs;
+        final String operator;
+        final String rhs;
+    
+        WhereCondition(String lhs, String operator, String rhs) {
+            this.lhs = lhs;
+            this.operator = operator;
+            this.rhs = rhs;
+        }
+        
+        static WhereCondition reverse(WhereCondition whereCondition) {
+            
+            if (whereCondition == null) {
+                return null; 
+            }
+            
+            if (whereCondition.operator.equalsIgnoreCase("IN") || whereCondition.operator.equalsIgnoreCase("LIKE")) {
+                return whereCondition;
+            } else {
+                return new WhereCondition(whereCondition.rhs, whereCondition.getReversedOperator(), whereCondition.lhs);
+            }
+        }
+        
+         static String appendWhere(WhereCondition whereCondition, String query) {
+             if (whereCondition == null) {
+                 return query;
+             }
+            return query + " WHERE " + whereCondition.lhs + " " + whereCondition.operator + " " + whereCondition.rhs;
+        }
+        
+        private String getReversedOperator() {
+            if (operator.equals("<")) {
+                return ">";
+            } else if (operator.equals(">")) {
+                return "<";
+            }
+            return operator;
+        }
+    }
+    
+    private static class HavingCondition {
+        
+        private String groupby;
+        private String having;
+        
+        HavingCondition(String groupby, String having) {
+            this.groupby = groupby;
+            this.having = having;
+        }
+        
+        static String appendHaving(HavingCondition havingCondition, String query) {
+            if (havingCondition == null) {
+                return query;
+            }
+            return query + " GROUP BY " + havingCondition.groupby + " HAVING " + havingCondition.having + " ";
+        }
+    }
+    
+    private static class OrderBy {
+        
+        enum Direction {
+            
+            ASC, DESC;
+            
+            Direction reverse() {
+                if (this == ASC) {
+                    return DESC;
+                }
+                return ASC;
+            }
+        }
+        
+        private List<String> columnNames = Lists.newArrayList();
+        private List<Direction> directions = Lists.newArrayList();
+        
+        OrderBy() {            
+        }
+        
+        OrderBy(String columnName, Direction orderBy) {
+            add(columnName, orderBy);
+        }
+        
+        void add(String columnName, Direction direction) {
+            columnNames.add(columnName);
+            directions.add(direction);
+        }
+        
+        static OrderBy reverse(OrderBy orderBy) {
+            
+            if (orderBy == null) {
+                return null;
+            }
+            
+            List<Direction> reversedDirections = Lists.newArrayList();
+            for (Direction dir : orderBy.directions) {
+                reversedDirections.add(dir.reverse());
+            }
+            OrderBy reversedOrderBy = new OrderBy();
+            reversedOrderBy.columnNames = orderBy.columnNames;
+            reversedOrderBy.directions = reversedDirections;
+            return reversedOrderBy;
+        }
+        
+        static String appendOrderBy(OrderBy orderBy, String query) {
+            if (orderBy == null || orderBy.columnNames.isEmpty()) {
+                return query;
+            }
+            query += " ORDER BY ";
+            for (int i = 0; i < orderBy.columnNames.size(); i++) {
+                query += orderBy.columnNames.get(i) + " " + orderBy.directions.get(i).toString() + " ";
+            }
+            
+            query += " LIMIT 1000 ";
+            
+            return query;
+        }        
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/compile/FromCompiler.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/FromCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/FromCompiler.java
index 30a2bb7..9845498 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/FromCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/FromCompiler.java
@@ -25,6 +25,7 @@ import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+
 import org.apache.hadoop.hbase.HConstants;
 import org.apache.hadoop.hbase.client.HTableInterface;
 import org.apache.phoenix.coprocessor.MetaDataProtocol;
@@ -650,7 +651,7 @@ public class FromCompiler {
                     PTableType.SUBQUERY, null, MetaDataProtocol.MIN_TABLE_TIMESTAMP, PTable.INITIAL_SEQ_NUM,
                     null, null, columns, null, null, Collections.<PTable>emptyList(),
                     false, Collections.<PName>emptyList(), null, null, false, false, false, null,
-                    null, null);
+                    null, null, false);
 
             String alias = subselectNode.getAlias();
             TableRef tableRef = new TableRef(alias, t, MetaDataProtocol.MIN_TABLE_TIMESTAMP, false);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java
index 78f54e8..e4ca5d9 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java
@@ -1297,8 +1297,12 @@ public class JoinCompiler {
         }
         
         return PTableImpl.makePTable(left.getTenantId(), left.getSchemaName(),
-                PNameFactory.newName(SchemaUtil.getTableName(left.getName().getString(), right.getName().getString())), left.getType(), left.getIndexState(), left.getTimeStamp(), left.getSequenceNumber(), left.getPKName(), left.getBucketNum(), merged,
-                left.getParentSchemaName(), left.getParentTableName(), left.getIndexes(), left.isImmutableRows(), Collections.<PName>emptyList(), null, null, PTable.DEFAULT_DISABLE_WAL, left.isMultiTenant(), left.getStoreNulls(), left.getViewType(), left.getViewIndexId(), left.getIndexType());
+                PNameFactory.newName(SchemaUtil.getTableName(left.getName().getString(), right.getName().getString())),
+                left.getType(), left.getIndexState(), left.getTimeStamp(), left.getSequenceNumber(), left.getPKName(), 
+                left.getBucketNum(), merged,left.getParentSchemaName(), left.getParentTableName(), left.getIndexes(),
+                left.isImmutableRows(), Collections.<PName>emptyList(), null, null, PTable.DEFAULT_DISABLE_WAL,
+                left.isMultiTenant(), left.getStoreNulls(), left.getViewType(), left.getViewIndexId(), left.getIndexType(),
+                left.rowKeyOrderOptimizable());
     }
 
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/compile/OrderByCompiler.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/OrderByCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/OrderByCompiler.java
index f0406d4..7275b64 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/OrderByCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/OrderByCompiler.java
@@ -122,8 +122,8 @@ public class OrderByCompiler {
                 boolean isAscending = node.isAscending();
                 boolean isNullsLast = node.isNullsLast();
                 tracker.track(expression, isAscending ? SortOrder.ASC : SortOrder.DESC, isNullsLast);
-                // FIXME: this isn't correct. If we have a schema where column A is DESC,
-                // An ORDER BY A should still be ASC.
+                // If we have a schema where column A is DESC, reverse the sort order and nulls last
+                // since this is the order they actually are in.
                 if (expression.getSortOrder() == SortOrder.DESC) {
                     isAscending = !isAscending;
                     isNullsLast = !isNullsLast;

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/compile/OrderPreservingTracker.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/OrderPreservingTracker.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/OrderPreservingTracker.java
index 1c31606..70ae231 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/OrderPreservingTracker.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/OrderPreservingTracker.java
@@ -79,8 +79,9 @@ public class OrderPreservingTracker {
     
     public OrderPreservingTracker(StatementContext context, GroupBy groupBy, Ordering ordering, int nNodes, TupleProjector projector) {
         int pkPositionOffset = 0;
+        PTable table = context.getResolver().getTables().get(0).getTable();
+        isOrderPreserving = table.rowKeyOrderOptimizable();
         if (groupBy.isEmpty()) { // FIXME: would the below table have any of these set in the case of a GROUP BY?
-            PTable table = context.getResolver().getTables().get(0).getTable();
             boolean isSalted = table.getBucketNum() != null;
             boolean isMultiTenant = context.getConnection().getTenantId() != null && table.isMultiTenant();
             boolean isSharedViewIndex = table.getViewIndexId() != null;
@@ -117,10 +118,12 @@ public class OrderPreservingTracker {
                          */
                         if (!groupBy.isEmpty() && !groupBy.isOrderPreserving()) {
                             isOrderPreserving = false;
+                            isReverse = false;
                             return;
                         }
                     } else if (!isReverse){
                         isOrderPreserving = false;
+                        isReverse = false;
                         return;
                     }
                 } else {
@@ -128,12 +131,14 @@ public class OrderPreservingTracker {
                         isReverse = false;
                     } else if (isReverse){
                         isOrderPreserving = false;
+                        isReverse = false;
                         return;
                     }
                 }
                 if (node.isNullable()) {
                     if (!Boolean.valueOf(isNullsLast).equals(isReverse)) {
                         isOrderPreserving = false;
+                        isReverse = false;
                         return;
                     }
                 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/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 473e579..80cfbfe 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
@@ -32,6 +32,7 @@ import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.phoenix.filter.SkipScanFilter;
 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.schema.SaltingUtil;
 import org.apache.phoenix.util.ByteUtil;
@@ -82,7 +83,9 @@ public class ScanRanges {
                 }
             }
             ranges = Collections.singletonList(keyRanges);
-            if (keys.size() > 1) {
+            // Treat as binary if descending because we've got a separator byte at the end
+            // which is not part of the value.
+            if (keys.size() > 1 || SchemaUtil.getSeparatorByte(schema.rowKeyOrderOptimizable(), false, schema.getField(0)) == QueryConstants.DESC_SEPARATOR_BYTE) {
                 schema = SchemaUtil.VAR_BINARY_SCHEMA;
                 slotSpan = ScanUtil.SINGLE_COLUMN_SLOT_SPAN;
             } else {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/compile/TupleProjectionCompiler.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/TupleProjectionCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/TupleProjectionCompiler.java
index ee65280..c6aa546 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/TupleProjectionCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/TupleProjectionCompiler.java
@@ -152,7 +152,7 @@ public class TupleProjectionCompiler {
                 table.getBucketNum(), projectedColumns, table.getParentSchemaName(),
                 table.getParentName(), table.getIndexes(), table.isImmutableRows(), Collections.<PName>emptyList(), null, null,
                 table.isWALDisabled(), table.isMultiTenant(), table.getStoreNulls(), table.getViewType(), table.getViewIndexId(),
-                table.getIndexType());
+                table.getIndexType(), table.rowKeyOrderOptimizable());
     }
 
     public static PTable createProjectedTable(TableRef tableRef, List<ColumnRef> sourceColumnRefs, boolean retainPKColumns) throws SQLException {
@@ -179,7 +179,7 @@ public class TupleProjectionCompiler {
                     retainPKColumns ? table.getBucketNum() : null, projectedColumns, null,
                     null, Collections.<PTable>emptyList(), table.isImmutableRows(), Collections.<PName>emptyList(), null, null,
                     table.isWALDisabled(), table.isMultiTenant(), table.getStoreNulls(), table.getViewType(), table.getViewIndexId(),
-                    null);
+                    null, table.rowKeyOrderOptimizable());
     }
 
     // For extracting column references from single select statement

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/compile/UnionCompiler.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/UnionCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/UnionCompiler.java
index 269232e..942e244 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/UnionCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/UnionCompiler.java
@@ -80,8 +80,9 @@ public class UnionCompiler {
         }
         Long scn = statement.getConnection().getSCN();
         PTable tempTable = PTableImpl.makePTable(statement.getConnection().getTenantId(), UNION_SCHEMA_NAME, UNION_TABLE_NAME, 
-                PTableType.SUBQUERY, null, HConstants.LATEST_TIMESTAMP, scn == null ? HConstants.LATEST_TIMESTAMP : scn, null, null, projectedColumns, null, null, null,
-                        true, null, null, null, true, true, true, null, null, null);
+                PTableType.SUBQUERY, null, HConstants.LATEST_TIMESTAMP, scn == null ? HConstants.LATEST_TIMESTAMP : scn, null, null,
+                        projectedColumns, null, null, null,
+                        true, null, null, null, true, true, true, null, null, null, false);
         TableRef tableRef = new TableRef(null, tempTable, 0, false);
         return tableRef;
     }


[27/50] [abbrv] phoenix git commit: PHOENIX-978: Allow views to extend parent's PK only if parent's last PK column is fixed length

Posted by ma...@apache.org.
PHOENIX-978: Allow views to extend parent's PK only if parent's last PK column is fixed length


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/66705d51
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/66705d51
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/66705d51

Branch: refs/heads/calcite
Commit: 66705d5170496bed7c39bd484517722ac8d09a13
Parents: a772a4a
Author: Eli Levine <el...@apache.org>
Authored: Wed Jul 8 18:46:43 2015 -0700
Committer: Thomas D'Silva <td...@salesforce.com>
Committed: Mon Jul 13 17:36:58 2015 -0700

----------------------------------------------------------------------
 .../apache/phoenix/end2end/AlterTableIT.java    |  9 ++----
 .../java/org/apache/phoenix/end2end/ViewIT.java | 17 ++++++++++
 .../phoenix/exception/SQLExceptionCode.java     |  1 +
 .../apache/phoenix/schema/MetaDataClient.java   | 33 ++++++++++++++++++++
 4 files changed, 54 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/66705d51/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java
index fbaded0..0425933 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java
@@ -64,7 +64,6 @@ import org.apache.phoenix.util.PropertiesUtil;
 import org.apache.phoenix.util.ReadOnlyProps;
 import org.apache.phoenix.util.SchemaUtil;
 import org.junit.BeforeClass;
-import org.junit.Ignore;
 import org.junit.Test;
 
 import com.google.common.base.Objects;
@@ -2132,15 +2131,13 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
     }
     
     @Test
-    @Ignore
-    // enable this test after https://issues.apache.org/jira/browse/PHOENIX-978 is fixed 
     public void testAddExistingViewPkColumnToBaseTableWithViews() throws Exception {
         Connection conn = DriverManager.getConnection(getUrl());
         try {       
             conn.createStatement().execute("CREATE TABLE IF NOT EXISTS TABLEWITHVIEW ("
                     + " ID char(10) NOT NULL,"
                     + " COL1 integer NOT NULL,"
-                    + " COL2 bigint NOT NULL,"
+                    + " COL2 integer NOT NULL,"
                     + " CONSTRAINT NAME_PK PRIMARY KEY (ID, COL1, COL2)"
                     + " )");
             assertTableDefinition(conn, "TABLEWITHVIEW", PTableType.TABLE, null, 0, 3, -1, "ID", "COL1", "COL2");
@@ -2169,7 +2166,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
             }
             
             // add the pk column of the view to the base table
-            conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD VIEW_COL1 DECIMAL PRIMARY KEY");
+            conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD VIEW_COL1 DECIMAL(10,2) PRIMARY KEY");
             assertTableDefinition(conn, "TABLEWITHVIEW", PTableType.TABLE, null, 1, 4, -1, "ID", "COL1", "COL2", "VIEW_COL1");
             assertTableDefinition(conn, "VIEWOFTABLE", PTableType.VIEW, "TABLEWITHVIEW", 1, 5, 4, "ID", "COL1", "COL2", "VIEW_COL1", "VIEW_COL2");
             
@@ -2179,7 +2176,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
             assertEquals(rs.getString(1), "view1");
             assertEquals(rs.getInt(2), 12);
             assertEquals(rs.getInt(3), 13);
-            assertEquals(rs.getInt(4), 14);
+            assertEquals(rs.getBigDecimal(4).intValue(), 14);
             assertFalse(rs.next());
 
             // query view

http://git-wip-us.apache.org/repos/asf/phoenix/blob/66705d51/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewIT.java
index db38ab3..1d8af35 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewIT.java
@@ -18,6 +18,7 @@
 package org.apache.phoenix.end2end;
 
 import static com.google.common.collect.Lists.newArrayListWithExpectedSize;
+import static org.apache.phoenix.exception.SQLExceptionCode.CANNOT_MODIFY_VIEW_PK;
 import static org.apache.phoenix.exception.SQLExceptionCode.NOT_NULLABLE_COLUMN_IN_ROW_KEY;
 import static org.apache.phoenix.util.TestUtil.analyzeTable;
 import static org.apache.phoenix.util.TestUtil.getAllSplits;
@@ -522,6 +523,22 @@ public class ViewIT extends BaseViewIT {
         assertPKs(rs, new String[] {"K1", "K2", "K3", "K4"});
     }
     
+    @Test
+    public void testViewAddsPKColumnWhoseParentsLastPKIsVarLength() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        String ddl = "CREATE TABLE tp (k1 INTEGER NOT NULL, k2 VARCHAR NOT NULL, v1 DECIMAL, CONSTRAINT pk PRIMARY KEY (k1, k2))";
+        conn.createStatement().execute(ddl);
+        ddl = "CREATE VIEW v1  AS SELECT * FROM tp WHERE v1 = 1.0";
+        conn.createStatement().execute(ddl);
+        ddl = "ALTER VIEW V1 ADD k3 VARCHAR PRIMARY KEY, k4 VARCHAR PRIMARY KEY, v2 INTEGER";
+        try {
+            conn.createStatement().execute(ddl);
+            fail("View cannot extend PK if parent's last PK is variable length. See https://issues.apache.org/jira/browse/PHOENIX-978.");
+        } catch (SQLException e) {
+            assertEquals(CANNOT_MODIFY_VIEW_PK.getErrorCode(), e.getErrorCode());
+        }
+    }
+    
     @Test(expected=ColumnAlreadyExistsException.class)
     public void testViewAddsClashingPKColumn() throws Exception {
         Connection conn = DriverManager.getConnection(getUrl());

http://git-wip-us.apache.org/repos/asf/phoenix/blob/66705d51/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 b9f81fb..195450c 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
@@ -189,6 +189,7 @@ public enum SQLExceptionCode {
     NOT_NULLABLE_COLUMN_IN_ROW_KEY(1006, "42J04", "Only nullable columns may be added to a multi-part row key."),
     VARBINARY_LAST_PK(1015, "42J04", "Cannot add column to table when the last PK column is of type VARBINARY or ARRAY."),
     NULLABLE_FIXED_WIDTH_LAST_PK(1023, "42J04", "Cannot add column to table when the last PK column is nullable and fixed width."),
+    CANNOT_MODIFY_VIEW_PK(1036, "42J04", "Cannot modify the primary key of a VIEW if last PK column of parent is variable length."),
     BASE_TABLE_COLUMN(1037, "42J04", "Cannot modify columns of base table used by tenant-specific tables."),
     // Key/value column related errors
     KEY_VALUE_NOT_NULL(1007, "42K01", "A key/value column may not be declared as not null."),

http://git-wip-us.apache.org/repos/asf/phoenix/blob/66705d51/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
index 9d0c1aa..56c1aa2 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
@@ -79,6 +79,8 @@ import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.VIEW_TYPE;
 import static org.apache.phoenix.query.QueryConstants.BASE_TABLE_BASE_COLUMN_COUNT;
 import static org.apache.phoenix.query.QueryServices.DROP_METADATA_ATTRIB;
 import static org.apache.phoenix.query.QueryServicesOptions.DEFAULT_DROP_METADATA;
+import static org.apache.phoenix.schema.PTable.ViewType.MAPPED;
+import static org.apache.phoenix.schema.PTableType.VIEW;
 
 import java.io.IOException;
 import java.sql.Connection;
@@ -162,6 +164,8 @@ import org.apache.phoenix.parse.PFunction.FunctionArgument;
 import org.apache.phoenix.parse.ParseNode;
 import org.apache.phoenix.parse.ParseNodeFactory;
 import org.apache.phoenix.parse.PrimaryKeyConstraint;
+import org.apache.phoenix.parse.SQLParser;
+import org.apache.phoenix.parse.SelectStatement;
 import org.apache.phoenix.parse.TableName;
 import org.apache.phoenix.parse.UpdateStatisticsStatement;
 import org.apache.phoenix.query.ConnectionQueryServices.Feature;
@@ -1677,6 +1681,9 @@ public class MetaDataClient {
                             .setColumnName(column.getName().getString())
                             .build().buildException();
                     }
+                    if (tableType == PTableType.VIEW && viewType != ViewType.MAPPED) {
+                        throwIfLastPKOfParentIsFixedLength(parent, schemaName, tableName, colDef);
+                    }
                     if (!pkColumns.add(column)) {
                         throw new ColumnAlreadyExistsException(schemaName, tableName, column.getName().getString());
                     }
@@ -2421,6 +2428,9 @@ public class MetaDataClient {
                                 .setColumnName(colDef.getColumnDefName().getColumnName()).build().buildException();
                             }
                         }
+                        if (colDef != null && colDef.isPK() && table.getType() == VIEW && table.getViewType() != MAPPED) {
+                            throwIfLastPKOfParentIsFixedLength(getParentOfView(table), schemaName, tableName, colDef);
+                        }
                         PColumn column = newColumn(position++, colDef, PrimaryKeyConstraint.EMPTY, table.getDefaultFamilyName() == null ? null : table.getDefaultFamilyName().getString(), true);
                         columns.add(column);
                         String pkName = null;
@@ -2956,4 +2966,27 @@ public class MetaDataClient {
         return table.getTableStats();
     }
 
+    private void throwIfLastPKOfParentIsFixedLength(PTable parent, String viewSchemaName, String viewName, ColumnDef col) throws SQLException {
+        if (isLastPKVariableLength(parent)) { 
+            throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_MODIFY_VIEW_PK)
+                .setSchemaName(viewSchemaName)
+                .setTableName(viewName)
+                .setColumnName(col.getColumnDefName().getColumnName())
+                .build().buildException(); }
+    }
+    
+    private boolean isLastPKVariableLength(PTable table) {
+        List<PColumn> pkColumns = table.getPKColumns();
+        if (pkColumns.size() < 1) {
+            return false;
+        } else {
+            return !pkColumns.get(pkColumns.size()-1).getDataType().isFixedWidth();
+        }
+    }
+    
+    private PTable getParentOfView(PTable view) throws SQLException {
+        SelectStatement select = new SQLParser(view.getViewStatement()).parseQuery();
+        String parentName = SchemaUtil.normalizeFullTableName(select.getFrom().toString().trim());
+        return connection.getMetaDataCache().getTable(new PTableKey(view.getTenantId(), parentName));
+    }
 }


[24/50] [abbrv] phoenix git commit: PHOENIX-2113 Include log4j.properties in Pherf so runtime logging is displayed on console by default

Posted by ma...@apache.org.
PHOENIX-2113 Include log4j.properties in Pherf so runtime logging is displayed on console by default


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/052836c7
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/052836c7
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/052836c7

Branch: refs/heads/calcite
Commit: 052836c72c97de6580dda22fb485b7e3fa6d277d
Parents: 300de02
Author: Mujtaba <mu...@apache.org>
Authored: Mon Jul 13 11:50:47 2015 -0700
Committer: Mujtaba <mu...@apache.org>
Committed: Mon Jul 13 11:50:47 2015 -0700

----------------------------------------------------------------------
 phoenix-pherf/config/log4j.properties | 58 ++++++++++++++++++++++++++++++
 1 file changed, 58 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/052836c7/phoenix-pherf/config/log4j.properties
----------------------------------------------------------------------
diff --git a/phoenix-pherf/config/log4j.properties b/phoenix-pherf/config/log4j.properties
new file mode 100644
index 0000000..a6673c4
--- /dev/null
+++ b/phoenix-pherf/config/log4j.properties
@@ -0,0 +1,58 @@
+#
+#
+# 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.
+#
+#
+
+log4j.rootLogger=INFO,console
+log4j.threshold=INFO
+log4j.appender.DRFA=org.apache.log4j.DailyRollingFileAppender
+log4j.appender.DRFA.File=pherf.log
+
+# Rollver at midnight
+log4j.appender.DRFA.DatePattern=.yyyy-MM-dd
+
+# 30-day backup
+#log4j.appender.DRFA.MaxBackupIndex=30
+log4j.appender.DRFA.layout=org.apache.log4j.PatternLayout
+
+# Pattern format: Date LogLevel LoggerName LogMessage
+log4j.appender.DRFA.layout.ConversionPattern=%d{ISO8601} %p %c: %m%n
+
+# Debugging Pattern format
+#log4j.appender.DRFA.layout.ConversionPattern=%d{ISO8601} %-5p %c{2} (%F:%M(%L)) - %m%n
+
+#
+# Null Appender
+#
+log4j.appender.NullAppender=org.apache.log4j.varia.NullAppender
+
+#
+# console
+# Add "console" to rootlogger above if you want to use this 
+#
+log4j.appender.console=org.apache.log4j.ConsoleAppender
+log4j.appender.console.target=System.err
+log4j.appender.console.layout=org.apache.log4j.PatternLayout
+log4j.appender.console.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %p %c{2}: %m%n
+
+# Custom Logging levels
+log4j.logger.org.apache.zookeeper=ERROR
+log4j.logger.org.apache.hadoop.hbase.zookeeper.RecoverableZooKeeper=ERROR
+log4j.logger.org.apache.hadoop.hbase.zookeeper.ZKUtil=ERROR
+log4j.logger.org.apache.hadoop.hbase.HBaseConfiguration=ERROR


[34/50] [abbrv] phoenix git commit: PHOENIX-2067 Sort order incorrect for variable length DESC columns

Posted by ma...@apache.org.
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;


[14/50] [abbrv] phoenix git commit: PHOENIX-2058 Check for existence and compatibility of columns being added in view

Posted by ma...@apache.org.
PHOENIX-2058 Check for existence and compatibility of columns being added in view


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/aa4c9003
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/aa4c9003
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/aa4c9003

Branch: refs/heads/calcite
Commit: aa4c9003d4bd4827718e0f37de8c270f94fd5694
Parents: 8c9a6b8
Author: Thomas D'Silva <td...@salesforce.com>
Authored: Wed Jul 8 15:54:17 2015 -0700
Committer: Thomas D'Silva <td...@salesforce.com>
Committed: Wed Jul 8 15:54:17 2015 -0700

----------------------------------------------------------------------
 .../src/main/java/org/apache/phoenix/schema/MetaDataClient.java    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/aa4c9003/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
index 8a1c14b..d2d4338 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
@@ -2204,7 +2204,7 @@ public class MetaDataClient {
             String familyName = null;
             String msg = null;
             // TODO: better to return error code
-            if (result.getColumnName() != null && result.getColumnName() != null) {
+            if (result.getColumnName() != null) {
                 familyName = result.getFamilyName() == null ? null : Bytes.toString(result.getFamilyName());
                 columnName = Bytes.toString(result.getColumnName());
                 msg = "Cannot add/drop column referenced by VIEW";


[03/50] [abbrv] phoenix git commit: PHOENIX-922 Support SELECT without a from clause

Posted by ma...@apache.org.
PHOENIX-922 Support SELECT without a from clause


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/df42fa13
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/df42fa13
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/df42fa13

Branch: refs/heads/calcite
Commit: df42fa13072df8a738e0bf2c2a22b58c512a9dcf
Parents: f579e72
Author: maryannxue <we...@intel.com>
Authored: Sat Jul 4 13:11:08 2015 -0400
Committer: maryannxue <we...@intel.com>
Committed: Sat Jul 4 13:11:08 2015 -0400

----------------------------------------------------------------------
 .../org/apache/phoenix/end2end/SequenceIT.java  | 27 ++++++
 phoenix-core/src/main/antlr3/PhoenixSQL.g       |  2 +-
 .../apache/phoenix/compile/FromCompiler.java    | 13 +--
 .../phoenix/compile/ProjectionCompiler.java     |  2 +-
 .../apache/phoenix/compile/QueryCompiler.java   |  9 +-
 .../compile/TupleProjectionCompiler.java        |  1 +
 .../apache/phoenix/execute/BaseQueryPlan.java   |  9 +-
 .../phoenix/execute/EmptyTableQueryPlan.java    | 96 ++++++++++++++++++++
 .../apache/phoenix/parse/SelectStatement.java   |  6 +-
 .../org/apache/phoenix/schema/PTableImpl.java   |  1 +
 .../org/apache/phoenix/schema/TableRef.java     |  2 +
 .../phoenix/compile/QueryCompilerTest.java      | 45 +++++++++
 .../apache/phoenix/parse/QueryParserTest.java   | 19 +---
 13 files changed, 195 insertions(+), 37 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/df42fa13/phoenix-core/src/it/java/org/apache/phoenix/end2end/SequenceIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SequenceIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SequenceIT.java
index 6f2ec82..4273022 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SequenceIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SequenceIT.java
@@ -1242,5 +1242,32 @@ public class SequenceIT extends BaseClientManagedTimeIT {
         assertEquals(4, rs.getLong(1));
         assertFalse(rs.next());
     }
+    
+    @Test
+    public void testNoFromClause() throws Exception {
+        ResultSet rs;
+        nextConnection();
+        conn.createStatement().execute("CREATE SEQUENCE myseq START WITH 1 INCREMENT BY 1");
+        conn.createStatement().execute("CREATE SEQUENCE anotherseq START WITH 2 INCREMENT BY 3");
+        nextConnection();
+        rs = conn.createStatement().executeQuery("EXPLAIN SELECT NEXT VALUE FOR myseq");
+        assertEquals("CLIENT RESERVE VALUES FROM 1 SEQUENCE", QueryUtil.getExplainPlan(rs));
+        rs = conn.createStatement().executeQuery("SELECT NEXT VALUE FOR myseq");
+        assertTrue(rs.next());
+        assertEquals(1, rs.getInt(1));
+        rs = conn.createStatement().executeQuery("EXPLAIN SELECT CURRENT VALUE FOR myseq");
+        assertEquals("CLIENT RESERVE VALUES FROM 1 SEQUENCE", QueryUtil.getExplainPlan(rs));
+        rs = conn.createStatement().executeQuery("SELECT CURRENT VALUE FOR myseq");
+        assertTrue(rs.next());
+        assertEquals(1, rs.getInt(1));
+        rs = conn.createStatement().executeQuery("SELECT NEXT VALUE FOR myseq, NEXT VALUE FOR anotherseq");
+        assertTrue(rs.next());
+        assertEquals(2, rs.getInt(1));
+        assertEquals(2, rs.getInt(2));
+        rs = conn.createStatement().executeQuery("SELECT CURRENT VALUE FOR myseq, NEXT VALUE FOR anotherseq");
+        assertTrue(rs.next());
+        assertEquals(2, rs.getInt(1));
+        assertEquals(5, rs.getInt(2));        
+    }
 
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/phoenix/blob/df42fa13/phoenix-core/src/main/antlr3/PhoenixSQL.g
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/antlr3/PhoenixSQL.g b/phoenix-core/src/main/antlr3/PhoenixSQL.g
index 2a8d11b..69014a2 100644
--- a/phoenix-core/src/main/antlr3/PhoenixSQL.g
+++ b/phoenix-core/src/main/antlr3/PhoenixSQL.g
@@ -629,7 +629,7 @@ single_select returns [SelectStatement ret]
 @init{ contextStack.push(new ParseContext()); }
     :   SELECT (h=hintClause)? 
         (d=DISTINCT | ALL)? sel=select_list
-        FROM from=parseFrom
+        (FROM from=parseFrom)?
         (WHERE where=expression)?
         (GROUP BY group=group_by)?
         (HAVING having=expression)?

http://git-wip-us.apache.org/repos/asf/phoenix/blob/df42fa13/phoenix-core/src/main/java/org/apache/phoenix/compile/FromCompiler.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/FromCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/FromCompiler.java
index bc753c9..30a2bb7 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/FromCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/FromCompiler.java
@@ -22,12 +22,9 @@ import java.sql.SQLFeatureNotSupportedException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
-
 import org.apache.hadoop.hbase.HConstants;
 import org.apache.hadoop.hbase.client.HTableInterface;
 import org.apache.phoenix.coprocessor.MetaDataProtocol;
@@ -37,13 +34,11 @@ import org.apache.phoenix.jdbc.PhoenixConnection;
 import org.apache.phoenix.parse.AliasedNode;
 import org.apache.phoenix.parse.BindTableNode;
 import org.apache.phoenix.parse.ColumnDef;
-import org.apache.phoenix.parse.CreateFunctionStatement;
 import org.apache.phoenix.parse.CreateTableStatement;
 import org.apache.phoenix.parse.DMLStatement;
 import org.apache.phoenix.parse.DerivedTableNode;
 import org.apache.phoenix.parse.FamilyWildcardParseNode;
 import org.apache.phoenix.parse.JoinTableNode;
-import org.apache.phoenix.parse.NamedNode;
 import org.apache.phoenix.parse.NamedTableNode;
 import org.apache.phoenix.parse.PFunction;
 import org.apache.phoenix.parse.ParseNode;
@@ -106,7 +101,7 @@ public class FromCompiler {
 
         @Override
         public List<TableRef> getTables() {
-            return Collections.emptyList();
+            return Collections.singletonList(TableRef.EMPTY_TABLE_REF);
         }
 
         @Override
@@ -117,16 +112,16 @@ public class FromCompiler {
         @Override
         public TableRef resolveTable(String schemaName, String tableName)
                 throws SQLException {
-            throw new UnsupportedOperationException();
+            throw new TableNotFoundException(schemaName, tableName);
         }
 
         @Override
         public ColumnRef resolveColumn(String schemaName, String tableName, String colName) throws SQLException {
-            throw new UnsupportedOperationException();
+            throw new ColumnNotFoundException(schemaName, tableName, null, colName);
         }
         
         public PFunction resolveFunction(String functionName) throws SQLException {
-            throw new UnsupportedOperationException();
+            throw new FunctionNotFoundException(functionName);
         };
 
         public boolean hasUDFs() {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/df42fa13/phoenix-core/src/main/java/org/apache/phoenix/compile/ProjectionCompiler.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/ProjectionCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/ProjectionCompiler.java
index c39db09..915a55d 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/ProjectionCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/ProjectionCompiler.java
@@ -407,7 +407,7 @@ public class ProjectionCompiler {
                 String columnAlias = aliasedNode.getAlias() != null ? aliasedNode.getAlias() : SchemaUtil.normalizeIdentifier(aliasedNode.getNode().getAlias());
                 boolean isCaseSensitive = aliasedNode.getAlias() != null ? aliasedNode.isCaseSensitve() : (columnAlias != null ? SchemaUtil.isCaseSensitive(aliasedNode.getNode().getAlias()) : selectVisitor.isCaseSensitive);
                 String name = columnAlias == null ? expression.toString() : columnAlias;
-                projectedColumns.add(new ExpressionProjector(name, tableRef.getTableAlias() == null ? table.getName().getString() : tableRef.getTableAlias(), expression, isCaseSensitive));
+                projectedColumns.add(new ExpressionProjector(name, tableRef.getTableAlias() == null ? (table.getName() == null ? "" : table.getName().getString()) : tableRef.getTableAlias(), expression, isCaseSensitive));
             }
             if(arrayKVFuncs.size() > 0 && arrayKVRefs.size() > 0) {
                 serailizeArrayIndexInformationAndSetInScan(context, arrayKVFuncs, arrayKVRefs);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/df42fa13/phoenix-core/src/main/java/org/apache/phoenix/compile/QueryCompiler.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/QueryCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/QueryCompiler.java
index 94ff075..ce3343e 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/QueryCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/QueryCompiler.java
@@ -35,6 +35,7 @@ import org.apache.phoenix.compile.OrderByCompiler.OrderBy;
 import org.apache.phoenix.execute.AggregatePlan;
 import org.apache.phoenix.execute.ClientAggregatePlan;
 import org.apache.phoenix.execute.ClientScanPlan;
+import org.apache.phoenix.execute.EmptyTableQueryPlan;
 import org.apache.phoenix.execute.HashJoinPlan;
 import org.apache.phoenix.execute.HashJoinPlan.HashSubPlan;
 import org.apache.phoenix.execute.HashJoinPlan.WhereClauseSubPlan;
@@ -555,9 +556,11 @@ public class QueryCompiler {
         QueryPlan plan = innerPlan;
         if (plan == null) {
             ParallelIteratorFactory parallelIteratorFactory = asSubquery ? null : this.parallelIteratorFactory;
-            plan = select.isAggregate() || select.isDistinct() ?
-                      new AggregatePlan(context, select, tableRef, projector, limit, orderBy, parallelIteratorFactory, groupBy, having)
-                    : new ScanPlan(context, select, tableRef, projector, limit, orderBy, parallelIteratorFactory, allowPageFilter);
+            plan = select.getFrom() == null ?
+                      new EmptyTableQueryPlan(context, select, tableRef, projector, limit, orderBy, parallelIteratorFactory)
+                    : (select.isAggregate() || select.isDistinct() ?
+                              new AggregatePlan(context, select, tableRef, projector, limit, orderBy, parallelIteratorFactory, groupBy, having)
+                            : new ScanPlan(context, select, tableRef, projector, limit, orderBy, parallelIteratorFactory, allowPageFilter));
         }
         if (!subqueries.isEmpty()) {
             int count = subqueries.size();

http://git-wip-us.apache.org/repos/asf/phoenix/blob/df42fa13/phoenix-core/src/main/java/org/apache/phoenix/compile/TupleProjectionCompiler.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/TupleProjectionCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/TupleProjectionCompiler.java
index 5114411..ee65280 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/TupleProjectionCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/TupleProjectionCompiler.java
@@ -61,6 +61,7 @@ public class TupleProjectionCompiler {
         Preconditions.checkArgument(!select.isJoin());
         // Non-group-by or group-by aggregations will create its own projected result.
         if (select.getInnerSelectStatement() != null 
+                || select.getFrom() == null
                 || select.isAggregate() 
                 || select.isDistinct()
                 || (context.getResolver().getTables().get(0).getTable().getType() != PTableType.TABLE

http://git-wip-us.apache.org/repos/asf/phoenix/blob/df42fa13/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 37b73c0..5739e3e 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
@@ -17,9 +17,6 @@
  */
 package org.apache.phoenix.execute;
 
-import static org.apache.phoenix.compile.OrderByCompiler.OrderBy.FWD_ROW_KEY_ORDER_BY;
-import static org.apache.phoenix.compile.OrderByCompiler.OrderBy.REV_ROW_KEY_ORDER_BY;
-
 import java.io.ByteArrayOutputStream;
 import java.io.DataOutputStream;
 import java.io.IOException;
@@ -56,8 +53,6 @@ import org.apache.phoenix.parse.FilterableStatement;
 import org.apache.phoenix.parse.HintNode.Hint;
 import org.apache.phoenix.parse.ParseNodeFactory;
 import org.apache.phoenix.parse.TableName;
-import org.apache.phoenix.query.QueryServices;
-import org.apache.phoenix.query.QueryServicesOptions;
 import org.apache.phoenix.schema.KeyValueSchema;
 import org.apache.phoenix.schema.PColumn;
 import org.apache.phoenix.schema.PName;
@@ -170,6 +165,10 @@ public abstract class BaseQueryPlan implements QueryPlan {
             return ResultIterator.EMPTY_ITERATOR;
         }
         
+        if (tableRef == TableRef.EMPTY_TABLE_REF) {
+            return newIterator(scanGrouper);
+        }
+        
         // 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();

http://git-wip-us.apache.org/repos/asf/phoenix/blob/df42fa13/phoenix-core/src/main/java/org/apache/phoenix/execute/EmptyTableQueryPlan.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/execute/EmptyTableQueryPlan.java b/phoenix-core/src/main/java/org/apache/phoenix/execute/EmptyTableQueryPlan.java
new file mode 100644
index 0000000..fd55732
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/execute/EmptyTableQueryPlan.java
@@ -0,0 +1,96 @@
+/*
+ * 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.sql.SQLException;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.hadoop.hbase.KeyValue;
+import org.apache.hadoop.hbase.client.Scan;
+import org.apache.phoenix.compile.GroupByCompiler.GroupBy;
+import org.apache.phoenix.compile.OrderByCompiler.OrderBy;
+import org.apache.phoenix.compile.RowProjector;
+import org.apache.phoenix.compile.StatementContext;
+import org.apache.phoenix.iterate.ParallelIteratorFactory;
+import org.apache.phoenix.iterate.ParallelScanGrouper;
+import org.apache.phoenix.iterate.ResultIterator;
+import org.apache.phoenix.iterate.SequenceResultIterator;
+import org.apache.phoenix.parse.FilterableStatement;
+import org.apache.phoenix.query.KeyRange;
+import org.apache.phoenix.schema.TableRef;
+import org.apache.phoenix.schema.tuple.SingleKeyValueTuple;
+import org.apache.phoenix.schema.tuple.Tuple;
+
+public class EmptyTableQueryPlan extends BaseQueryPlan {
+
+    public EmptyTableQueryPlan(StatementContext context, FilterableStatement statement, 
+            TableRef tableRef, RowProjector projection, Integer limit, OrderBy orderBy,
+            ParallelIteratorFactory parallelIteratorFactory) {
+        super(context, statement, tableRef, projection, context.getBindManager().getParameterMetaData(), limit, orderBy, GroupBy.EMPTY_GROUP_BY, parallelIteratorFactory);
+    }
+
+    @Override
+    public List<KeyRange> getSplits() {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public List<List<Scan>> getScans() {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public boolean useRoundRobinIterator() throws SQLException {
+        return false;
+    }
+
+    @Override
+    protected ResultIterator newIterator(ParallelScanGrouper scanGrouper)
+            throws SQLException {
+        ResultIterator scanner = new ResultIterator() {
+            private boolean hasNext = true;
+
+            @Override
+            public void close() throws SQLException {
+                this.hasNext = false;
+            }
+
+            @Override
+            public Tuple next() throws SQLException {
+                if (hasNext) {
+                    hasNext = false;
+                    return new SingleKeyValueTuple(KeyValue.LOWESTKEY);
+                }
+                return null;
+            }
+
+            @Override
+            public void explain(List<String> planSteps) {
+            }
+            
+        };
+        
+        if (context.getSequenceManager().getSequenceCount() > 0) {
+            scanner = new SequenceResultIterator(scanner, context.getSequenceManager());
+        }
+        
+        return scanner;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/df42fa13/phoenix-core/src/main/java/org/apache/phoenix/parse/SelectStatement.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/SelectStatement.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/SelectStatement.java
index 362e98d..93c7364 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/SelectStatement.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/SelectStatement.java
@@ -124,8 +124,10 @@ public class SelectStatement implements FilterableStatement {
             buf.append(',');
         }
         buf.setLength(buf.length()-1);
-        buf.append(" FROM ");
-        fromTable.toSQL(resolver, buf);
+        if (fromTable != null) {
+            buf.append(" FROM ");
+            fromTable.toSQL(resolver, buf);
+        }
         if (where != null) {
             buf.append(" WHERE ");
             where.toSQL(resolver, buf);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/df42fa13/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableImpl.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableImpl.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableImpl.java
index 25b3e87..a947bfc 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableImpl.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableImpl.java
@@ -130,6 +130,7 @@ public class PTableImpl implements PTable {
     public PTableImpl() {
         this.indexes = Collections.emptyList();
         this.physicalNames = Collections.emptyList();
+        this.rowKeySchema = RowKeySchema.EMPTY_SCHEMA;
     }
 
     public PTableImpl(PName tenantId, String schemaName, String tableName, long timestamp, List<PColumnFamily> families) { // For base table of mapped VIEW

http://git-wip-us.apache.org/repos/asf/phoenix/blob/df42fa13/phoenix-core/src/main/java/org/apache/phoenix/schema/TableRef.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/TableRef.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/TableRef.java
index bd88770..78b00fa 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/TableRef.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/TableRef.java
@@ -26,6 +26,8 @@ import org.apache.phoenix.util.SchemaUtil;
 
 
 public class TableRef {
+    public static final TableRef EMPTY_TABLE_REF = new TableRef(new PTableImpl());
+    
     private PTable table;
     private final String alias;
     private final long upperBoundTimeStamp;

http://git-wip-us.apache.org/repos/asf/phoenix/blob/df42fa13/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java b/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java
index 7f15329..6e637b7 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java
@@ -1900,5 +1900,50 @@ public class QueryCompilerTest extends BaseConnectionlessQueryTest {
             conn.close();
         }
     }
+     
+     @Test
+     public void testNoFromClauseSelect() throws Exception {
+         Connection conn = DriverManager.getConnection(getUrl());
+         ResultSet rs = conn.createStatement().executeQuery("SELECT 2 * 3 * 4, 5 + 1");
+         assertTrue(rs.next());
+         assertEquals(24, rs.getInt(1));
+         assertEquals(6, rs.getInt(2));
+         assertFalse(rs.next());
+         
+         String query = 
+                 "SELECT 'a' AS col\n" +
+                 "UNION ALL\n" +
+                 "SELECT 'b' AS col\n" +
+                 "UNION ALL\n" +
+                 "SELECT 'c' AS col";
+         rs = conn.createStatement().executeQuery(query);
+         assertTrue(rs.next());
+         assertEquals("a", rs.getString(1));
+         assertTrue(rs.next());
+         assertEquals("b", rs.getString(1));
+         assertTrue(rs.next());
+         assertEquals("c", rs.getString(1));
+         assertFalse(rs.next());
+ 
+         rs = conn.createStatement().executeQuery("SELECT * FROM (" + query + ")");
+         assertTrue(rs.next());
+         assertEquals("a", rs.getString(1));
+         assertTrue(rs.next());
+         assertEquals("b", rs.getString(1));
+         assertTrue(rs.next());
+         assertEquals("c", rs.getString(1));
+         assertFalse(rs.next());
+     }
+     
+     
+     @Test
+     public void testFailNoFromClauseSelect() throws Exception {
+         Connection conn = DriverManager.getConnection(getUrl());
+         try {
+             conn.createStatement().executeQuery("SELECT foo, bar");
+             fail("Should have got ColumnNotFoundException");
+         } catch (ColumnNotFoundException e) {            
+         }
+     }
 
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/df42fa13/phoenix-core/src/test/java/org/apache/phoenix/parse/QueryParserTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/parse/QueryParserTest.java b/phoenix-core/src/test/java/org/apache/phoenix/parse/QueryParserTest.java
index eabd789..d651246 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/parse/QueryParserTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/parse/QueryParserTest.java
@@ -212,7 +212,7 @@ public class QueryParserTest {
             parseQuery(sql);
             fail();
         } catch (SQLException e) {
-            assertEquals(SQLExceptionCode.MISMATCHED_TOKEN.getErrorCode(), e.getErrorCode());
+            assertEquals(SQLExceptionCode.MISSING_TOKEN.getErrorCode(), e.getErrorCode());
         }
     }
 
@@ -348,7 +348,7 @@ public class QueryParserTest {
             parseQuery(sql);
             fail("Should have caught exception.");
         } catch (SQLException e) {
-            assertTrue(e.getMessage(), e.getMessage().contains("ERROR 602 (42P00): Syntax error. Missing \"FROM\" at line 1, column 16."));
+            assertTrue(e.getMessage(), e.getMessage().contains("ERROR 602 (42P00): Syntax error. Missing \"EOF\" at line 1, column 16."));
         }
     }
 
@@ -375,19 +375,6 @@ public class QueryParserTest {
     }
 
     @Test
-    public void testParsingStatementWithMissingToken() throws Exception {
-        try {
-            String sql = ((
-                    "select a b\n" +
-                    "where e = d\n"));
-            parseQuery(sql);
-            fail("Should have caught exception.");
-        } catch (SQLException e) {
-            assertEquals(SQLExceptionCode.MISMATCHED_TOKEN.getErrorCode(), e.getErrorCode());
-        }
-    }
-
-    @Test
     public void testParseCreateTableInlinePrimaryKeyWithOrder() throws Exception {
     	for (String order : new String[]{"asc", "desc"}) {
             String s = "create table core.entity_history_archive (id char(15) primary key ${o})".replace("${o}", order);
@@ -612,7 +599,7 @@ public class QueryParserTest {
             parseQuery(sql);
             fail();
         } catch (SQLException e) {
-            assertEquals(SQLExceptionCode.MISMATCHED_TOKEN.getErrorCode(), e.getErrorCode());
+            assertEquals(SQLExceptionCode.MISSING_TOKEN.getErrorCode(), e.getErrorCode());
         }
     }
 


[17/50] [abbrv] phoenix git commit: PHOENIX-2103 : Pig tests aren't dropping tables as expected between test runs

Posted by ma...@apache.org.
PHOENIX-2103 : Pig tests aren't dropping tables as expected between test runs


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/984e6222
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/984e6222
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/984e6222

Branch: refs/heads/calcite
Commit: 984e62223c8aa507e9c044cecfc7fc92ffa42522
Parents: 11bdb0e
Author: ravimagham <ra...@apache.org>
Authored: Thu Jul 9 20:20:32 2015 -0700
Committer: ravimagham <ra...@apache.org>
Committed: Thu Jul 9 20:20:32 2015 -0700

----------------------------------------------------------------------
 .../phoenix/pig/PhoenixHBaseLoaderIT.java       | 134 ++++++++-----------
 1 file changed, 53 insertions(+), 81 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/984e6222/phoenix-pig/src/it/java/org/apache/phoenix/pig/PhoenixHBaseLoaderIT.java
----------------------------------------------------------------------
diff --git a/phoenix-pig/src/it/java/org/apache/phoenix/pig/PhoenixHBaseLoaderIT.java b/phoenix-pig/src/it/java/org/apache/phoenix/pig/PhoenixHBaseLoaderIT.java
index 7fcf6ac..53a62ee 100644
--- a/phoenix-pig/src/it/java/org/apache/phoenix/pig/PhoenixHBaseLoaderIT.java
+++ b/phoenix-pig/src/it/java/org/apache/phoenix/pig/PhoenixHBaseLoaderIT.java
@@ -19,9 +19,11 @@
  */
 package org.apache.phoenix.pig;
 
-import static org.apache.phoenix.query.BaseTest.setUpConfigForMiniCluster;
+import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.apache.phoenix.util.PhoenixRuntime.JDBC_PROTOCOL_SEPARATOR;
+import static org.apache.phoenix.util.TestUtil.LOCALHOST;
 
 import java.sql.Connection;
 import java.sql.DriverManager;
@@ -35,18 +37,11 @@ import java.util.Properties;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.apache.hadoop.conf.Configuration;
-import org.apache.hadoop.hbase.HBaseTestingUtility;
-import org.apache.phoenix.end2end.NeedsOwnMiniClusterTest;
-import org.apache.phoenix.jdbc.PhoenixDriver;
-import org.apache.phoenix.query.QueryServices;
-import org.apache.phoenix.util.PhoenixRuntime;
+import org.apache.phoenix.end2end.BaseHBaseManagedTimeIT;
 import org.apache.phoenix.util.PropertiesUtil;
 import org.apache.phoenix.util.SchemaUtil;
-import org.apache.phoenix.util.TestUtil;
 import org.apache.pig.ExecType;
 import org.apache.pig.PigServer;
-import org.apache.pig.backend.hadoop.datastorage.ConfigurationUtil;
 import org.apache.pig.builtin.mock.Storage;
 import org.apache.pig.builtin.mock.Storage.Data;
 import org.apache.pig.data.DataType;
@@ -54,20 +49,15 @@ import org.apache.pig.data.Tuple;
 import org.apache.pig.impl.logicalLayer.schema.Schema;
 import org.apache.pig.impl.logicalLayer.schema.Schema.FieldSchema;
 import org.junit.After;
-import org.junit.AfterClass;
 import org.junit.Before;
-import org.junit.BeforeClass;
 import org.junit.Test;
-import org.junit.experimental.categories.Category;
-
 import com.google.common.base.Preconditions;
 
 /**
  * 
  * Test class to run all the integration tests against a virtual map reduce cluster.
  */
-@Category(NeedsOwnMiniClusterTest.class)
-public class PhoenixHBaseLoaderIT {
+public class PhoenixHBaseLoaderIT extends BaseHBaseManagedTimeIT {
     
     private static final Log LOG = LogFactory.getLog(PhoenixHBaseLoaderIT.class);
     private static final String SCHEMA_NAME = "T";
@@ -76,32 +66,16 @@ public class PhoenixHBaseLoaderIT {
     private static final String TABLE_FULL_NAME = SchemaUtil.getTableName(SCHEMA_NAME, TABLE_NAME);
     private static final String CASE_SENSITIVE_TABLE_NAME = SchemaUtil.getEscapedArgument("a");
     private static final String CASE_SENSITIVE_TABLE_FULL_NAME = SchemaUtil.getTableName(SCHEMA_NAME,CASE_SENSITIVE_TABLE_NAME);
-    private static HBaseTestingUtility hbaseTestUtil;
-    private static String zkQuorum;
-    private static Connection conn;
-    private static PigServer pigServer;
-    private static Configuration conf;
-
-    @BeforeClass
-    public static void setUpBeforeClass() throws Exception {
-        hbaseTestUtil = new HBaseTestingUtility();
-        conf = hbaseTestUtil.getConfiguration();
-        setUpConfigForMiniCluster(conf);
-        conf.set(QueryServices.DROP_METADATA_ATTRIB, Boolean.toString(true));
-        hbaseTestUtil.startMiniCluster();
+    private String zkQuorum;
+    private Connection conn;
+    private PigServer pigServer;
 
-        Class.forName(PhoenixDriver.class.getName());
-        zkQuorum = "localhost:" + hbaseTestUtil.getZkCluster().getClientPort();
-        Properties props = PropertiesUtil.deepCopy(TestUtil.TEST_PROPERTIES);
-        props.put(QueryServices.DROP_METADATA_ATTRIB, Boolean.toString(true));
-        conn = DriverManager.getConnection(PhoenixRuntime.JDBC_PROTOCOL +
-                 PhoenixRuntime.JDBC_PROTOCOL_SEPARATOR + zkQuorum,props);
-     }
-    
     @Before
     public void setUp() throws Exception {
-        pigServer = new PigServer(ExecType.LOCAL,
-                ConfigurationUtil.toProperties(conf));
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        conn = DriverManager.getConnection(getUrl(), props);
+        zkQuorum = LOCALHOST + JDBC_PROTOCOL_SEPARATOR + getZKClientPort(getTestClusterConfig());
+        pigServer = new PigServer(ExecType.LOCAL, getTestClusterConfig());
     }
 
     /**
@@ -110,13 +84,14 @@ public class PhoenixHBaseLoaderIT {
      */
     @Test
     public void testSchemaForTable() throws Exception {
+        final String TABLE = "TABLE1";
         final String ddl = String.format("CREATE TABLE %s "
                 + "  (a_string varchar not null, a_binary varbinary not null, a_integer integer, cf1.a_float float"
-                + "  CONSTRAINT pk PRIMARY KEY (a_string, a_binary))\n", TABLE_FULL_NAME);
+                + "  CONSTRAINT pk PRIMARY KEY (a_string, a_binary))\n", TABLE);
         conn.createStatement().execute(ddl);
-
+        conn.commit();
         pigServer.registerQuery(String.format(
-                "A = load 'hbase://table/%s' using " + PhoenixHBaseLoader.class.getName() + "('%s');", TABLE_FULL_NAME,
+                "A = load 'hbase://table/%s' using " + PhoenixHBaseLoader.class.getName() + "('%s');", TABLE,
                 zkQuorum));
         
         final Schema schema = pigServer.dumpSchema("A");
@@ -140,15 +115,15 @@ public class PhoenixHBaseLoaderIT {
     public void testSchemaForTableWithSpecificColumns() throws Exception {
         
         //create the table
-        final String ddl = "CREATE TABLE " + TABLE_FULL_NAME 
+        final String TABLE = "TABLE2";
+        final String ddl = "CREATE TABLE " + TABLE
                 + "  (ID INTEGER NOT NULL PRIMARY KEY,NAME VARCHAR, AGE INTEGER) ";
         conn.createStatement().execute(ddl);
         
-        
         final String selectColumns = "ID,NAME";
         pigServer.registerQuery(String.format(
                 "A = load 'hbase://table/%s/%s' using " + PhoenixHBaseLoader.class.getName() + "('%s');",
-                TABLE_FULL_NAME, selectColumns, zkQuorum));
+                TABLE, selectColumns, zkQuorum));
         
         Schema schema = pigServer.dumpSchema("A");
         List<FieldSchema> fields = schema.getFields();
@@ -157,7 +132,6 @@ public class PhoenixHBaseLoaderIT {
         assertTrue(fields.get(0).type == DataType.INTEGER);
         assertTrue(fields.get(1).alias.equalsIgnoreCase("NAME"));
         assertTrue(fields.get(1).type == DataType.CHARARRAY);
-        
     }
     
     /**
@@ -168,15 +142,16 @@ public class PhoenixHBaseLoaderIT {
     public void testSchemaForQuery() throws Exception {
         
        //create the table.
-        String ddl = String.format("CREATE TABLE " + TABLE_FULL_NAME +
+        final String TABLE = "TABLE3";
+        String ddl = String.format("CREATE TABLE " + TABLE +
                  "  (A_STRING VARCHAR NOT NULL, A_DECIMAL DECIMAL NOT NULL, CF1.A_INTEGER INTEGER, CF2.A_DOUBLE DOUBLE"
-                + "  CONSTRAINT pk PRIMARY KEY (A_STRING, A_DECIMAL))\n", TABLE_FULL_NAME);
+                + "  CONSTRAINT pk PRIMARY KEY (A_STRING, A_DECIMAL))\n", TABLE);
         conn.createStatement().execute(ddl);
-        
+
         
         
         //sql query for LOAD
-        final String sqlQuery = "SELECT A_STRING,CF1.A_INTEGER,CF2.A_DOUBLE FROM " + TABLE_FULL_NAME;
+        final String sqlQuery = "SELECT A_STRING,CF1.A_INTEGER,CF2.A_DOUBLE FROM " + TABLE;
         pigServer.registerQuery(String.format(
                 "A = load 'hbase://query/%s' using " + PhoenixHBaseLoader.class.getName() + "('%s');",
                 sqlQuery, zkQuorum));
@@ -201,13 +176,14 @@ public class PhoenixHBaseLoaderIT {
     public void testSchemaForTableWithAlias() throws Exception {
         
         //create the table.
-        String ddl = "CREATE TABLE  " + TABLE_FULL_NAME 
+        final String TABLE = "S.TABLE4";
+        String ddl = "CREATE TABLE  " + TABLE
                 + "  (A_STRING VARCHAR NOT NULL, A_DECIMAL DECIMAL NOT NULL, CF1.A_INTEGER INTEGER, CF2.A_DOUBLE DOUBLE"
                 + "  CONSTRAINT pk PRIMARY KEY (A_STRING, A_DECIMAL)) \n";
         conn.createStatement().execute(ddl);
-        
+
         //select query given as part of LOAD.
-        final String sqlQuery = "SELECT A_STRING,A_DECIMAL,CF1.A_INTEGER,CF2.A_DOUBLE FROM " + TABLE_FULL_NAME;
+        final String sqlQuery = "SELECT A_STRING,A_DECIMAL,CF1.A_INTEGER,CF2.A_DOUBLE FROM " + TABLE;
         
         LOG.info(String.format("Generated SQL Query [%s]",sqlQuery));
         
@@ -317,13 +293,14 @@ public class PhoenixHBaseLoaderIT {
     public void testForNonPKSQLQuery() throws Exception {
         
          //create the table
-         String ddl = "CREATE TABLE  " + TABLE_FULL_NAME 
+        final String TABLE = "TABLE5";
+        String ddl = "CREATE TABLE  " + TABLE
                 + " ( ID VARCHAR PRIMARY KEY, FOO VARCHAR, BAR INTEGER, BAZ UNSIGNED_INT)";
                 
         conn.createStatement().execute(ddl);
         
         //upsert data.
-        final String dml = "UPSERT INTO " + TABLE_FULL_NAME + " VALUES(?,?,?,?) ";
+        final String dml = "UPSERT INTO " + TABLE + " VALUES(?,?,?,?) ";
         PreparedStatement stmt = conn.prepareStatement(dml);
         stmt.setString(1, "a");
         stmt.setString(2, "a");
@@ -339,7 +316,7 @@ public class PhoenixHBaseLoaderIT {
         conn.commit();
         
         //sql query
-        final String sqlQuery = String.format(" SELECT FOO, BAZ FROM %s WHERE BAR = -1 " , TABLE_FULL_NAME);
+        final String sqlQuery = String.format(" SELECT FOO, BAZ FROM %s WHERE BAR = -1 " , TABLE);
       
         pigServer.registerQuery(String.format(
                 "A = load 'hbase://query/%s' using " + PhoenixHBaseLoader.class.getName() + "('%s');", sqlQuery,
@@ -372,13 +349,14 @@ public class PhoenixHBaseLoaderIT {
     public void testGroupingOfDataForTable() throws Exception {
         
          //create the table
-         String ddl = "CREATE TABLE  " + TABLE_FULL_NAME 
+        final String TABLE = "TABLE6";
+        String ddl = "CREATE TABLE  " + TABLE
                 + "  (ID  INTEGER NOT NULL PRIMARY KEY, NAME VARCHAR, AGE INTEGER, SAL INTEGER) ";
                 
         conn.createStatement().execute(ddl);
         
         //prepare data with 10 rows having age 25 and the other 30.
-        final String dml = "UPSERT INTO " + TABLE_FULL_NAME + " VALUES(?,?,?,?)";
+        final String dml = "UPSERT INTO " + TABLE + " VALUES(?,?,?,?)";
         PreparedStatement stmt = conn.prepareStatement(dml);
         int rows = 20;
         int j = 0, k = 0;
@@ -406,7 +384,7 @@ public class PhoenixHBaseLoaderIT {
          //load data and filter rows whose age is > 25
         pigServer.setBatchOn();
         pigServer.registerQuery(String.format(
-                "A = load 'hbase://table/%s' using " + PhoenixHBaseLoader.class.getName() + "('%s');", TABLE_FULL_NAME,
+                "A = load 'hbase://table/%s' using " + PhoenixHBaseLoader.class.getName() + "('%s');", TABLE,
                 zkQuorum));
         
         pigServer.registerQuery("B = GROUP A BY AGE;");
@@ -426,18 +404,19 @@ public class PhoenixHBaseLoaderIT {
     public void testLoadAndStore() throws Exception {
         
          //create the tables
-         final String sourceTableddl = "CREATE TABLE  " + TABLE_FULL_NAME 
+        final String TABLE = "TABLE7";
+        final String sourceTableddl = "CREATE TABLE  " + TABLE
                 + "  (ID  INTEGER NOT NULL PRIMARY KEY, NAME VARCHAR, AGE INTEGER, SAL INTEGER) ";
          
-         final String targetTable = "AGGREGATE";
-         final String targetTableddl = "CREATE TABLE " + targetTable 
+        final String targetTable = "AGGREGATE";
+        final String targetTableddl = "CREATE TABLE " + targetTable
                  +  "(AGE INTEGER NOT NULL PRIMARY KEY , MIN_SAL INTEGER , MAX_SAL INTEGER) ";
                  
         conn.createStatement().execute(sourceTableddl);
         conn.createStatement().execute(targetTableddl);
         
         //prepare data with 10 rows having age 25 and the other 30.
-        final String dml = "UPSERT INTO " + TABLE_FULL_NAME + " VALUES(?,?,?,?)";
+        final String dml = "UPSERT INTO " + TABLE + " VALUES(?,?,?,?)";
         PreparedStatement stmt = conn.prepareStatement(dml);
         int rows = 20;
         int j = 0, k = 0;
@@ -460,7 +439,7 @@ public class PhoenixHBaseLoaderIT {
          //load data and filter rows whose age is > 25
         pigServer.setBatchOn();
         pigServer.registerQuery(String.format(
-                "A = load 'hbase://table/%s' using " + PhoenixHBaseLoader.class.getName() + "('%s');", TABLE_FULL_NAME,
+                "A = load 'hbase://table/%s' using " + PhoenixHBaseLoader.class.getName() + "('%s');", TABLE,
                 zkQuorum));
         
         pigServer.registerQuery("B = GROUP A BY AGE;");
@@ -491,7 +470,8 @@ public class PhoenixHBaseLoaderIT {
     public void testDataForSQLQueryWithSequences() throws Exception {
         
          //create the table
-         String ddl = "CREATE TABLE " + TABLE_FULL_NAME
+        final String TABLE = "TABLE8";
+        String ddl = "CREATE TABLE " + TABLE
                 + " (ID INTEGER NOT NULL PRIMARY KEY, NAME VARCHAR, AGE INTEGER) ";
                 
         conn.createStatement().execute(ddl);
@@ -501,7 +481,7 @@ public class PhoenixHBaseLoaderIT {
         conn.createStatement().execute(sequenceDdl);
            
         //prepare data with 10 rows having age 25 and the other 30.
-        final String dml = "UPSERT INTO " + TABLE_FULL_NAME + " VALUES(?,?,?)";
+        final String dml = "UPSERT INTO " + TABLE + " VALUES(?,?,?)";
         PreparedStatement stmt = conn.prepareStatement(dml);
         int rows = 20;
         for(int i = 0 ; i < rows; i++) {
@@ -513,7 +493,7 @@ public class PhoenixHBaseLoaderIT {
         conn.commit();
         
         //sql query load data and filter rows whose age is > 25
-        final String sqlQuery = " SELECT NEXT VALUE FOR my_sequence AS my_seq,ID,NAME,AGE FROM " + TABLE_FULL_NAME + " WHERE AGE > 25";
+        final String sqlQuery = " SELECT NEXT VALUE FOR my_sequence AS my_seq,ID,NAME,AGE FROM " + TABLE + " WHERE AGE > 25";
         pigServer.registerQuery(String.format(
                 "A = load 'hbase://query/%s' using " + PhoenixHBaseLoader.class.getName() + "('%s');", sqlQuery,
                 zkQuorum));
@@ -532,12 +512,13 @@ public class PhoenixHBaseLoaderIT {
     public void testDataForSQLQueryWithFunctions() throws Exception {
         
          //create the table
-         String ddl = "CREATE TABLE " + TABLE_FULL_NAME
+         final String TABLE = "TABLE9";
+         String ddl = "CREATE TABLE " + TABLE
                 + " (ID INTEGER NOT NULL PRIMARY KEY, NAME VARCHAR) ";
                 
         conn.createStatement().execute(ddl);
         
-        final String dml = "UPSERT INTO " + TABLE_FULL_NAME + " VALUES(?,?)";
+        final String dml = "UPSERT INTO " + TABLE + " VALUES(?,?)";
         PreparedStatement stmt = conn.prepareStatement(dml);
         int rows = 20;
         for(int i = 0 ; i < rows; i++) {
@@ -548,7 +529,7 @@ public class PhoenixHBaseLoaderIT {
         conn.commit();
         
         //sql query
-        final String sqlQuery = " SELECT UPPER(NAME) AS n FROM " + TABLE_FULL_NAME + " ORDER BY ID" ;
+        final String sqlQuery = " SELECT UPPER(NAME) AS n FROM " + TABLE + " ORDER BY ID" ;
 
         pigServer.registerQuery(String.format(
                 "A = load 'hbase://query/%s' using "  + PhoenixHBaseLoader.class.getName() + "('%s');", sqlQuery,
@@ -604,23 +585,14 @@ public class PhoenixHBaseLoaderIT {
     
     @After
     public void tearDown() throws Exception {
-        dropTable(TABLE_FULL_NAME);
-        dropTable(CASE_SENSITIVE_TABLE_FULL_NAME);
+        if(conn != null) {
+            conn.close();
+        }
         pigServer.shutdown();
     }
 
-
     private void dropTable(String tableFullName) throws SQLException {
       Preconditions.checkNotNull(conn);
       conn.createStatement().execute(String.format("DROP TABLE IF EXISTS %s",tableFullName));
     }
-
-    @AfterClass
-    public static void tearDownAfterClass() throws Exception {
-        try {
-            conn.close();
-        } finally {
-            hbaseTestUtil.shutdownMiniCluster();
-        }
-    }
 }
\ No newline at end of file


[30/50] [abbrv] phoenix git commit: PHOENIX-2067 Sort order incorrect for variable length DESC columns

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDateArray.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDateArray.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDateArray.java
index 764401c..a07418c 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDateArray.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDateArray.java
@@ -17,93 +17,78 @@
  */
 package org.apache.phoenix.schema.types;
 
-import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
-import org.apache.phoenix.schema.SortOrder;
-
-import java.sql.Types;
 import java.sql.Date;
 
-public class PDateArray extends PArrayDataType<Date[]> {
-
-  public static final PDateArray INSTANCE = new PDateArray();
-
-  private PDateArray() {
-    super("DATE ARRAY", PDataType.ARRAY_TYPE_BASE + PDate.INSTANCE.getSqlType(), PhoenixArray.class,
-        null, 40);
-  }
+import org.apache.phoenix.schema.SortOrder;
 
-  @Override
-  public boolean isArrayType() {
-    return true;
-  }
+public class PDateArray extends PArrayDataType<Date[]> {
 
-  @Override
-  public boolean isFixedWidth() {
-    return false;
-  }
+    public static final PDateArray INSTANCE = new PDateArray();
 
-  @Override
-  public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
-    return compareTo(lhs, rhs);
-  }
+    private PDateArray() {
+        super("DATE ARRAY", PDataType.ARRAY_TYPE_BASE + PDate.INSTANCE.getSqlType(), PhoenixArray.class,
+                null, 40);
+    }
 
-  @Override
-  public Integer getByteSize() {
-    return null;
-  }
+    @Override
+    public boolean isArrayType() {
+        return true;
+    }
 
-  @Override
-  public byte[] toBytes(Object object) {
-    return toBytes(object, SortOrder.ASC);
-  }
+    @Override
+    public boolean isFixedWidth() {
+        return false;
+    }
 
-  @Override
-  public byte[] toBytes(Object object, SortOrder sortOrder) {
-    return toBytes(object, PDate.INSTANCE, sortOrder);
-  }
+    @Override
+    public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
+        return compareTo(lhs, rhs);
+    }
 
-  @Override
-  public Object toObject(byte[] bytes, int offset, int length,
-      PDataType actualType, SortOrder sortOrder, Integer maxLength, Integer scale) {
-    return toObject(bytes, offset, length, PDate.INSTANCE, sortOrder, maxLength, scale,
-        PDate.INSTANCE);
-  }
+    @Override
+    public Integer getByteSize() {
+        return null;
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType) {
-    return isCoercibleTo(targetType, this);
-  }
+    @Override
+    public byte[] toBytes(Object object) {
+        return toBytes(object, SortOrder.ASC);
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType, Object value) {
-    if (value == null) {
-      return true;
+    @Override
+    public byte[] toBytes(Object object, SortOrder sortOrder) {
+        return toBytes(object, PDate.INSTANCE, sortOrder);
     }
-    PhoenixArray pArr = (PhoenixArray) value;
-    Object[] dateArr = (Object[]) pArr.array;
-    for (Object i : dateArr) {
-      if (!super.isCoercibleTo(PDate.INSTANCE, i)) {
-        return false;
-      }
+
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length,
+            PDataType actualType, SortOrder sortOrder, Integer maxLength, Integer scale) {
+        return toObject(bytes, offset, length, PDate.INSTANCE, sortOrder, maxLength, scale,
+                PDate.INSTANCE);
     }
-    return true;
-  }
 
-  @Override
-  public int getResultSetSqlType() {
-    return Types.ARRAY;
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType) {
+        return isCoercibleTo(targetType, this);
+    }
 
-  @Override
-  public void coerceBytes(ImmutableBytesWritable ptr, Object object, PDataType actualType,
-      Integer maxLength, Integer scale, SortOrder actualModifer, Integer desiredMaxLength,
-      Integer desiredScale,SortOrder desiredModifier) {
-    coerceBytes(ptr, object, actualType, maxLength, scale, desiredMaxLength, desiredScale,
-        this, actualModifer, desiredModifier);
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType, Object value) {
+        if (value == null) {
+            return true;
+        }
+        PhoenixArray pArr = (PhoenixArray) value;
+        Object[] dateArr = (Object[]) pArr.array;
+        for (Object i : dateArr) {
+            if (!super.isCoercibleTo(PDate.INSTANCE, i)) {
+                return false;
+            }
+        }
+        return true;
+    }
 
-  @Override
-  public Object getSampleValue(Integer maxLength, Integer arrayLength) {
-    return getSampleValue(PDate.INSTANCE, arrayLength, maxLength);
-  }
+    @Override
+    public Object getSampleValue(Integer maxLength, Integer arrayLength) {
+        return getSampleValue(PDate.INSTANCE, arrayLength, maxLength);
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDecimalArray.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDecimalArray.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDecimalArray.java
index 6874ac3..101546a 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDecimalArray.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDecimalArray.java
@@ -17,94 +17,72 @@
  */
 package org.apache.phoenix.schema.types;
 
-import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
-import org.apache.phoenix.schema.SortOrder;
-
 import java.math.BigDecimal;
-import java.sql.Types;
-
-public class PDecimalArray extends PArrayDataType<BigDecimal[]> {
-
-  public static final PDecimalArray INSTANCE = new PDecimalArray();
 
-  private PDecimalArray() {
-    super("DECIMAL ARRAY", PDataType.ARRAY_TYPE_BASE + PDecimal.INSTANCE.getSqlType(),
-        PhoenixArray.class, null, 35);
-  }
+import org.apache.phoenix.schema.SortOrder;
 
-  @Override
-  public boolean isArrayType() {
-    return true;
-  }
+public class PDecimalArray extends PArrayDataType<BigDecimal[]> {
 
-  @Override
-  public boolean isFixedWidth() {
-    return false;
-  }
+    public static final PDecimalArray INSTANCE = new PDecimalArray();
 
-  @Override
-  public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
-    return compareTo(lhs, rhs);
-  }
+    private PDecimalArray() {
+        super("DECIMAL ARRAY", PDataType.ARRAY_TYPE_BASE + PDecimal.INSTANCE.getSqlType(), PhoenixArray.class, null, 35);
+    }
 
-  @Override
-  public Integer getByteSize() {
-    return null;
-  }
+    @Override
+    public boolean isArrayType() {
+        return true;
+    }
 
-  @Override
-  public byte[] toBytes(Object object) {
-    return toBytes(object, SortOrder.ASC);
-  }
+    @Override
+    public boolean isFixedWidth() {
+        return false;
+    }
 
-  @Override
-  public byte[] toBytes(Object object, SortOrder sortOrder) {
-    return toBytes(object, PDecimal.INSTANCE, sortOrder);
-  }
+    @Override
+    public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
+        return compareTo(lhs, rhs);
+    }
 
-  @Override
-  public Object toObject(byte[] bytes, int offset, int length,
-      PDataType actualType, SortOrder sortOrder, Integer maxLength,
-      Integer scale) {
-    return toObject(bytes, offset, length, PDecimal.INSTANCE, sortOrder, maxLength, scale,
-        PDecimal.INSTANCE);
-  }
+    @Override
+    public Integer getByteSize() {
+        return null;
+    }
 
-  @Override
-  public void coerceBytes(ImmutableBytesWritable ptr, Object object, PDataType actualType,
-      Integer maxLength, Integer scale, SortOrder actualModifer, Integer desiredMaxLength,
-      Integer desiredScale, SortOrder desiredModifier) {
-    coerceBytes(ptr, object, actualType, maxLength, scale, desiredMaxLength, desiredScale,
-        this, actualModifer, desiredModifier);
-  }
+    @Override
+    public byte[] toBytes(Object object) {
+        return toBytes(object, SortOrder.ASC);
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType) {
-    return isCoercibleTo(targetType, this);
-  }
+    @Override
+    public byte[] toBytes(Object object, SortOrder sortOrder) {
+        return toBytes(object, PDecimal.INSTANCE, sortOrder);
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType, Object value) {
-    if (value == null) {
-      return true;
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length, PDataType actualType, SortOrder sortOrder,
+            Integer maxLength, Integer scale) {
+        return toObject(bytes, offset, length, PDecimal.INSTANCE, sortOrder, maxLength, scale, PDecimal.INSTANCE);
     }
-    PhoenixArray pArr = (PhoenixArray) value;
-    Object[] decimalArr = (Object[]) pArr.array;
-    for (Object i : decimalArr) {
-      if (!super.isCoercibleTo(PDecimal.INSTANCE, i)) {
-        return false;
-      }
+
+    @Override
+    public boolean isCoercibleTo(PDataType targetType) {
+        return isCoercibleTo(targetType, this);
     }
-    return true;
-  }
 
-  @Override
-  public int getResultSetSqlType() {
-    return Types.ARRAY;
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType, Object value) {
+        if (value == null) { return true; }
+        PhoenixArray pArr = (PhoenixArray)value;
+        Object[] decimalArr = (Object[])pArr.array;
+        for (Object i : decimalArr) {
+            if (!super.isCoercibleTo(PDecimal.INSTANCE, i)) { return false; }
+        }
+        return true;
+    }
 
-  @Override
-  public Object getSampleValue(Integer maxLength, Integer arrayLength) {
-    return getSampleValue(PDecimal.INSTANCE, arrayLength, maxLength);
-  }
+    @Override
+    public Object getSampleValue(Integer maxLength, Integer arrayLength) {
+        return getSampleValue(PDecimal.INSTANCE, arrayLength, maxLength);
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDoubleArray.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDoubleArray.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDoubleArray.java
index 7ecec61..d585229 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDoubleArray.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDoubleArray.java
@@ -17,92 +17,76 @@
  */
 package org.apache.phoenix.schema.types;
 
-import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.phoenix.schema.SortOrder;
 
-import java.sql.Types;
-
 public class PDoubleArray extends PArrayDataType<double[]> {
 
-  public static final PDoubleArray INSTANCE = new PDoubleArray();
-
-  private PDoubleArray() {
-    super("DOUBLE ARRAY", PDataType.ARRAY_TYPE_BASE + PDouble.INSTANCE.getSqlType(),
-        PhoenixArray.class, null, 34);
-  }
-
-  @Override
-  public boolean isArrayType() {
-    return true;
-  }
-
-  @Override
-  public boolean isFixedWidth() {
-    return false;
-  }
+    public static final PDoubleArray INSTANCE = new PDoubleArray();
 
-  @Override
-  public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
-    return compareTo(lhs, rhs);
-  }
+    private PDoubleArray() {
+        super("DOUBLE ARRAY", PDataType.ARRAY_TYPE_BASE + PDouble.INSTANCE.getSqlType(),
+                PhoenixArray.class, null, 34);
+    }
 
-  @Override
-  public Integer getByteSize() {
-    return null;
-  }
+    @Override
+    public boolean isArrayType() {
+        return true;
+    }
 
-  @Override
-  public byte[] toBytes(Object object) {
-    return toBytes(object, SortOrder.ASC);
-  }
+    @Override
+    public boolean isFixedWidth() {
+        return false;
+    }
 
-  @Override
-  public byte[] toBytes(Object object, SortOrder sortOrder) {
-    return toBytes(object, PDouble.INSTANCE, sortOrder);
-  }
+    @Override
+    public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
+        return compareTo(lhs, rhs);
+    }
 
-  @Override
-  public Object toObject(byte[] bytes, int offset, int length,
-      PDataType actualType, SortOrder sortOrder, Integer maxLength, Integer scale) {
-    return toObject(bytes, offset, length, PDouble.INSTANCE, sortOrder, maxLength, scale,
-        PDouble.INSTANCE);
-  }
+    @Override
+    public Integer getByteSize() {
+        return null;
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType) {
-    return isCoercibleTo(targetType, this);
-  }
+    @Override
+    public byte[] toBytes(Object object) {
+        return toBytes(object, SortOrder.ASC);
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType, Object value) {
-    if (value == null) {
-      return true;
+    @Override
+    public byte[] toBytes(Object object, SortOrder sortOrder) {
+        return toBytes(object, PDouble.INSTANCE, sortOrder);
     }
-    PhoenixArray.PrimitiveDoublePhoenixArray pArr = (PhoenixArray.PrimitiveDoublePhoenixArray) value;
-    double[] doubleArr = (double[]) pArr.array;
-    for (double i : doubleArr) {
-      if (!super.isCoercibleTo(PDouble.INSTANCE, i)) {
-        return false;
-      }
+
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length,
+            PDataType actualType, SortOrder sortOrder, Integer maxLength, Integer scale) {
+        return toObject(bytes, offset, length, PDouble.INSTANCE, sortOrder, maxLength, scale,
+                PDouble.INSTANCE);
     }
-    return true;
-  }
 
-  @Override
-  public void coerceBytes(ImmutableBytesWritable ptr, Object object, PDataType actualType,
-      Integer maxLength, Integer scale, SortOrder actualModifer, Integer desiredMaxLength,
-      Integer desiredScale, SortOrder desiredModifier) {
-    coerceBytes(ptr, object, actualType, maxLength, scale, desiredMaxLength, desiredScale,
-        this, actualModifer, desiredModifier);
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType) {
+        return isCoercibleTo(targetType, this);
+    }
 
-  @Override
-  public int getResultSetSqlType() {
-    return Types.ARRAY;
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType, Object value) {
+        if (value == null) {
+            return true;
+        }
+        PhoenixArray.PrimitiveDoublePhoenixArray pArr = (PhoenixArray.PrimitiveDoublePhoenixArray) value;
+        double[] doubleArr = (double[]) pArr.array;
+        for (double i : doubleArr) {
+            if (!super.isCoercibleTo(PDouble.INSTANCE, i)) {
+                return false;
+            }
+        }
+        return true;
+    }
 
-  @Override
-  public Object getSampleValue(Integer maxLength, Integer arrayLength) {
-    return getSampleValue(PDouble.INSTANCE, arrayLength, maxLength);
-  }
+    @Override
+    public Object getSampleValue(Integer maxLength, Integer arrayLength) {
+        return getSampleValue(PDouble.INSTANCE, arrayLength, maxLength);
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PFloatArray.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PFloatArray.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PFloatArray.java
index 5e8ddb3..6005c3e 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PFloatArray.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PFloatArray.java
@@ -17,93 +17,77 @@
  */
 package org.apache.phoenix.schema.types;
 
-import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.phoenix.schema.SortOrder;
 
-import java.sql.Types;
-
 public class PFloatArray extends PArrayDataType<float[]> {
 
-  public static final PFloatArray INSTANCE = new PFloatArray();
-
-  private PFloatArray() {
-    super("FLOAT ARRAY", PDataType.ARRAY_TYPE_BASE + PFloat.INSTANCE.getSqlType(),
-        PhoenixArray.class, null, 33);
-  }
-
-  @Override
-  public boolean isArrayType() {
-    return true;
-  }
-
-  @Override
-  public boolean isFixedWidth() {
-    return false;
-  }
+    public static final PFloatArray INSTANCE = new PFloatArray();
 
-  @Override
-  public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
-    return compareTo(lhs, rhs);
-  }
+    private PFloatArray() {
+        super("FLOAT ARRAY", PDataType.ARRAY_TYPE_BASE + PFloat.INSTANCE.getSqlType(),
+                PhoenixArray.class, null, 33);
+    }
 
-  @Override
-  public Integer getByteSize() {
-    return null;
-  }
+    @Override
+    public boolean isArrayType() {
+        return true;
+    }
 
-  @Override
-  public byte[] toBytes(Object object) {
-    return toBytes(object, SortOrder.ASC);
-  }
+    @Override
+    public boolean isFixedWidth() {
+        return false;
+    }
 
-  @Override
-  public byte[] toBytes(Object object, SortOrder sortOrder) {
-    return toBytes(object, PFloat.INSTANCE, sortOrder);
-  }
+    @Override
+    public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
+        return compareTo(lhs, rhs);
+    }
 
-  @Override
-  public Object toObject(byte[] bytes, int offset, int length,
-      PDataType actualType, SortOrder sortOrder, Integer maxLength,
-      Integer scale) {
-    return toObject(bytes, offset, length, PFloat.INSTANCE, sortOrder, maxLength, scale,
-        PFloat.INSTANCE);
-  }
+    @Override
+    public Integer getByteSize() {
+        return null;
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType) {
-    return isCoercibleTo(targetType, this);
-  }
+    @Override
+    public byte[] toBytes(Object object) {
+        return toBytes(object, SortOrder.ASC);
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType, Object value) {
-    if (value == null) {
-      return true;
+    @Override
+    public byte[] toBytes(Object object, SortOrder sortOrder) {
+        return toBytes(object, PFloat.INSTANCE, sortOrder);
     }
-    PhoenixArray.PrimitiveFloatPhoenixArray pArr = (PhoenixArray.PrimitiveFloatPhoenixArray) value;
-    float[] floatArr = (float[]) pArr.array;
-    for (float i : floatArr) {
-      if (!super.isCoercibleTo(PFloat.INSTANCE, i)) {
-        return false;
-      }
+
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length,
+            PDataType actualType, SortOrder sortOrder, Integer maxLength,
+            Integer scale) {
+        return toObject(bytes, offset, length, PFloat.INSTANCE, sortOrder, maxLength, scale,
+                PFloat.INSTANCE);
     }
-    return true;
-  }
 
-  @Override
-  public int getResultSetSqlType() {
-    return Types.ARRAY;
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType) {
+        return isCoercibleTo(targetType, this);
+    }
 
-  @Override
-  public void coerceBytes(ImmutableBytesWritable ptr, Object object, PDataType actualType,
-      Integer maxLength, Integer scale, SortOrder actualModifer, Integer desiredMaxLength,
-      Integer desiredScale, SortOrder desiredModifier) {
-    coerceBytes(ptr, object, actualType, maxLength, scale, desiredMaxLength, desiredScale,
-        this, actualModifer, desiredModifier);
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType, Object value) {
+        if (value == null) {
+            return true;
+        }
+        PhoenixArray.PrimitiveFloatPhoenixArray pArr = (PhoenixArray.PrimitiveFloatPhoenixArray) value;
+        float[] floatArr = (float[]) pArr.array;
+        for (float i : floatArr) {
+            if (!super.isCoercibleTo(PFloat.INSTANCE, i)) {
+                return false;
+            }
+        }
+        return true;
+    }
 
-  @Override
-  public Object getSampleValue(Integer maxLength, Integer arrayLength) {
-    return getSampleValue(PFloat.INSTANCE, arrayLength, maxLength);
-  }
+    @Override
+    public Object getSampleValue(Integer maxLength, Integer arrayLength) {
+        return getSampleValue(PFloat.INSTANCE, arrayLength, maxLength);
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PIntegerArray.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PIntegerArray.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PIntegerArray.java
index 28a5c4c..b317cfe 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PIntegerArray.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PIntegerArray.java
@@ -17,93 +17,77 @@
  */
 package org.apache.phoenix.schema.types;
 
-import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.phoenix.schema.SortOrder;
 
-import java.sql.Types;
-
 public class PIntegerArray extends PArrayDataType<int[]> {
 
-  public static final PIntegerArray INSTANCE = new PIntegerArray();
-
-  private PIntegerArray() {
-    super("INTEGER ARRAY", PDataType.ARRAY_TYPE_BASE + PInteger.INSTANCE.getSqlType(),
-        PhoenixArray.class, null, 24);
-  }
-
-  @Override
-  public boolean isArrayType() {
-    return true;
-  }
-
-  @Override
-  public boolean isFixedWidth() {
-    return false;
-  }
+    public static final PIntegerArray INSTANCE = new PIntegerArray();
 
-  @Override
-  public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
-    return compareTo(lhs, rhs);
-  }
+    private PIntegerArray() {
+        super("INTEGER ARRAY", PDataType.ARRAY_TYPE_BASE + PInteger.INSTANCE.getSqlType(),
+                PhoenixArray.class, null, 24);
+    }
 
-  @Override
-  public Integer getByteSize() {
-    return null;
-  }
+    @Override
+    public boolean isArrayType() {
+        return true;
+    }
 
-  @Override
-  public byte[] toBytes(Object object) {
-    return toBytes(object, SortOrder.ASC);
-  }
+    @Override
+    public boolean isFixedWidth() {
+        return false;
+    }
 
-  @Override
-  public byte[] toBytes(Object object, SortOrder sortOrder) {
-    return toBytes(object, PInteger.INSTANCE, sortOrder);
-  }
+    @Override
+    public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
+        return compareTo(lhs, rhs);
+    }
 
-  @Override
-  public Object toObject(byte[] bytes, int offset, int length,
-      PDataType actualType, SortOrder sortOrder, Integer maxLength,
-      Integer scale) {
-    return toObject(bytes, offset, length, PInteger.INSTANCE, sortOrder, maxLength, scale,
-        PInteger.INSTANCE);
-  }
+    @Override
+    public Integer getByteSize() {
+        return null;
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType) {
-    return isCoercibleTo(targetType, this);
-  }
+    @Override
+    public byte[] toBytes(Object object) {
+        return toBytes(object, SortOrder.ASC);
+    }
 
-  @Override
-  public void coerceBytes(ImmutableBytesWritable ptr, Object object, PDataType actualType,
-      Integer maxLength, Integer scale, SortOrder actualModifer, Integer desiredMaxLength,
-      Integer desiredScale, SortOrder desiredModifier) {
-    coerceBytes(ptr, object, actualType, maxLength, scale, desiredMaxLength, desiredScale,
-        this, actualModifer, desiredModifier);
-  }
+    @Override
+    public byte[] toBytes(Object object, SortOrder sortOrder) {
+        return toBytes(object, PInteger.INSTANCE, sortOrder);
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType, Object value) {
-    if (value == null) {
-      return true;
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length,
+            PDataType actualType, SortOrder sortOrder, Integer maxLength,
+            Integer scale) {
+        return toObject(bytes, offset, length, PInteger.INSTANCE, sortOrder, maxLength, scale,
+                PInteger.INSTANCE);
     }
-    PhoenixArray.PrimitiveIntPhoenixArray pArr = (PhoenixArray.PrimitiveIntPhoenixArray) value;
-    int[] intArr = (int[]) pArr.array;
-    for (int i : intArr) {
-      if (!super.isCoercibleTo(PInteger.INSTANCE, i)) {
-        return false;
-      }
+
+    @Override
+    public boolean isCoercibleTo(PDataType targetType) {
+        return isCoercibleTo(targetType, this);
     }
-    return true;
-  }
 
-  @Override
-  public int getResultSetSqlType() {
-    return Types.ARRAY;
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType, Object value) {
+        if (value == null) {
+            return true;
+        }
+        PhoenixArray.PrimitiveIntPhoenixArray pArr = (PhoenixArray.PrimitiveIntPhoenixArray) value;
+        int[] intArr = (int[]) pArr.array;
+        for (int i : intArr) {
+            if (!super.isCoercibleTo(PInteger.INSTANCE, i)) {
+                return false;
+            }
+        }
+        return true;
+    }
 
-  @Override
-  public Object getSampleValue(Integer maxLength, Integer arrayLength) {
-    return getSampleValue(PInteger.INSTANCE, arrayLength, maxLength);
-  }
+    @Override
+    public Object getSampleValue(Integer maxLength, Integer arrayLength) {
+        return getSampleValue(PInteger.INSTANCE, arrayLength, maxLength);
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PLongArray.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PLongArray.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PLongArray.java
index 0670792..aef6437 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PLongArray.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PLongArray.java
@@ -17,93 +17,77 @@
  */
 package org.apache.phoenix.schema.types;
 
-import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.phoenix.schema.SortOrder;
 
-import java.sql.Types;
-
 public class PLongArray extends PArrayDataType<long[]> {
 
-  public static final PLongArray INSTANCE = new PLongArray();
-
-  private PLongArray() {
-    super("BIGINT ARRAY", PDataType.ARRAY_TYPE_BASE + PLong.INSTANCE.getSqlType(),
-        PhoenixArray.class, null, 30);
-  }
-
-  @Override
-  public boolean isArrayType() {
-    return true;
-  }
-
-  @Override
-  public boolean isFixedWidth() {
-    return false;
-  }
+    public static final PLongArray INSTANCE = new PLongArray();
 
-  @Override
-  public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
-    return compareTo(lhs, rhs);
-  }
+    private PLongArray() {
+        super("BIGINT ARRAY", PDataType.ARRAY_TYPE_BASE + PLong.INSTANCE.getSqlType(),
+                PhoenixArray.class, null, 30);
+    }
 
-  @Override
-  public Integer getByteSize() {
-    return null;
-  }
+    @Override
+    public boolean isArrayType() {
+        return true;
+    }
 
-  @Override
-  public byte[] toBytes(Object object) {
-    return toBytes(object, SortOrder.ASC);
-  }
+    @Override
+    public boolean isFixedWidth() {
+        return false;
+    }
 
-  @Override
-  public byte[] toBytes(Object object, SortOrder sortOrder) {
-    return toBytes(object, PLong.INSTANCE, sortOrder);
-  }
+    @Override
+    public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
+        return compareTo(lhs, rhs);
+    }
 
-  @Override
-  public Object toObject(byte[] bytes, int offset, int length,
-      PDataType actualType, SortOrder sortOrder, Integer maxLength,
-      Integer scale) {
-    return toObject(bytes, offset, length, PLong.INSTANCE, sortOrder, maxLength, scale,
-        PLong.INSTANCE);
-  }
+    @Override
+    public Integer getByteSize() {
+        return null;
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType) {
-    return isCoercibleTo(targetType, this);
-  }
+    @Override
+    public byte[] toBytes(Object object) {
+        return toBytes(object, SortOrder.ASC);
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType, Object value) {
-    if (value == null) {
-      return true;
+    @Override
+    public byte[] toBytes(Object object, SortOrder sortOrder) {
+        return toBytes(object, PLong.INSTANCE, sortOrder);
     }
-    PhoenixArray.PrimitiveLongPhoenixArray pArr = (PhoenixArray.PrimitiveLongPhoenixArray) value;
-    long[] longArr = (long[]) pArr.array;
-    for (long i : longArr) {
-      if (!super.isCoercibleTo(PLong.INSTANCE, i)) {
-        return false;
-      }
+
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length,
+            PDataType actualType, SortOrder sortOrder, Integer maxLength,
+            Integer scale) {
+        return toObject(bytes, offset, length, PLong.INSTANCE, sortOrder, maxLength, scale,
+                PLong.INSTANCE);
     }
-    return true;
-  }
 
-  @Override
-  public int getResultSetSqlType() {
-    return Types.ARRAY;
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType) {
+        return isCoercibleTo(targetType, this);
+    }
 
-  @Override
-  public void coerceBytes(ImmutableBytesWritable ptr, Object object, PDataType actualType,
-      Integer maxLength, Integer scale, SortOrder actualModifer, Integer desiredMaxLength,
-      Integer desiredScale, SortOrder desiredModifier) {
-    coerceBytes(ptr, object, actualType, maxLength, scale, desiredMaxLength, desiredScale,
-        this, actualModifer, desiredModifier);
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType, Object value) {
+        if (value == null) {
+            return true;
+        }
+        PhoenixArray.PrimitiveLongPhoenixArray pArr = (PhoenixArray.PrimitiveLongPhoenixArray) value;
+        long[] longArr = (long[]) pArr.array;
+        for (long i : longArr) {
+            if (!super.isCoercibleTo(PLong.INSTANCE, i)) {
+                return false;
+            }
+        }
+        return true;
+    }
 
-  @Override
-  public Object getSampleValue(Integer maxLength, Integer arrayLength) {
-    return getSampleValue(PLong.INSTANCE, arrayLength, maxLength);
-  }
+    @Override
+    public Object getSampleValue(Integer maxLength, Integer arrayLength) {
+        return getSampleValue(PLong.INSTANCE, arrayLength, maxLength);
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PSmallintArray.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PSmallintArray.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PSmallintArray.java
index 98da723..aea4384 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PSmallintArray.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PSmallintArray.java
@@ -17,93 +17,77 @@
  */
 package org.apache.phoenix.schema.types;
 
-import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.phoenix.schema.SortOrder;
 
-import java.sql.Types;
-
 public class PSmallintArray extends PArrayDataType<short[]> {
 
-  public static final PSmallintArray INSTANCE = new PSmallintArray();
-
-  private PSmallintArray() {
-    super("SMALLINT ARRAY", PDataType.ARRAY_TYPE_BASE + PSmallint.INSTANCE.getSqlType(),
-        PhoenixArray.class, null, 31);
-  }
-
-  @Override
-  public boolean isArrayType() {
-    return true;
-  }
-
-  @Override
-  public boolean isFixedWidth() {
-    return false;
-  }
+    public static final PSmallintArray INSTANCE = new PSmallintArray();
 
-  @Override
-  public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
-    return compareTo(lhs, rhs);
-  }
+    private PSmallintArray() {
+        super("SMALLINT ARRAY", PDataType.ARRAY_TYPE_BASE + PSmallint.INSTANCE.getSqlType(),
+                PhoenixArray.class, null, 31);
+    }
 
-  @Override
-  public Integer getByteSize() {
-    return null;
-  }
+    @Override
+    public boolean isArrayType() {
+        return true;
+    }
 
-  @Override
-  public byte[] toBytes(Object object) {
-    return toBytes(object, SortOrder.ASC);
-  }
+    @Override
+    public boolean isFixedWidth() {
+        return false;
+    }
 
-  @Override
-  public byte[] toBytes(Object object, SortOrder sortOrder) {
-    return toBytes(object, PSmallint.INSTANCE, sortOrder);
-  }
+    @Override
+    public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
+        return compareTo(lhs, rhs);
+    }
 
-  @Override
-  public Object toObject(byte[] bytes, int offset, int length,
-      PDataType actualType, SortOrder sortOrder, Integer maxLength,
-      Integer scale) {
-    return toObject(bytes, offset, length, PSmallint.INSTANCE, sortOrder, maxLength, scale,
-        PSmallint.INSTANCE);
-  }
+    @Override
+    public Integer getByteSize() {
+        return null;
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType) {
-    return isCoercibleTo(targetType, this);
-  }
+    @Override
+    public byte[] toBytes(Object object) {
+        return toBytes(object, SortOrder.ASC);
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType, Object value) {
-    if (value == null) {
-      return true;
+    @Override
+    public byte[] toBytes(Object object, SortOrder sortOrder) {
+        return toBytes(object, PSmallint.INSTANCE, sortOrder);
     }
-    PhoenixArray.PrimitiveShortPhoenixArray pArr = (PhoenixArray.PrimitiveShortPhoenixArray) value;
-    short[] shortArr = (short[]) pArr.array;
-    for (short i : shortArr) {
-      if (!super.isCoercibleTo(PSmallint.INSTANCE, i)) {
-        return false;
-      }
+
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length,
+            PDataType actualType, SortOrder sortOrder, Integer maxLength,
+            Integer scale) {
+        return toObject(bytes, offset, length, PSmallint.INSTANCE, sortOrder, maxLength, scale,
+                PSmallint.INSTANCE);
     }
-    return true;
-  }
 
-  @Override
-  public int getResultSetSqlType() {
-    return Types.ARRAY;
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType) {
+        return isCoercibleTo(targetType, this);
+    }
 
-  @Override
-  public void coerceBytes(ImmutableBytesWritable ptr, Object object, PDataType actualType,
-      Integer maxLength, Integer scale, SortOrder actualModifer, Integer desiredMaxLength,
-      Integer desiredScale, SortOrder desiredModifier) {
-    coerceBytes(ptr, object, actualType, maxLength, scale, desiredMaxLength, desiredScale,
-        this, actualModifer, desiredModifier);
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType, Object value) {
+        if (value == null) {
+            return true;
+        }
+        PhoenixArray.PrimitiveShortPhoenixArray pArr = (PhoenixArray.PrimitiveShortPhoenixArray) value;
+        short[] shortArr = (short[]) pArr.array;
+        for (short i : shortArr) {
+            if (!super.isCoercibleTo(PSmallint.INSTANCE, i)) {
+                return false;
+            }
+        }
+        return true;
+    }
 
-  @Override
-  public Object getSampleValue(Integer maxLength, Integer arrayLength) {
-    return getSampleValue(PSmallint.INSTANCE, arrayLength, maxLength);
-  }
+    @Override
+    public Object getSampleValue(Integer maxLength, Integer arrayLength) {
+        return getSampleValue(PSmallint.INSTANCE, arrayLength, maxLength);
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PTimeArray.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PTimeArray.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PTimeArray.java
index e0a7655..c19fd71 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PTimeArray.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PTimeArray.java
@@ -17,94 +17,79 @@
  */
 package org.apache.phoenix.schema.types;
 
-import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
-import org.apache.phoenix.schema.SortOrder;
-
 import java.sql.Time;
-import java.sql.Types;
-
-public class PTimeArray extends PArrayDataType<Time[]> {
-
-  public static final PTimeArray INSTANCE = new PTimeArray();
 
-  private PTimeArray() {
-    super("TIME ARRAY", PDataType.ARRAY_TYPE_BASE + PTime.INSTANCE.getSqlType(), PhoenixArray.class,
-        null, 38);
-  }
+import org.apache.phoenix.schema.SortOrder;
 
-  @Override
-  public boolean isArrayType() {
-    return true;
-  }
+public class PTimeArray extends PArrayDataType<Time[]> {
 
-  @Override
-  public boolean isFixedWidth() {
-    return false;
-  }
+    public static final PTimeArray INSTANCE = new PTimeArray();
 
-  @Override
-  public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
-    return compareTo(lhs, rhs);
-  }
+    private PTimeArray() {
+        super("TIME ARRAY", PDataType.ARRAY_TYPE_BASE + PTime.INSTANCE.getSqlType(), PhoenixArray.class,
+                null, 38);
+    }
 
-  @Override
-  public Integer getByteSize() {
-    return null;
-  }
+    @Override
+    public boolean isArrayType() {
+        return true;
+    }
 
-  @Override
-  public byte[] toBytes(Object object) {
-    return toBytes(object, SortOrder.ASC);
-  }
+    @Override
+    public boolean isFixedWidth() {
+        return false;
+    }
 
-  @Override
-  public byte[] toBytes(Object object, SortOrder sortOrder) {
-    return toBytes(object, PTime.INSTANCE, sortOrder);
-  }
+    @Override
+    public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
+        return compareTo(lhs, rhs);
+    }
 
-  @Override
-  public Object toObject(byte[] bytes, int offset, int length,
-      PDataType actualType, SortOrder sortOrder, Integer maxLength,
-      Integer scale) {
-    return toObject(bytes, offset, length, PTime.INSTANCE, sortOrder, maxLength, scale,
-        PTime.INSTANCE);
-  }
+    @Override
+    public Integer getByteSize() {
+        return null;
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType) {
-    return isCoercibleTo(targetType, this);
-  }
+    @Override
+    public byte[] toBytes(Object object) {
+        return toBytes(object, SortOrder.ASC);
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType, Object value) {
-    if (value == null) {
-      return true;
+    @Override
+    public byte[] toBytes(Object object, SortOrder sortOrder) {
+        return toBytes(object, PTime.INSTANCE, sortOrder);
     }
-    PhoenixArray pArr = (PhoenixArray) value;
-    Object[] timeArr = (Object[]) pArr.array;
-    for (Object i : timeArr) {
-      if (!super.isCoercibleTo(PTime.INSTANCE, i)) {
-        return false;
-      }
+
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length,
+            PDataType actualType, SortOrder sortOrder, Integer maxLength,
+            Integer scale) {
+        return toObject(bytes, offset, length, PTime.INSTANCE, sortOrder, maxLength, scale,
+                PTime.INSTANCE);
     }
-    return true;
-  }
 
-  @Override
-  public int getResultSetSqlType() {
-    return Types.ARRAY;
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType) {
+        return isCoercibleTo(targetType, this);
+    }
 
-  @Override
-  public void coerceBytes(ImmutableBytesWritable ptr, Object object, PDataType actualType,
-      Integer maxLength, Integer scale, SortOrder actualModifer, Integer desiredMaxLength,
-      Integer desiredScale, SortOrder desiredModifier) {
-    coerceBytes(ptr, object, actualType, maxLength, scale, desiredMaxLength, desiredScale,
-        this, actualModifer, desiredModifier);
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType, Object value) {
+        if (value == null) {
+            return true;
+        }
+        PhoenixArray pArr = (PhoenixArray) value;
+        Object[] timeArr = (Object[]) pArr.array;
+        for (Object i : timeArr) {
+            if (!super.isCoercibleTo(PTime.INSTANCE, i)) {
+                return false;
+            }
+        }
+        return true;
+    }
 
-  @Override
-  public Object getSampleValue(Integer maxLength, Integer arrayLength) {
-    return getSampleValue(PTime.INSTANCE, arrayLength, maxLength);
-  }
+    @Override
+    public Object getSampleValue(Integer maxLength, Integer arrayLength) {
+        return getSampleValue(PTime.INSTANCE, arrayLength, maxLength);
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PTimestampArray.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PTimestampArray.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PTimestampArray.java
index d44bda9..562f139 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PTimestampArray.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PTimestampArray.java
@@ -17,93 +17,79 @@
  */
 package org.apache.phoenix.schema.types;
 
-import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
-import org.apache.phoenix.schema.SortOrder;
+import java.sql.Timestamp;
 
-import java.sql.*;
+import org.apache.phoenix.schema.SortOrder;
 
 public class PTimestampArray extends PArrayDataType<Timestamp[]> {
 
-  public static final PTimestampArray INSTANCE = new PTimestampArray();
-
-  private PTimestampArray() {
-    super("TIMESTAMP ARRAY", PDataType.ARRAY_TYPE_BASE + PTimestamp.INSTANCE.getSqlType(),
-        PhoenixArray.class, null, 36);
-  }
-
-  @Override
-  public boolean isArrayType() {
-    return true;
-  }
-
-  @Override
-  public boolean isFixedWidth() {
-    return false;
-  }
+    public static final PTimestampArray INSTANCE = new PTimestampArray();
 
-  @Override
-  public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
-    return compareTo(lhs, rhs);
-  }
+    private PTimestampArray() {
+        super("TIMESTAMP ARRAY", PDataType.ARRAY_TYPE_BASE + PTimestamp.INSTANCE.getSqlType(),
+                PhoenixArray.class, null, 36);
+    }
 
-  @Override
-  public Integer getByteSize() {
-    return null;
-  }
+    @Override
+    public boolean isArrayType() {
+        return true;
+    }
 
-  @Override
-  public byte[] toBytes(Object object) {
-    return toBytes(object, SortOrder.ASC);
-  }
+    @Override
+    public boolean isFixedWidth() {
+        return false;
+    }
 
-  @Override
-  public byte[] toBytes(Object object, SortOrder sortOrder) {
-    return toBytes(object, PTimestamp.INSTANCE, sortOrder);
-  }
+    @Override
+    public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
+        return compareTo(lhs, rhs);
+    }
 
-  @Override
-  public Object toObject(byte[] bytes, int offset, int length,
-      PDataType actualType, SortOrder sortOrder, Integer maxLength,
-      Integer scale) {
-    return toObject(bytes, offset, length, PTimestamp.INSTANCE, sortOrder, maxLength, scale,
-        PTimestamp.INSTANCE);
-  }
+    @Override
+    public Integer getByteSize() {
+        return null;
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType) {
-    return isCoercibleTo(targetType, this);
-  }
+    @Override
+    public byte[] toBytes(Object object) {
+        return toBytes(object, SortOrder.ASC);
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType, Object value) {
-    if (value == null) {
-      return true;
+    @Override
+    public byte[] toBytes(Object object, SortOrder sortOrder) {
+        return toBytes(object, PTimestamp.INSTANCE, sortOrder);
     }
-    PhoenixArray pArr = (PhoenixArray) value;
-    Object[] timeStampArr = (Object[]) pArr.array;
-    for (Object i : timeStampArr) {
-      if (!super.isCoercibleTo(PTimestamp.INSTANCE, i)) {
-        return false;
-      }
+
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length,
+            PDataType actualType, SortOrder sortOrder, Integer maxLength,
+            Integer scale) {
+        return toObject(bytes, offset, length, PTimestamp.INSTANCE, sortOrder, maxLength, scale,
+                PTimestamp.INSTANCE);
     }
-    return true;
-  }
 
-  @Override
-  public int getResultSetSqlType() {
-    return Types.ARRAY;
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType) {
+        return isCoercibleTo(targetType, this);
+    }
 
-  @Override
-  public void coerceBytes(ImmutableBytesWritable ptr, Object object, PDataType actualType,
-      Integer maxLength, Integer scale, SortOrder actualModifer, Integer desiredMaxLength,
-      Integer desiredScale, SortOrder desiredModifier) {
-    coerceBytes(ptr, object, actualType, maxLength, scale, desiredMaxLength, desiredScale,
-        this, actualModifer, desiredModifier);
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType, Object value) {
+        if (value == null) {
+            return true;
+        }
+        PhoenixArray pArr = (PhoenixArray) value;
+        Object[] timeStampArr = (Object[]) pArr.array;
+        for (Object i : timeStampArr) {
+            if (!super.isCoercibleTo(PTimestamp.INSTANCE, i)) {
+                return false;
+            }
+        }
+        return true;
+    }
 
-  @Override
-  public Object getSampleValue(Integer maxLength, Integer arrayLength) {
-    return getSampleValue(PTimestamp.INSTANCE, arrayLength, maxLength);
-  }
+    @Override
+    public Object getSampleValue(Integer maxLength, Integer arrayLength) {
+        return getSampleValue(PTimestamp.INSTANCE, arrayLength, maxLength);
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PTinyintArray.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PTinyintArray.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PTinyintArray.java
index e0b300d..7ffaeb1 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PTinyintArray.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PTinyintArray.java
@@ -17,93 +17,77 @@
  */
 package org.apache.phoenix.schema.types;
 
-import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.phoenix.schema.SortOrder;
 
-import java.sql.Types;
-
 public class PTinyintArray extends PArrayDataType<byte[]> {
 
-  public static final PTinyintArray INSTANCE = new PTinyintArray();
-
-  private PTinyintArray() {
-    super("TINYINT ARRAY", PDataType.ARRAY_TYPE_BASE + PTinyint.INSTANCE.getSqlType(),
-        PhoenixArray.class, null, 32);
-  }
-
-  @Override
-  public boolean isArrayType() {
-    return true;
-  }
-
-  @Override
-  public boolean isFixedWidth() {
-    return false;
-  }
+    public static final PTinyintArray INSTANCE = new PTinyintArray();
 
-  @Override
-  public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
-    return compareTo(lhs, rhs);
-  }
+    private PTinyintArray() {
+        super("TINYINT ARRAY", PDataType.ARRAY_TYPE_BASE + PTinyint.INSTANCE.getSqlType(),
+                PhoenixArray.class, null, 32);
+    }
 
-  @Override
-  public Integer getByteSize() {
-    return null;
-  }
+    @Override
+    public boolean isArrayType() {
+        return true;
+    }
 
-  @Override
-  public byte[] toBytes(Object object) {
-    return toBytes(object, SortOrder.ASC);
-  }
+    @Override
+    public boolean isFixedWidth() {
+        return false;
+    }
 
-  @Override
-  public byte[] toBytes(Object object, SortOrder sortOrder) {
-    return toBytes(object, PTinyint.INSTANCE, sortOrder);
-  }
+    @Override
+    public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
+        return compareTo(lhs, rhs);
+    }
 
-  @Override
-  public Object toObject(byte[] bytes, int offset, int length,
-      PDataType actualType, SortOrder sortOrder, Integer maxLength,
-      Integer scale) {
-    return toObject(bytes, offset, length, PTinyint.INSTANCE, sortOrder, maxLength, scale,
-        PTinyint.INSTANCE);
-  }
+    @Override
+    public Integer getByteSize() {
+        return null;
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType) {
-    return isCoercibleTo(targetType, this);
-  }
+    @Override
+    public byte[] toBytes(Object object) {
+        return toBytes(object, SortOrder.ASC);
+    }
 
-  @Override
-  public void coerceBytes(ImmutableBytesWritable ptr, Object object, PDataType actualType,
-      Integer maxLength, Integer scale, SortOrder actualModifer, Integer desiredMaxLength,
-      Integer desiredScale, SortOrder desiredModifier) {
-    coerceBytes(ptr, object, actualType, maxLength, scale, desiredMaxLength, desiredScale,
-        this, actualModifer, desiredModifier);
-  }
+    @Override
+    public byte[] toBytes(Object object, SortOrder sortOrder) {
+        return toBytes(object, PTinyint.INSTANCE, sortOrder);
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType, Object value) {
-    if (value == null) {
-      return true;
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length,
+            PDataType actualType, SortOrder sortOrder, Integer maxLength,
+            Integer scale) {
+        return toObject(bytes, offset, length, PTinyint.INSTANCE, sortOrder, maxLength, scale,
+                PTinyint.INSTANCE);
     }
-    PhoenixArray.PrimitiveBytePhoenixArray pArr = (PhoenixArray.PrimitiveBytePhoenixArray) value;
-    byte[] byteArr = (byte[]) pArr.array;
-    for (byte i : byteArr) {
-      if (!super.isCoercibleTo(PTinyint.INSTANCE, i)) {
-        return false;
-      }
+
+    @Override
+    public boolean isCoercibleTo(PDataType targetType) {
+        return isCoercibleTo(targetType, this);
     }
-    return true;
-  }
 
-  @Override
-  public int getResultSetSqlType() {
-    return Types.ARRAY;
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType, Object value) {
+        if (value == null) {
+            return true;
+        }
+        PhoenixArray.PrimitiveBytePhoenixArray pArr = (PhoenixArray.PrimitiveBytePhoenixArray) value;
+        byte[] byteArr = (byte[]) pArr.array;
+        for (byte i : byteArr) {
+            if (!super.isCoercibleTo(PTinyint.INSTANCE, i)) {
+                return false;
+            }
+        }
+        return true;
+    }
 
-  @Override
-  public Object getSampleValue(Integer maxLength, Integer arrayLength) {
-    return getSampleValue(PTinyint.INSTANCE, arrayLength, maxLength);
-  }
+    @Override
+    public Object getSampleValue(Integer maxLength, Integer arrayLength) {
+        return getSampleValue(PTinyint.INSTANCE, arrayLength, maxLength);
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedDateArray.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedDateArray.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedDateArray.java
index 859c93b..b2e173a 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedDateArray.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedDateArray.java
@@ -17,94 +17,74 @@
  */
 package org.apache.phoenix.schema.types;
 
-import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
-import org.apache.phoenix.schema.SortOrder;
-
-import java.sql.Types;
 import java.util.Date;
 
-public class PUnsignedDateArray extends PArrayDataType<Date> {
-
-  public static final PUnsignedDateArray INSTANCE = new PUnsignedDateArray();
-
-  private PUnsignedDateArray() {
-    super("UNSIGNED_DATE ARRAY", PDataType.ARRAY_TYPE_BASE + PUnsignedDate.INSTANCE.getSqlType(),
-        PhoenixArray.class, null, 41);
-  }
+import org.apache.phoenix.schema.SortOrder;
 
-  @Override
-  public boolean isArrayType() {
-    return true;
-  }
+public class PUnsignedDateArray extends PArrayDataType<Date> {
 
-  @Override
-  public boolean isFixedWidth() {
-    return false;
-  }
+    public static final PUnsignedDateArray INSTANCE = new PUnsignedDateArray();
 
-  @Override
-  public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
-    return compareTo(lhs, rhs);
-  }
+    private PUnsignedDateArray() {
+        super("UNSIGNED_DATE ARRAY", PDataType.ARRAY_TYPE_BASE + PUnsignedDate.INSTANCE.getSqlType(),
+                PhoenixArray.class, null, 41);
+    }
 
-  @Override
-  public Integer getByteSize() {
-    return null;
-  }
+    @Override
+    public boolean isArrayType() {
+        return true;
+    }
 
-  @Override
-  public byte[] toBytes(Object object) {
-    return toBytes(object, SortOrder.ASC);
-  }
+    @Override
+    public boolean isFixedWidth() {
+        return false;
+    }
 
-  @Override
-  public byte[] toBytes(Object object, SortOrder sortOrder) {
-    return toBytes(object, PUnsignedDate.INSTANCE, sortOrder);
-  }
+    @Override
+    public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
+        return compareTo(lhs, rhs);
+    }
 
-  @Override
-  public Object toObject(byte[] bytes, int offset, int length,
-      PDataType actualType, SortOrder sortOrder, Integer maxLength,
-      Integer scale) {
-    return toObject(bytes, offset, length, PUnsignedDate.INSTANCE, sortOrder, maxLength,
-        scale, PUnsignedDate.INSTANCE);
-  }
+    @Override
+    public Integer getByteSize() {
+        return null;
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType) {
-    return isCoercibleTo(targetType, this);
-  }
+    @Override
+    public byte[] toBytes(Object object) {
+        return toBytes(object, SortOrder.ASC);
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType, Object value) {
-    if (value == null) {
-      return true;
+    @Override
+    public byte[] toBytes(Object object, SortOrder sortOrder) {
+        return toBytes(object, PUnsignedDate.INSTANCE, sortOrder);
     }
-    PhoenixArray pArr = (PhoenixArray) value;
-    Object[] dateArr = (Object[]) pArr.array;
-    for (Object i : dateArr) {
-      if (!super.isCoercibleTo(PUnsignedDate.INSTANCE, i)) {
-        return false;
-      }
+
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length, PDataType actualType, SortOrder sortOrder,
+            Integer maxLength, Integer scale) {
+        return toObject(bytes, offset, length, PUnsignedDate.INSTANCE, sortOrder, maxLength, scale,
+                PUnsignedDate.INSTANCE);
     }
-    return true;
-  }
 
-  @Override
-  public int getResultSetSqlType() {
-    return Types.ARRAY;
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType) {
+        return isCoercibleTo(targetType, this);
+    }
 
-  @Override
-  public void coerceBytes(ImmutableBytesWritable ptr, Object object, PDataType actualType,
-      Integer maxLength, Integer scale, SortOrder actualModifer, Integer desiredMaxLength,
-      Integer desiredScale, SortOrder desiredModifier) {
-    coerceBytes(ptr, object, actualType, maxLength, scale, desiredMaxLength, desiredScale,
-        this, actualModifer, desiredModifier);
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType, Object value) {
+        if (value == null) { return true; }
+        PhoenixArray pArr = (PhoenixArray)value;
+        Object[] dateArr = (Object[])pArr.array;
+        for (Object i : dateArr) {
+            if (!super.isCoercibleTo(PUnsignedDate.INSTANCE, i)) { return false; }
+        }
+        return true;
+    }
 
-  @Override
-  public Object getSampleValue(Integer maxLength, Integer arrayLength) {
-    return getSampleValue(PUnsignedDate.INSTANCE, arrayLength, maxLength);
-  }
+    @Override
+    public Object getSampleValue(Integer maxLength, Integer arrayLength) {
+        return getSampleValue(PUnsignedDate.INSTANCE, arrayLength, maxLength);
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedDoubleArray.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedDoubleArray.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedDoubleArray.java
index 50c69bf..be16178 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedDoubleArray.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedDoubleArray.java
@@ -17,96 +17,80 @@
  */
 package org.apache.phoenix.schema.types;
 
-import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.phoenix.schema.SortOrder;
 
-import java.sql.Types;
-
 public class PUnsignedDoubleArray extends PArrayDataType<double[]> {
 
-  public static final PUnsignedDoubleArray INSTANCE = new PUnsignedDoubleArray();
-
-  private PUnsignedDoubleArray() {
-    super("UNSIGNED_DOUBLE ARRAY", PDataType.ARRAY_TYPE_BASE + PUnsignedDouble.INSTANCE.getSqlType(),
-        PhoenixArray.class, null, 47);
-  }
-
-  @Override
-  public boolean isArrayType() {
-    return true;
-  }
-
-  @Override
-  public boolean isFixedWidth() {
-    return false;
-  }
+    public static final PUnsignedDoubleArray INSTANCE = new PUnsignedDoubleArray();
 
-  @Override
-  public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
-    return compareTo(lhs, rhs);
-  }
+    private PUnsignedDoubleArray() {
+        super("UNSIGNED_DOUBLE ARRAY", PDataType.ARRAY_TYPE_BASE + PUnsignedDouble.INSTANCE.getSqlType(),
+                PhoenixArray.class, null, 47);
+    }
 
-  @Override
-  public Integer getByteSize() {
-    return null;
-  }
+    @Override
+    public boolean isArrayType() {
+        return true;
+    }
 
-  @Override
-  public byte[] toBytes(Object object) {
-    return toBytes(object, SortOrder.ASC);
-  }
+    @Override
+    public boolean isFixedWidth() {
+        return false;
+    }
 
-  @Override
-  public byte[] toBytes(Object object, SortOrder sortOrder) {
-    return toBytes(object, PUnsignedDouble.INSTANCE, sortOrder);
-  }
+    @Override
+    public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
+        return compareTo(lhs, rhs);
+    }
 
-  @Override
-  public Object toObject(byte[] bytes, int offset, int length,
-      PDataType actualType, SortOrder sortOrder, Integer maxLength,
-      Integer scale) {
-    return toObject(bytes, offset, length, PUnsignedDouble.INSTANCE, sortOrder, maxLength,
-        scale, PUnsignedDouble.INSTANCE);
-  }
+    @Override
+    public Integer getByteSize() {
+        return null;
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType) {
-    return isCoercibleTo(targetType, this);
-  }
+    @Override
+    public byte[] toBytes(Object object) {
+        return toBytes(object, SortOrder.ASC);
+    }
 
-  @Override
-  public void coerceBytes(ImmutableBytesWritable ptr, Object object, PDataType actualType,
-      Integer maxLength, Integer scale, SortOrder actualModifer, Integer desiredMaxLength,
-      Integer desiredScale, SortOrder desiredModifier) {
-    coerceBytes(ptr, object, actualType, maxLength, scale, desiredMaxLength, desiredScale,
-        this, actualModifer, desiredModifier);
-  }
+    @Override
+    public byte[] toBytes(Object object, SortOrder sortOrder) {
+        return toBytes(object, PUnsignedDouble.INSTANCE, sortOrder);
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType, Object value) {
-    if (value == null) {
-      return true;
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length,
+            PDataType actualType, SortOrder sortOrder, Integer maxLength,
+            Integer scale) {
+        return toObject(bytes, offset, length, PUnsignedDouble.INSTANCE, sortOrder, maxLength,
+                scale, PUnsignedDouble.INSTANCE);
     }
-    PhoenixArray pArr = (PhoenixArray) value;
-    Object[] doubleArr = (Object[]) pArr.array;
-    for (Object i : doubleArr) {
-      if (!super.isCoercibleTo(PUnsignedDouble.INSTANCE, i) && (!super.isCoercibleTo(
-          PUnsignedTimestamp.INSTANCE, i))
-          && (!super.isCoercibleTo(PUnsignedTime.INSTANCE, i)) && (!super
-          .isCoercibleTo(PUnsignedDate.INSTANCE, i))) {
-        return false;
-      }
+
+    @Override
+    public boolean isCoercibleTo(PDataType targetType) {
+        return isCoercibleTo(targetType, this);
     }
-    return true;
-  }
 
-  @Override
-  public int getResultSetSqlType() {
-    return Types.ARRAY;
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType, Object value) {
+        if (value == null) {
+            return true;
+        }
+        PhoenixArray pArr = (PhoenixArray) value;
+        Object[] doubleArr = (Object[]) pArr.array;
+        for (Object i : doubleArr) {
+            if (!super.isCoercibleTo(PUnsignedDouble.INSTANCE, i) && (!super.isCoercibleTo(
+                    PUnsignedTimestamp.INSTANCE, i))
+                    && (!super.isCoercibleTo(PUnsignedTime.INSTANCE, i)) && (!super
+                            .isCoercibleTo(PUnsignedDate.INSTANCE, i))) {
+                return false;
+            }
+        }
+        return true;
+    }
 
-  @Override
-  public Object getSampleValue(Integer maxLength, Integer arrayLength) {
-    return getSampleValue(PUnsignedDouble.INSTANCE, arrayLength, maxLength);
-  }
+    @Override
+    public Object getSampleValue(Integer maxLength, Integer arrayLength) {
+        return getSampleValue(PUnsignedDouble.INSTANCE, arrayLength, maxLength);
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedFloatArray.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedFloatArray.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedFloatArray.java
index db9d075..bc1e1fb 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedFloatArray.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedFloatArray.java
@@ -17,93 +17,77 @@
  */
 package org.apache.phoenix.schema.types;
 
-import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.phoenix.schema.SortOrder;
 
-import java.sql.Types;
-
 public class PUnsignedFloatArray extends PArrayDataType<float[]> {
 
-  public static final PUnsignedFloatArray INSTANCE = new PUnsignedFloatArray();
-
-  private PUnsignedFloatArray() {
-    super("UNSIGNED_FLOAT ARRAY", PDataType.ARRAY_TYPE_BASE + PUnsignedFloat.INSTANCE.getSqlType(),
-        PhoenixArray.class, null, 46);
-  }
-
-  @Override
-  public boolean isArrayType() {
-    return true;
-  }
-
-  @Override
-  public boolean isFixedWidth() {
-    return false;
-  }
+    public static final PUnsignedFloatArray INSTANCE = new PUnsignedFloatArray();
 
-  @Override
-  public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
-    return compareTo(lhs, rhs);
-  }
+    private PUnsignedFloatArray() {
+        super("UNSIGNED_FLOAT ARRAY", PDataType.ARRAY_TYPE_BASE + PUnsignedFloat.INSTANCE.getSqlType(),
+                PhoenixArray.class, null, 46);
+    }
 
-  @Override
-  public Integer getByteSize() {
-    return null;
-  }
+    @Override
+    public boolean isArrayType() {
+        return true;
+    }
 
-  @Override
-  public byte[] toBytes(Object object) {
-    return toBytes(object, SortOrder.ASC);
-  }
+    @Override
+    public boolean isFixedWidth() {
+        return false;
+    }
 
-  @Override
-  public byte[] toBytes(Object object, SortOrder sortOrder) {
-    return toBytes(object, PUnsignedFloat.INSTANCE, sortOrder);
-  }
+    @Override
+    public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
+        return compareTo(lhs, rhs);
+    }
 
-  @Override
-  public Object toObject(byte[] bytes, int offset, int length,
-      PDataType actualType, SortOrder sortOrder, Integer maxLength,
-      Integer scale) {
-    return toObject(bytes, offset, length, PUnsignedFloat.INSTANCE, sortOrder, maxLength,
-        scale, PUnsignedFloat.INSTANCE);
-  }
+    @Override
+    public Integer getByteSize() {
+        return null;
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType) {
-    return isCoercibleTo(targetType, this);
-  }
+    @Override
+    public byte[] toBytes(Object object) {
+        return toBytes(object, SortOrder.ASC);
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType, Object value) {
-    if (value == null) {
-      return true;
+    @Override
+    public byte[] toBytes(Object object, SortOrder sortOrder) {
+        return toBytes(object, PUnsignedFloat.INSTANCE, sortOrder);
     }
-    PhoenixArray pArr = (PhoenixArray) value;
-    Object[] floatArr = (Object[]) pArr.array;
-    for (Object i : floatArr) {
-      if (!super.isCoercibleTo(PUnsignedFloat.INSTANCE, i)) {
-        return false;
-      }
+
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length,
+            PDataType actualType, SortOrder sortOrder, Integer maxLength,
+            Integer scale) {
+        return toObject(bytes, offset, length, PUnsignedFloat.INSTANCE, sortOrder, maxLength,
+                scale, PUnsignedFloat.INSTANCE);
     }
-    return true;
-  }
 
-  @Override
-  public int getResultSetSqlType() {
-    return Types.ARRAY;
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType) {
+        return isCoercibleTo(targetType, this);
+    }
 
-  @Override
-  public void coerceBytes(ImmutableBytesWritable ptr, Object object, PDataType actualType,
-      Integer maxLength, Integer scale, SortOrder actualModifer, Integer desiredMaxLength,
-      Integer desiredScale, SortOrder desiredModifier) {
-    coerceBytes(ptr, object, actualType, maxLength, scale, desiredMaxLength, desiredScale,
-        this, actualModifer, desiredModifier);
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType, Object value) {
+        if (value == null) {
+            return true;
+        }
+        PhoenixArray pArr = (PhoenixArray) value;
+        Object[] floatArr = (Object[]) pArr.array;
+        for (Object i : floatArr) {
+            if (!super.isCoercibleTo(PUnsignedFloat.INSTANCE, i)) {
+                return false;
+            }
+        }
+        return true;
+    }
 
-  @Override
-  public Object getSampleValue(Integer maxLength, Integer arrayLength) {
-    return getSampleValue(PUnsignedFloat.INSTANCE, arrayLength, maxLength);
-  }
+    @Override
+    public Object getSampleValue(Integer maxLength, Integer arrayLength) {
+        return getSampleValue(PUnsignedFloat.INSTANCE, arrayLength, maxLength);
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedIntArray.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedIntArray.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedIntArray.java
index 11e5f10..3d430e4 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedIntArray.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedIntArray.java
@@ -17,93 +17,77 @@
  */
 package org.apache.phoenix.schema.types;
 
-import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.phoenix.schema.SortOrder;
 
-import java.sql.Types;
-
 public class PUnsignedIntArray extends PArrayDataType<int[]> {
 
-  public static final PUnsignedIntArray INSTANCE = new PUnsignedIntArray();
-
-  private PUnsignedIntArray() {
-    super("UNSIGNED_INT ARRAY", PDataType.ARRAY_TYPE_BASE + PUnsignedInt.INSTANCE.getSqlType(),
-        PhoenixArray.class, null, 43);
-  }
-
-  @Override
-  public boolean isArrayType() {
-    return true;
-  }
-
-  @Override
-  public boolean isFixedWidth() {
-    return false;
-  }
+    public static final PUnsignedIntArray INSTANCE = new PUnsignedIntArray();
 
-  @Override
-  public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
-    return compareTo(lhs, rhs);
-  }
+    private PUnsignedIntArray() {
+        super("UNSIGNED_INT ARRAY", PDataType.ARRAY_TYPE_BASE + PUnsignedInt.INSTANCE.getSqlType(),
+                PhoenixArray.class, null, 43);
+    }
 
-  @Override
-  public Integer getByteSize() {
-    return null;
-  }
+    @Override
+    public boolean isArrayType() {
+        return true;
+    }
 
-  @Override
-  public byte[] toBytes(Object object) {
-    return toBytes(object, SortOrder.ASC);
-  }
+    @Override
+    public boolean isFixedWidth() {
+        return false;
+    }
 
-  @Override
-  public byte[] toBytes(Object object, SortOrder sortOrder) {
-    return toBytes(object, PUnsignedInt.INSTANCE, sortOrder);
-  }
+    @Override
+    public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
+        return compareTo(lhs, rhs);
+    }
 
-  @Override
-  public Object toObject(byte[] bytes, int offset, int length,
-      PDataType actualType, SortOrder sortOrder, Integer maxLength,
-      Integer scale) {
-    return toObject(bytes, offset, length, PUnsignedInt.INSTANCE, sortOrder, maxLength,
-        scale, PUnsignedInt.INSTANCE);
-  }
+    @Override
+    public Integer getByteSize() {
+        return null;
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType) {
-    return isCoercibleTo(targetType, this);
-  }
+    @Override
+    public byte[] toBytes(Object object) {
+        return toBytes(object, SortOrder.ASC);
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType, Object value) {
-    if (value == null) {
-      return true;
+    @Override
+    public byte[] toBytes(Object object, SortOrder sortOrder) {
+        return toBytes(object, PUnsignedInt.INSTANCE, sortOrder);
     }
-    PhoenixArray pArr = (PhoenixArray) value;
-    Object[] intArr = (Object[]) pArr.array;
-    for (Object i : intArr) {
-      if (!super.isCoercibleTo(PUnsignedInt.INSTANCE, i)) {
-        return false;
-      }
+
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length,
+            PDataType actualType, SortOrder sortOrder, Integer maxLength,
+            Integer scale) {
+        return toObject(bytes, offset, length, PUnsignedInt.INSTANCE, sortOrder, maxLength,
+                scale, PUnsignedInt.INSTANCE);
     }
-    return true;
-  }
 
-  @Override
-  public int getResultSetSqlType() {
-    return Types.ARRAY;
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType) {
+        return isCoercibleTo(targetType, this);
+    }
 
-  @Override
-  public void coerceBytes(ImmutableBytesWritable ptr, Object object, PDataType actualType,
-      Integer maxLength, Integer scale, SortOrder actualModifer, Integer desiredMaxLength,
-      Integer desiredScale, SortOrder desiredModifier) {
-    coerceBytes(ptr, object, actualType, maxLength, scale, desiredMaxLength, desiredScale,
-        this, actualModifer, desiredModifier);
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType, Object value) {
+        if (value == null) {
+            return true;
+        }
+        PhoenixArray pArr = (PhoenixArray) value;
+        Object[] intArr = (Object[]) pArr.array;
+        for (Object i : intArr) {
+            if (!super.isCoercibleTo(PUnsignedInt.INSTANCE, i)) {
+                return false;
+            }
+        }
+        return true;
+    }
 
-  @Override
-  public Object getSampleValue(Integer maxLength, Integer arrayLength) {
-    return getSampleValue(PUnsignedInt.INSTANCE, arrayLength, maxLength);
-  }
+    @Override
+    public Object getSampleValue(Integer maxLength, Integer arrayLength) {
+        return getSampleValue(PUnsignedInt.INSTANCE, arrayLength, maxLength);
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedLongArray.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedLongArray.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedLongArray.java
index dcbe210..2a7c5ef 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedLongArray.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedLongArray.java
@@ -17,93 +17,77 @@
  */
 package org.apache.phoenix.schema.types;
 
-import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.phoenix.schema.SortOrder;
 
-import java.sql.Types;
-
 public class PUnsignedLongArray extends PArrayDataType<long[]> {
 
-  public static final PUnsignedLongArray INSTANCE = new PUnsignedLongArray();
-
-  private PUnsignedLongArray() {
-    super("UNSIGNED_LONG ARRAY", PDataType.ARRAY_TYPE_BASE + PUnsignedLong.INSTANCE.getSqlType(),
-        PhoenixArray.class, null, 42);
-  }
-
-  @Override
-  public boolean isArrayType() {
-    return true;
-  }
-
-  @Override
-  public boolean isFixedWidth() {
-    return false;
-  }
+    public static final PUnsignedLongArray INSTANCE = new PUnsignedLongArray();
 
-  @Override
-  public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
-    return compareTo(lhs, rhs);
-  }
+    private PUnsignedLongArray() {
+        super("UNSIGNED_LONG ARRAY", PDataType.ARRAY_TYPE_BASE + PUnsignedLong.INSTANCE.getSqlType(),
+                PhoenixArray.class, null, 42);
+    }
 
-  @Override
-  public Integer getByteSize() {
-    return null;
-  }
+    @Override
+    public boolean isArrayType() {
+        return true;
+    }
 
-  @Override
-  public byte[] toBytes(Object object) {
-    return toBytes(object, SortOrder.ASC);
-  }
+    @Override
+    public boolean isFixedWidth() {
+        return false;
+    }
 
-  @Override
-  public byte[] toBytes(Object object, SortOrder sortOrder) {
-    return toBytes(object, PUnsignedLong.INSTANCE, sortOrder);
-  }
+    @Override
+    public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
+        return compareTo(lhs, rhs);
+    }
 
-  @Override
-  public Object toObject(byte[] bytes, int offset, int length,
-      PDataType actualType, SortOrder sortOrder, Integer maxLength,
-      Integer scale) {
-    return toObject(bytes, offset, length, PUnsignedLong.INSTANCE, sortOrder, maxLength,
-        scale, PUnsignedLong.INSTANCE);
-  }
+    @Override
+    public Integer getByteSize() {
+        return null;
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType) {
-    return isCoercibleTo(targetType, this);
-  }
+    @Override
+    public byte[] toBytes(Object object) {
+        return toBytes(object, SortOrder.ASC);
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType, Object value) {
-    if (value == null) {
-      return true;
+    @Override
+    public byte[] toBytes(Object object, SortOrder sortOrder) {
+        return toBytes(object, PUnsignedLong.INSTANCE, sortOrder);
     }
-    PhoenixArray pArr = (PhoenixArray) value;
-    Object[] longArr = (Object[]) pArr.array;
-    for (Object i : longArr) {
-      if (!super.isCoercibleTo(PUnsignedLong.INSTANCE, i)) {
-        return false;
-      }
+
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length,
+            PDataType actualType, SortOrder sortOrder, Integer maxLength,
+            Integer scale) {
+        return toObject(bytes, offset, length, PUnsignedLong.INSTANCE, sortOrder, maxLength,
+                scale, PUnsignedLong.INSTANCE);
     }
-    return true;
-  }
 
-  @Override
-  public int getResultSetSqlType() {
-    return Types.ARRAY;
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType) {
+        return isCoercibleTo(targetType, this);
+    }
 
-  @Override
-  public void coerceBytes(ImmutableBytesWritable ptr, Object object, PDataType actualType,
-      Integer maxLength, Integer scale, SortOrder actualModifer, Integer desiredMaxLength,
-      Integer desiredScale, SortOrder desiredModifier) {
-    coerceBytes(ptr, object, actualType, maxLength, scale, desiredMaxLength, desiredScale,
-        this, actualModifer, desiredModifier);
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType, Object value) {
+        if (value == null) {
+            return true;
+        }
+        PhoenixArray pArr = (PhoenixArray) value;
+        Object[] longArr = (Object[]) pArr.array;
+        for (Object i : longArr) {
+            if (!super.isCoercibleTo(PUnsignedLong.INSTANCE, i)) {
+                return false;
+            }
+        }
+        return true;
+    }
 
-  @Override
-  public Object getSampleValue(Integer maxLength, Integer arrayLength) {
-    return getSampleValue(PUnsignedLong.INSTANCE, arrayLength, maxLength);
-  }
+    @Override
+    public Object getSampleValue(Integer maxLength, Integer arrayLength) {
+        return getSampleValue(PUnsignedLong.INSTANCE, arrayLength, maxLength);
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedSmallintArray.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedSmallintArray.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedSmallintArray.java
index 36823c2..f481efb 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedSmallintArray.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedSmallintArray.java
@@ -17,94 +17,78 @@
  */
 package org.apache.phoenix.schema.types;
 
-import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.phoenix.schema.SortOrder;
 
-import java.sql.Types;
-
 public class PUnsignedSmallintArray extends PArrayDataType<short[]> {
 
-  public static final PUnsignedSmallintArray INSTANCE = new PUnsignedSmallintArray();
-
-  private PUnsignedSmallintArray() {
-    super("UNSIGNED_SMALLINT ARRAY",
-        PDataType.ARRAY_TYPE_BASE + PUnsignedSmallint.INSTANCE.getSqlType(), PhoenixArray.class,
-        null, 44);
-  }
-
-  @Override
-  public boolean isArrayType() {
-    return true;
-  }
-
-  @Override
-  public boolean isFixedWidth() {
-    return false;
-  }
+    public static final PUnsignedSmallintArray INSTANCE = new PUnsignedSmallintArray();
 
-  @Override
-  public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
-    return compareTo(lhs, rhs);
-  }
+    private PUnsignedSmallintArray() {
+        super("UNSIGNED_SMALLINT ARRAY",
+                PDataType.ARRAY_TYPE_BASE + PUnsignedSmallint.INSTANCE.getSqlType(), PhoenixArray.class,
+                null, 44);
+    }
 
-  @Override
-  public Integer getByteSize() {
-    return null;
-  }
+    @Override
+    public boolean isArrayType() {
+        return true;
+    }
 
-  @Override
-  public byte[] toBytes(Object object) {
-    return toBytes(object, SortOrder.ASC);
-  }
+    @Override
+    public boolean isFixedWidth() {
+        return false;
+    }
 
-  @Override
-  public byte[] toBytes(Object object, SortOrder sortOrder) {
-    return toBytes(object, PUnsignedSmallint.INSTANCE, sortOrder);
-  }
+    @Override
+    public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
+        return compareTo(lhs, rhs);
+    }
 
-  @Override
-  public Object toObject(byte[] bytes, int offset, int length,
-      PDataType actualType, SortOrder sortOrder, Integer maxLength,
-      Integer scale) {
-    return toObject(bytes, offset, length, PUnsignedSmallint.INSTANCE, sortOrder, maxLength,
-        scale, PUnsignedSmallint.INSTANCE);
-  }
+    @Override
+    public Integer getByteSize() {
+        return null;
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType) {
-    return isCoercibleTo(targetType, this);
-  }
+    @Override
+    public byte[] toBytes(Object object) {
+        return toBytes(object, SortOrder.ASC);
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType, Object value) {
-    if (value == null) {
-      return true;
+    @Override
+    public byte[] toBytes(Object object, SortOrder sortOrder) {
+        return toBytes(object, PUnsignedSmallint.INSTANCE, sortOrder);
     }
-    PhoenixArray pArr = (PhoenixArray) value;
-    Object[] shortArr = (Object[]) pArr.array;
-    for (Object i : shortArr) {
-      if (!super.isCoercibleTo(PUnsignedSmallint.INSTANCE, i)) {
-        return false;
-      }
+
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length,
+            PDataType actualType, SortOrder sortOrder, Integer maxLength,
+            Integer scale) {
+        return toObject(bytes, offset, length, PUnsignedSmallint.INSTANCE, sortOrder, maxLength,
+                scale, PUnsignedSmallint.INSTANCE);
     }
-    return true;
-  }
 
-  @Override
-  public int getResultSetSqlType() {
-    return Types.ARRAY;
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType) {
+        return isCoercibleTo(targetType, this);
+    }
 
-  @Override
-  public void coerceBytes(ImmutableBytesWritable ptr, Object object, PDataType actualType,
-      Integer maxLength, Integer scale, SortOrder actualModifer, Integer desiredMaxLength,
-      Integer desiredScale, SortOrder desiredModifier) {
-    coerceBytes(ptr, object, actualType, maxLength, scale, desiredMaxLength, desiredScale,
-        this, actualModifer, desiredModifier);
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType, Object value) {
+        if (value == null) {
+            return true;
+        }
+        PhoenixArray pArr = (PhoenixArray) value;
+        Object[] shortArr = (Object[]) pArr.array;
+        for (Object i : shortArr) {
+            if (!super.isCoercibleTo(PUnsignedSmallint.INSTANCE, i)) {
+                return false;
+            }
+        }
+        return true;
+    }
 
-  @Override
-  public Object getSampleValue(Integer maxLength, Integer arrayLength) {
-    return getSampleValue(PUnsignedSmallint.INSTANCE, arrayLength, maxLength);
-  }
+    @Override
+    public Object getSampleValue(Integer maxLength, Integer arrayLength) {
+        return getSampleValue(PUnsignedSmallint.INSTANCE, arrayLength, maxLength);
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/2620a80c/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedTimeArray.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedTimeArray.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedTimeArray.java
index 9d7584b..0851d48 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedTimeArray.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedTimeArray.java
@@ -17,93 +17,79 @@
  */
 package org.apache.phoenix.schema.types;
 
-import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
-import org.apache.phoenix.schema.SortOrder;
+import java.sql.Time;
 
-import java.sql.*;
+import org.apache.phoenix.schema.SortOrder;
 
 public class PUnsignedTimeArray extends PArrayDataType<Time[]> {
 
-  public static final PUnsignedTimeArray INSTANCE = new PUnsignedTimeArray();
-
-  private PUnsignedTimeArray() {
-    super("UNSIGNED_TIME ARRAY", PDataType.ARRAY_TYPE_BASE + PUnsignedTime.INSTANCE.getSqlType(),
-        PhoenixArray.class, null, 39);
-  }
-
-  @Override
-  public boolean isArrayType() {
-    return true;
-  }
-
-  @Override
-  public boolean isFixedWidth() {
-    return false;
-  }
+    public static final PUnsignedTimeArray INSTANCE = new PUnsignedTimeArray();
 
-  @Override
-  public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
-    return compareTo(lhs, rhs);
-  }
+    private PUnsignedTimeArray() {
+        super("UNSIGNED_TIME ARRAY", PDataType.ARRAY_TYPE_BASE + PUnsignedTime.INSTANCE.getSqlType(),
+                PhoenixArray.class, null, 39);
+    }
 
-  @Override
-  public Integer getByteSize() {
-    return null;
-  }
+    @Override
+    public boolean isArrayType() {
+        return true;
+    }
 
-  @Override
-  public byte[] toBytes(Object object) {
-    return toBytes(object, SortOrder.ASC);
-  }
+    @Override
+    public boolean isFixedWidth() {
+        return false;
+    }
 
-  @Override
-  public byte[] toBytes(Object object, SortOrder sortOrder) {
-    return toBytes(object, PUnsignedTime.INSTANCE, sortOrder);
-  }
+    @Override
+    public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
+        return compareTo(lhs, rhs);
+    }
 
-  @Override
-  public Object toObject(byte[] bytes, int offset, int length,
-      PDataType actualType, SortOrder sortOrder, Integer maxLength,
-      Integer scale) {
-    return toObject(bytes, offset, length, PUnsignedTime.INSTANCE, sortOrder, maxLength,
-        scale, PUnsignedTime.INSTANCE);
-  }
+    @Override
+    public Integer getByteSize() {
+        return null;
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType) {
-    return isCoercibleTo(targetType, this);
-  }
+    @Override
+    public byte[] toBytes(Object object) {
+        return toBytes(object, SortOrder.ASC);
+    }
 
-  @Override
-  public boolean isCoercibleTo(PDataType targetType, Object value) {
-    if (value == null) {
-      return true;
+    @Override
+    public byte[] toBytes(Object object, SortOrder sortOrder) {
+        return toBytes(object, PUnsignedTime.INSTANCE, sortOrder);
     }
-    PhoenixArray pArr = (PhoenixArray) value;
-    Object[] timeArr = (Object[]) pArr.array;
-    for (Object i : timeArr) {
-      if (!super.isCoercibleTo(PUnsignedTime.INSTANCE, i)) {
-        return false;
-      }
+
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length,
+            PDataType actualType, SortOrder sortOrder, Integer maxLength,
+            Integer scale) {
+        return toObject(bytes, offset, length, PUnsignedTime.INSTANCE, sortOrder, maxLength,
+                scale, PUnsignedTime.INSTANCE);
     }
-    return true;
-  }
 
-  @Override
-  public void coerceBytes(ImmutableBytesWritable ptr, Object object, PDataType actualType,
-      Integer maxLength, Integer scale, SortOrder actualModifer, Integer desiredMaxLength,
-      Integer desiredScale, SortOrder desiredModifier) {
-    coerceBytes(ptr, object, actualType, maxLength, scale, desiredMaxLength, desiredScale,
-        this, actualModifer, desiredModifier);
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType) {
+        return isCoercibleTo(targetType, this);
+    }
 
-  @Override
-  public int getResultSetSqlType() {
-    return Types.ARRAY;
-  }
+    @Override
+    public boolean isCoercibleTo(PDataType targetType, Object value) {
+        if (value == null) {
+            return true;
+        }
+        PhoenixArray pArr = (PhoenixArray) value;
+        Object[] timeArr = (Object[]) pArr.array;
+        for (Object i : timeArr) {
+            if (!super.isCoercibleTo(PUnsignedTime.INSTANCE, i)) {
+                return false;
+            }
+        }
+        return true;
+    }
 
-  @Override
-  public Object getSampleValue(Integer maxLength, Integer arrayLength) {
-    return getSampleValue(PUnsignedTime.INSTANCE, arrayLength, maxLength);
-  }
+    @Override
+    public Object getSampleValue(Integer maxLength, Integer arrayLength) {
+        return getSampleValue(PUnsignedTime.INSTANCE, arrayLength, maxLength);
+    }
 }


[19/50] [abbrv] phoenix git commit: PHOENIX-1954 Reserve chunks of numbers for a sequence (Jan Fernando)

Posted by ma...@apache.org.
PHOENIX-1954 Reserve chunks of numbers for a sequence (Jan Fernando)


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/3b1bfa0d
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/3b1bfa0d
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/3b1bfa0d

Branch: refs/heads/calcite
Commit: 3b1bfa0d7b83f0b9ee0ad535d6e1f99777c14cb6
Parents: 984e622
Author: James Taylor <jt...@salesforce.com>
Authored: Thu Jul 9 20:49:03 2015 -0700
Committer: James Taylor <jt...@salesforce.com>
Committed: Thu Jul 9 20:56:48 2015 -0700

----------------------------------------------------------------------
 .../end2end/SequenceBulkAllocationIT.java       | 1286 ++++++++++++++++++
 phoenix-core/src/main/antlr3/PhoenixSQL.g       |    5 +-
 .../apache/phoenix/compile/SequenceManager.java |   70 +-
 .../compile/SequenceValueExpression.java        |   14 +-
 .../coprocessor/SequenceRegionObserver.java     |   64 +-
 .../phoenix/exception/SQLExceptionCode.java     |    2 +
 .../apache/phoenix/jdbc/PhoenixStatement.java   |   10 +-
 .../apache/phoenix/parse/ParseNodeFactory.java  |    6 +-
 .../phoenix/parse/SequenceValueParseNode.java   |    8 +-
 .../phoenix/query/ConnectionQueryServices.java  |    5 +-
 .../query/ConnectionQueryServicesImpl.java      |   23 +-
 .../query/ConnectionlessQueryServicesImpl.java  |   20 +-
 .../query/DelegateConnectionQueryServices.java  |   13 +-
 .../apache/phoenix/schema/MetaDataClient.java   |    2 +-
 .../org/apache/phoenix/schema/Sequence.java     |  107 +-
 .../phoenix/schema/SequenceAllocation.java      |   48 +
 .../org/apache/phoenix/util/SequenceUtil.java   |   47 +-
 .../phoenix/schema/SequenceAllocationTest.java  |   59 +
 .../apache/phoenix/util/SequenceUtilTest.java   |   54 +
 19 files changed, 1763 insertions(+), 80 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/3b1bfa0d/phoenix-core/src/it/java/org/apache/phoenix/end2end/SequenceBulkAllocationIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SequenceBulkAllocationIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SequenceBulkAllocationIT.java
new file mode 100644
index 0000000..e7db1ec
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SequenceBulkAllocationIT.java
@@ -0,0 +1,1286 @@
+/*
+ * 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.end2end;
+
+import static org.apache.phoenix.util.PhoenixRuntime.TENANT_ID_ATTRIB;
+import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.phoenix.exception.SQLExceptionCode;
+import org.apache.phoenix.query.QueryServices;
+import org.apache.phoenix.util.PhoenixRuntime;
+import org.apache.phoenix.util.PropertiesUtil;
+import org.apache.phoenix.util.QueryUtil;
+import org.apache.phoenix.util.ReadOnlyProps;
+import org.junit.After;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import com.google.common.collect.Lists;
+
+/**
+ * Suite of integration tests that validate that Bulk Allocation of Sequence values
+ * using the NEXT <n> VALUES FOR <seq> syntax works as expected and interacts
+ * correctly with NEXT VALUE FOR <seq> and CURRENT VALUE FOR <seq>.
+ * 
+ * All tests are run with both a generic connection and a multi-tenant connection.
+ * 
+ */
+@RunWith(Parameterized.class)
+public class SequenceBulkAllocationIT extends BaseClientManagedTimeIT {
+
+    private static final long BATCH_SIZE = 3;
+    private static final String SELECT_NEXT_VALUE_SQL =
+            "SELECT NEXT VALUE FOR %s FROM SYSTEM.\"SEQUENCE\" LIMIT 1";
+    private static final String SELECT_CURRENT_VALUE_SQL =
+            "SELECT CURRENT VALUE FOR %s FROM SYSTEM.\"SEQUENCE\" LIMIT 1";
+    private static final String CREATE_SEQUENCE_NO_MIN_MAX_TEMPLATE =
+            "CREATE SEQUENCE bulkalloc.alpha START WITH %s INCREMENT BY %s CACHE %s";
+    private static final String CREATE_SEQUENCE_WITH_MIN_MAX_TEMPLATE =
+            "CREATE SEQUENCE bulkalloc.alpha START WITH %s INCREMENT BY %s MINVALUE %s MAXVALUE %s CACHE %s";
+    private static final String CREATE_SEQUENCE_WITH_MIN_MAX_AND_CYCLE_TEMPLATE =
+            "CREATE SEQUENCE bulkalloc.alpha START WITH %s INCREMENT BY %s MINVALUE %s MAXVALUE %s CYCLE CACHE %s";
+
+    
+    private Connection conn;
+    private String tenantId;
+    
+    public SequenceBulkAllocationIT(String tenantId) {
+        this.tenantId = tenantId;
+    }
+
+    @BeforeClass
+    @Shadower(classBeingShadowed = BaseClientManagedTimeIT.class)
+    public static void doSetup() throws Exception {
+        Map<String, String> props = getDefaultProps();
+        // Must update config before starting server
+        props.put(QueryServices.SEQUENCE_CACHE_SIZE_ATTRIB, Long.toString(BATCH_SIZE));
+        setUpTestDriver(new ReadOnlyProps(props.entrySet().iterator()));
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        // close any open connection between tests, so that connections are not leaked
+        if (conn != null) {
+            conn.close();
+        }
+    }
+    
+    @Parameters
+    public static Object[] data() {
+        return new Object[] {null, "tenant1"};
+    }
+    
+    
+    @Test
+    public void testSequenceParseNextValuesWithNull() throws Exception {
+        nextConnection();
+        try {
+            conn.createStatement().executeQuery(
+                "SELECT NEXT NULL VALUES FOR bulkalloc.alpha FROM SYSTEM.\"SEQUENCE\" LIMIT 1");
+            fail("null is not allowed to be used for <n> in NEXT <n> VALUES FOR <seq>");
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.NUM_SEQ_TO_ALLOCATE_MUST_BE_CONSTANT.getErrorCode(),
+                e.getErrorCode());
+            assertTrue(e.getNextException() == null);
+        }
+    }
+    
+    @Test
+    public void testSequenceParseNextValuesWithNonNumber() throws Exception {
+        nextConnection();    
+        try {
+            conn.createStatement().executeQuery(
+                "SELECT NEXT '89b' VALUES FOR bulkalloc.alpha FROM SYSTEM.\"SEQUENCE\" LIMIT 1");
+            fail("Only integers and longs are allowed to be used for <n> in NEXT <n> VALUES FOR <seq>");
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.NUM_SEQ_TO_ALLOCATE_MUST_BE_CONSTANT.getErrorCode(),
+                e.getErrorCode());
+            assertTrue(e.getNextException() == null);
+        }
+    }
+    
+    
+    @Test
+    public void testSequenceParseNextValuesWithNegativeNumber() throws Exception {
+        nextConnection();
+        try {
+            conn.createStatement().executeQuery(
+                "SELECT NEXT '-1' VALUES FOR bulkalloc.alpha FROM SYSTEM.\"SEQUENCE\" LIMIT 1");
+            fail("null is not allowed to be used for <n> in NEXT <n> VALUES FOR <seq>");
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.NUM_SEQ_TO_ALLOCATE_MUST_BE_CONSTANT.getErrorCode(),
+                e.getErrorCode());
+            assertTrue(e.getNextException() == null);
+        }
+    }
+
+    @Test
+    public void testParseNextValuesSequenceWithZeroAllocated() throws Exception {
+        nextConnection();    
+        try {
+            conn.createStatement().executeQuery(
+                "SELECT NEXT 0 VALUES FOR bulkalloc.alpha FROM SYSTEM.\"SEQUENCE\" LIMIT 1");
+            fail("Only integers and longs are allowed to be used for <n> in NEXT <n> VALUES FOR <seq>");
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.NUM_SEQ_TO_ALLOCATE_MUST_BE_CONSTANT.getErrorCode(),
+                e.getErrorCode());
+            assertTrue(e.getNextException() == null);
+        }
+    }
+    
+
+    @Test
+    public void testNextValuesForSequenceWithNoAllocatedValues() throws Exception {
+        // Create Sequence
+        final SequenceProperties props =
+                new SequenceProperties.Builder().incrementBy(1).startsWith(1).cacheSize(1)
+                        .numAllocated(100).build();
+
+        nextConnection();
+        createSequenceWithNoMinMax(props);
+        nextConnection();
+
+        // Bulk Allocate Sequence Slots
+        final int currentValueAfterAllocation = 100;
+        reserveSlotsInBulkAndAssertValue(1, props.numAllocated);
+        assertExpectedStateInSystemSequence(props, 101);
+        assertExpectedNumberOfValuesAllocated(1, currentValueAfterAllocation, props.incrementBy, props.numAllocated);
+        
+        // Assert standard Sequence Operations return expected values
+        assertExpectedCurrentValueForSequence(currentValueAfterAllocation);
+        assertExpectedNextValueForSequence(101);
+    }
+    
+    @Test
+    /**
+     * Validates we can invoke NEXT <n> VALUES FOR using bind vars.
+     */
+    public void testNextValuesForSequenceUsingBinds() throws Exception {
+        
+        // Create Sequence
+        final SequenceProperties props =
+                new SequenceProperties.Builder().incrementBy(1).startsWith(1).cacheSize(1)
+                        .numAllocated(100).build();
+
+        nextConnection();
+        createSequenceWithNoMinMax(props);
+        nextConnection();
+        
+        // Allocate 100 slots using SQL with Bind Params and a PreparedStatement
+        final int currentValueAfterAllocation = 100;
+        reserveSlotsInBulkUsingBindsAndAssertValue(1,props.numAllocated);
+        assertExpectedStateInSystemSequence(props, 101);
+        assertExpectedNumberOfValuesAllocated(1, currentValueAfterAllocation, props.incrementBy, props.numAllocated);
+
+        // Assert standard Sequence Operations return expected values
+        assertExpectedCurrentValueForSequence(currentValueAfterAllocation);
+        assertExpectedNextValueForSequence(101);
+    }
+    
+
+    @Test
+    public void testNextValuesForSequenceWithPreviouslyAllocatedValues() throws Exception {
+        final SequenceProperties props =
+                new SequenceProperties.Builder().incrementBy(1).startsWith(1).cacheSize(100)
+                        .numAllocated(1000).build();
+
+        nextConnection();
+        createSequenceWithNoMinMax(props);
+        nextConnection();
+
+        assertExpectedNextValueForSequence(1);
+        assertExpectedCurrentValueForSequence(1);
+        assertExpectedNextValueForSequence(2);
+
+        // Bulk Allocate Sequence Slots
+        int currentValueAfterAllocation = 1100;
+        int nextValueAfterAllocation = currentValueAfterAllocation + props.incrementBy;
+        int startValueAfterAllocation = 101;
+        reserveSlotsInBulkAndAssertValue(startValueAfterAllocation, props.numAllocated);
+        assertBulkAllocationSucceeded(props, currentValueAfterAllocation, startValueAfterAllocation);
+
+        // Assert standard Sequence Operations return expected values
+        assertExpectedCurrentValueForSequence(currentValueAfterAllocation);
+        assertExpectedNextValueForSequence(nextValueAfterAllocation);
+    }
+   
+    
+    @Test
+    /**
+     * Validates that if we close a connection after performing 
+     * NEXT <n> VALUES FOR <seq> the values are correctly returned from
+     * the latest batch.
+     */
+    public void testConnectionCloseReturnsSequenceValuesCorrectly() throws Exception {
+        final SequenceProperties props =
+                new SequenceProperties.Builder().incrementBy(2).startsWith(1).cacheSize(100)
+                        .numAllocated(100).build();
+        nextConnection();
+        createSequenceWithNoMinMax(props);
+        nextConnection();
+        
+        assertExpectedNextValueForSequence(1);
+        assertExpectedCurrentValueForSequence(1);
+        assertExpectedNextValueForSequence(3);
+        
+        // Bulk Allocate Sequence Slots
+        int currentValueAfterAllocation = 399;
+        int nextValueAfterAllocation = currentValueAfterAllocation + props.incrementBy;
+        int startValueAfterAllocation = 201;
+        reserveSlotsInBulkAndAssertValue(startValueAfterAllocation, props.numAllocated);
+        assertExpectedCurrentValueForSequence(currentValueAfterAllocation);
+        
+        // Close the Connection
+        conn.close();
+        
+        // Test that sequence, doesn't have gaps after closing the connection
+        nextConnection();
+        assertExpectedNextValueForSequence(nextValueAfterAllocation);
+        assertExpectedCurrentValueForSequence(nextValueAfterAllocation);
+
+    }
+    
+    @Test
+    /**
+     * Validates that calling NEXT <n> VALUES FOR <seq> works correctly with UPSERT.
+     */
+    public void testNextValuesForSequenceWithUpsert() throws Exception {
+        
+        // Create Sequence
+        final SequenceProperties props =
+                new SequenceProperties.Builder().incrementBy(1).startsWith(1).cacheSize(100)
+                        .numAllocated(1000).build();
+        nextConnection();
+        createSequenceWithNoMinMax(props);
+
+        // Create TABLE
+        nextGenericConnection();
+        conn.createStatement().execute("CREATE TABLE bulkalloc.test ( id INTEGER NOT NULL PRIMARY KEY)");
+        nextConnection();
+        
+        // Grab batch from Sequence
+        assertExpectedNextValueForSequence(1);
+        assertExpectedCurrentValueForSequence(1);
+        assertExpectedNextValueForSequence(2);
+        assertExpectedStateInSystemSequence(props, 101);
+        
+        
+        // Perform UPSERT and validate Sequence was incremented as expected
+        conn.createStatement().execute("UPSERT INTO bulkalloc.test (id) VALUES (NEXT " + props.numAllocated +  " VALUES FOR bulkalloc.alpha)");
+        conn.commit();
+        assertExpectedStateInSystemSequence(props, 1101);
+        
+        // SELECT values out and verify
+        nextConnection();
+        String query = "SELECT id, NEXT VALUE FOR bulkalloc.alpha FROM bulkalloc.test";
+        ResultSet rs = conn.prepareStatement(query).executeQuery();
+        assertTrue(rs.next());
+        assertEquals(101, rs.getInt(1)); // Threw out cache of 100, incremented by 1000
+        assertEquals(1101, rs.getInt(2));
+        assertFalse(rs.next());
+    }
+
+
+
+
+    @Test
+    public void testNextValuesForSequenceWithIncrementBy() throws Exception {
+        final SequenceProperties props =
+                new SequenceProperties.Builder().incrementBy(3).startsWith(1).cacheSize(100)
+                        .numAllocated(1000).build();
+        nextConnection();
+        createSequenceWithNoMinMax(props);
+        nextConnection();
+
+        assertExpectedNextValueForSequence(1);
+        assertExpectedCurrentValueForSequence(1);
+        assertExpectedNextValueForSequence(4);
+
+        // Bulk Allocate Sequence Slots
+        int currentValueAfterAllocation = 3298;
+        int startValueAfterAllocation = 301;
+        reserveSlotsInBulkAndAssertValue(startValueAfterAllocation, props.numAllocated);
+        assertBulkAllocationSucceeded(props, currentValueAfterAllocation, startValueAfterAllocation);
+
+        // Assert standard Sequence Operations return expected values
+        assertExpectedCurrentValueForSequence(3298);
+        assertExpectedNextValueForSequence(3301);
+    }
+    
+    @Test
+    public void testNextValuesForSequenceWithNegativeIncrementBy() throws Exception {
+        final SequenceProperties props =
+                new SequenceProperties.Builder().incrementBy(-1).startsWith(2000).cacheSize(100)
+                        .numAllocated(1000).build();
+        nextConnection();
+        createSequenceWithNoMinMax(props);
+        nextConnection();
+
+        assertExpectedNextValueForSequence(2000);
+        assertExpectedCurrentValueForSequence(2000);
+        assertExpectedNextValueForSequence(1999);
+
+        // Bulk Allocate Sequence Slots
+        int currentValueAfterAllocation = 901;
+        int startValueAfterAllocation = 1900;
+        reserveSlotsInBulkAndAssertValue(startValueAfterAllocation, props.numAllocated);
+        assertBulkAllocationSucceeded(props, currentValueAfterAllocation, startValueAfterAllocation);
+
+
+        // Assert standard Sequence Operations return expected values
+        assertExpectedCurrentValueForSequence(901);
+        assertExpectedNextValueForSequence(900);
+    }
+
+    @Test
+    public void testNextValuesForSequenceWithNegativeIncrementByGreaterThanOne() throws Exception {
+        final SequenceProperties props =
+                new SequenceProperties.Builder().incrementBy(-5).startsWith(2000).cacheSize(100)
+                        .numAllocated(100).build();
+        nextConnection();
+        createSequenceWithNoMinMax(props);
+        nextConnection();
+        
+        // Pull first batch from Sequence
+        assertExpectedNextValueForSequence(2000);
+        assertExpectedCurrentValueForSequence(2000);
+        assertExpectedNextValueForSequence(1995);
+
+        // Bulk Allocate Sequence Slots
+        int currentValueAfterAllocation = 1005;
+        int startValueAfterAllocation = 1500;
+        reserveSlotsInBulkAndAssertValue(startValueAfterAllocation, props.numAllocated);
+        assertBulkAllocationSucceeded(props, currentValueAfterAllocation, startValueAfterAllocation);
+        
+
+        // Assert standard Sequence Operations return expected values
+        assertExpectedCurrentValueForSequence(1005);
+        assertExpectedNextValueForSequence(1000);
+    }
+    
+    
+    @Test
+    /**
+     * Validates that for NEXT <n> VALUES FOR if you try an allocate more slots such that that
+     * we exceed the max value of the sequence we throw an exception. Allocating sequence values in bulk
+     * should be an all or nothing operation - if the operation succeeds clients are guaranteed that they
+     * have access to all slots requested.
+     */
+    public void testNextValuesForSequenceExceedsMaxValue() throws Exception {
+        final SequenceProperties sequenceProps =
+                new SequenceProperties.Builder().incrementBy(1).startsWith(100).cacheSize(100)
+                        .numAllocated(1000).minValue(100).maxValue(900).build();
+
+        nextConnection();
+        createSequenceWithMinMax(sequenceProps);
+        nextConnection();
+
+        // Pull first batch from the sequence
+        assertExpectedNextValueForSequence(100);
+        assertExpectedCurrentValueForSequence(100);
+        assertExpectedNextValueForSequence(101);
+
+        // Attempt to bulk Allocate more slots than available
+        try {
+            conn.createStatement().executeQuery(
+                        "SELECT NEXT " + sequenceProps.numAllocated
+                                + " VALUES FOR bulkalloc.alpha FROM SYSTEM.\"SEQUENCE\" LIMIT 1");
+            fail("Invoking SELECT NEXT VALUES should have thrown Reached Max Value Exception");
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.SEQUENCE_VAL_REACHED_MAX_VALUE.getErrorCode(),
+                e.getErrorCode());
+            assertTrue(e.getNextException() == null);
+        }
+
+        // Assert sequence didn't advance
+        assertExpectedCurrentValueForSequence(101);
+        assertExpectedNextValueForSequence(102);
+    }
+
+    @Test
+    /**
+     * Validates that for NEXT <n> VALUES FOR if you try an allocate more slots such that that
+     * we exceed the min value of the sequence we throw an exception. Allocating sequence values in bulk
+     * should be an all or nothing operation - if the operation succeeds clients are guaranteed that they
+     * have access to all slots requested.
+     */
+    public void testNextValuesForSequenceExceedsMinValue() throws Exception {
+        final SequenceProperties sequenceProps =
+                new SequenceProperties.Builder().incrementBy(-5).startsWith(900).cacheSize(100)
+                        .numAllocated(160).minValue(100).maxValue(900).build();
+
+        nextConnection();
+        createSequenceWithMinMax(sequenceProps);
+        nextConnection();
+
+        // Pull first batch from the sequence
+        assertExpectedNextValueForSequence(900);
+        assertExpectedCurrentValueForSequence(900);
+        assertExpectedNextValueForSequence(895);
+
+        // Attempt to bulk Allocate more slots than available
+        try {
+            conn.createStatement().executeQuery(
+                        "SELECT NEXT " + sequenceProps.numAllocated
+                                + " VALUES FOR bulkalloc.alpha FROM SYSTEM.\"SEQUENCE\" LIMIT 1");
+            fail("Invoking SELECT NEXT VALUES should have thrown Reached Max Value Exception");
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.SEQUENCE_VAL_REACHED_MIN_VALUE.getErrorCode(),
+                e.getErrorCode());
+            assertTrue(e.getNextException() == null);
+        }
+
+        // Assert sequence didn't advance (we still throw out the cached values)
+        assertExpectedCurrentValueForSequence(895);
+        assertExpectedNextValueForSequence(890);
+    }
+
+    
+    @Test
+    /**
+     * Validates that if we don't exceed the limit bulk allocation works with sequences with a 
+     * min and max defined.
+     */
+    public void testNextValuesForSequenceWithMinMaxDefined() throws Exception {
+        final SequenceProperties props =
+                new SequenceProperties.Builder().incrementBy(5).startsWith(100).cacheSize(100)
+                        .numAllocated(1000).minValue(100).maxValue(6000).build();
+
+        nextConnection();
+        createSequenceWithMinMax(props);
+        nextConnection();
+
+        assertExpectedNextValueForSequence(100);
+        assertExpectedCurrentValueForSequence(100);
+        assertExpectedNextValueForSequence(105);
+
+        // Bulk Allocate Sequence Slots
+        int currentValueAfterAllocation = 5595;
+        int startValueAfterAllocation = 600;
+        reserveSlotsInBulkAndAssertValue(startValueAfterAllocation, props.numAllocated);
+        assertBulkAllocationSucceeded(props, currentValueAfterAllocation, startValueAfterAllocation);
+
+        // Assert standard Sequence Operations return expected values
+        assertExpectedCurrentValueForSequence(5595);
+        assertExpectedNextValueForSequence(5600);
+    }
+    
+    @Test
+    public void testNextValuesForSequenceWithDefaultMax() throws Exception {
+        final SequenceProperties props =
+                new SequenceProperties.Builder().incrementBy(1).startsWith(100).cacheSize(100)
+                        .numAllocated(Long.MAX_VALUE - 100).build();
+        
+        // Create Sequence
+        nextConnection();
+        createSequenceWithMinMax(props);
+        nextConnection();
+
+        // Bulk Allocate Sequence Slots
+        long currentValueAfterAllocation = 100;
+        long startValueAfterAllocation = Long.MAX_VALUE;
+        reserveSlotsInBulkAndAssertValue(currentValueAfterAllocation, props.numAllocated);
+        assertExpectedStateInSystemSequence(props, startValueAfterAllocation);
+        
+        // Try and get next value
+        try {
+            conn.createStatement().executeQuery(String.format(SELECT_NEXT_VALUE_SQL, "bulkalloc.alpha"));
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.SEQUENCE_VAL_REACHED_MAX_VALUE.getErrorCode(),
+                e.getErrorCode());
+            assertTrue(e.getNextException() == null);
+        }
+    }
+    
+    @Test
+    /**
+     * Validates that if our current or start value is > 0 and we ask for Long.MAX
+     * and overflow to the next value, the correct Exception is thrown when 
+     * the expression is evaluated.
+     */
+    public void testNextValuesForSequenceOverflowAllocation() throws Exception {
+        final SequenceProperties props =
+                new SequenceProperties.Builder().incrementBy(1).startsWith(100).cacheSize(100)
+                        .numAllocated(Long.MAX_VALUE).build();
+        
+        // Create Sequence
+        nextConnection();
+        createSequenceWithMinMax(props);
+        nextConnection();
+
+        // Bulk Allocate Sequence Slots
+        try {
+            conn.createStatement().executeQuery(
+                "SELECT NEXT " + Long.MAX_VALUE
+                        + " VALUES FOR bulkalloc.alpha FROM SYSTEM.\"SEQUENCE\"");
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.SEQUENCE_VAL_REACHED_MAX_VALUE.getErrorCode(),
+                e.getErrorCode());
+            assertTrue(e.getNextException() == null);
+        }
+    }
+    
+    
+    @Test
+    /**
+     * Validates that specifying an bulk allocation less than the size of the cache defined on the sequence works
+     * as expected.
+     */
+    public void testNextValuesForSequenceAllocationLessThanCacheSize() throws Exception {
+        final SequenceProperties props =
+                new SequenceProperties.Builder().incrementBy(5).startsWith(100).cacheSize(100)
+                        .numAllocated(50).minValue(100).maxValue(6000).build();
+
+        nextConnection();
+        createSequenceWithMinMax(props);
+        nextConnection();
+
+        assertExpectedNextValueForSequence(100);
+        assertExpectedCurrentValueForSequence(100);
+        assertExpectedNextValueForSequence(105);
+
+        // Bulk Allocate Sequence Slots
+        int currentValueAfterAllocation = 355;
+        int startValueAfterAllocation = 110;
+        reserveSlotsInBulkAndAssertValue(startValueAfterAllocation, props.numAllocated);
+        assertExpectedStateInSystemSequence(props, 600);
+        assertExpectedNumberOfValuesAllocated(startValueAfterAllocation, currentValueAfterAllocation, props.incrementBy, props.numAllocated);
+
+        // Assert standard Sequence Operations return expected values
+        // 105 + (50 * 5) = 355 
+        assertExpectedCurrentValueForSequence(355);
+        assertExpectedNextValueForSequence(360);
+        assertExpectedNextValueForSequence(365);
+        assertExpectedNextValueForSequence(370);
+    }
+    
+    @Test
+    /**
+     * Validates that specifying an bulk allocation less than the size of the cache defined on the sequence works
+     * as expected if we don't have enough values in the cache to support the allocation.
+     */
+    public void testNextValuesForInsufficentCacheValuesAllocationLessThanCacheSize() throws Exception {
+        final SequenceProperties props =
+                new SequenceProperties.Builder().incrementBy(5).startsWith(100).cacheSize(100)
+                        .numAllocated(50).minValue(100).maxValue(6000).build();
+
+        nextConnection();
+        createSequenceWithMinMax(props);
+        nextConnection();
+        
+        // Allocate 51 slots, only 49 will be left
+        int currentValueAfter51Allocations  = 355; // 100 + 51 * 5
+        for (int i = 100; i <= currentValueAfter51Allocations; i = i + 5) {
+            assertExpectedNextValueForSequence(i);
+        }
+        assertExpectedCurrentValueForSequence(currentValueAfter51Allocations);
+
+        // Bulk Allocate 50 Sequence Slots which greater than amount left in cache
+        // This should throw away rest of the cache, and allocate the request slot
+        // from the next start value
+        int currentValueAfterAllocation = 845;
+        int startValueAfterAllocation = 600;
+        reserveSlotsInBulkAndAssertValue(startValueAfterAllocation, props.numAllocated);
+        assertBulkAllocationSucceeded(props, currentValueAfterAllocation, startValueAfterAllocation);
+
+        // Assert standard Sequence Operations return expected values
+        assertExpectedCurrentValueForSequence(845);
+        assertExpectedNextValueForSequence(850);
+        assertExpectedNextValueForSequence(855);
+        assertExpectedNextValueForSequence(860);
+    }
+    
+    @Test
+    /**
+     * Validates that for NEXT <n> VALUES FOR is not supported on Sequences that have the
+     * CYCLE flag set to true.
+     */
+    public void testNextValuesForSequenceWithCycles() throws Exception {
+        final SequenceProperties sequenceProps =
+                new SequenceProperties.Builder().incrementBy(5).startsWith(100).cacheSize(100)
+                        .numAllocated(1000).minValue(100).maxValue(900).build();
+
+        nextConnection();
+        createSequenceWithMinMaxAndCycle(sequenceProps);
+        nextConnection();
+
+        // Full first batch from the sequence
+        assertExpectedNextValueForSequence(100);
+        assertExpectedCurrentValueForSequence(100);
+        assertExpectedNextValueForSequence(105);
+
+        // Attempt to bulk Allocate more slots than available
+        try {
+             conn.createStatement().executeQuery(
+                        "SELECT NEXT " + sequenceProps.numAllocated
+                                + " VALUES FOR bulkalloc.alpha FROM SYSTEM.\"SEQUENCE\" LIMIT 1");
+            fail("Invoking SELECT NEXT VALUES should have failed as operation is not supported for sequences with Cycles.");
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.NUM_SEQ_TO_ALLOCATE_NOT_SUPPORTED.getErrorCode(),
+                e.getErrorCode());
+            assertTrue(e.getNextException() == null);
+        }
+
+        // Assert sequence didn't advance
+        assertExpectedCurrentValueForSequence(105);
+        assertExpectedNextValueForSequence(110);
+        assertExpectedNextValueForSequence(115);
+    }
+
+    @Test
+    /**
+     * Validates that if we have multiple NEXT <n> VALUES FOR <seq> expression and the 
+     * CURRENT VALUE FOR expression work correctly when used in the same statement.
+     */
+    public void testCurrentValueForAndNextValuesForExpressionsForSameSequence() throws Exception {
+        final SequenceProperties props =
+                new SequenceProperties.Builder().incrementBy(1).startsWith(1).cacheSize(100)
+                        .numAllocated(1000).build();
+
+        nextConnection();
+        createSequenceWithNoMinMax(props);
+        nextConnection();
+
+        assertExpectedNextValueForSequence(1);
+        assertExpectedCurrentValueForSequence(1);
+        assertExpectedNextValueForSequence(2);
+
+        // Bulk Allocate Sequence Slots
+        int currentValueAfterAllocation = 1100;
+        int nextValueAfterAllocation = currentValueAfterAllocation + props.incrementBy;
+        int startValueAfterAllocation = 101;
+        ResultSet rs =
+                conn.createStatement().executeQuery(
+                    "SELECT CURRENT VALUE FOR bulkalloc.alpha, NEXT " + props.numAllocated + " VALUES FOR bulkalloc.alpha FROM SYSTEM.\"SEQUENCE\"");
+        assertTrue(rs.next());
+        assertBulkAllocationSucceeded(props, currentValueAfterAllocation, startValueAfterAllocation);
+        int currentValueFor = rs.getInt(1);
+        int nextValuesFor = rs.getInt(2);
+        assertEquals("Expected the next value to be first value reserved", startValueAfterAllocation, nextValuesFor);
+        assertEquals("Expected current value to be the same as next value", startValueAfterAllocation, currentValueFor);
+        
+        // Assert standard Sequence Operations return expected values
+        assertExpectedCurrentValueForSequence(currentValueAfterAllocation);
+        assertExpectedNextValueForSequence(nextValueAfterAllocation);
+    }
+    
+    @Test
+    /**
+     * Validates that if we have multiple NEXT <n> VALUES FOR <seq> expressions for the *same* sequence
+     * in a statement we only process the one which has the highest value of <n> and return the start
+     * value for that for all expressions.
+     */
+    public void testMultipleNextValuesForExpressionsForSameSequence() throws Exception {
+        final SequenceProperties props =
+                new SequenceProperties.Builder().incrementBy(1).startsWith(1).cacheSize(100)
+                        .numAllocated(1000).build();
+
+        nextConnection();
+        createSequenceWithNoMinMax(props);
+        nextConnection();
+
+        assertExpectedNextValueForSequence(1);
+        assertExpectedCurrentValueForSequence(1);
+        assertExpectedNextValueForSequence(2);
+
+        // Bulk Allocate Sequence Slots - One for 5 and one for 1000, 1000 should have precedence
+        int currentValueAfterAllocation = 1100;
+        int nextValueAfterAllocation = currentValueAfterAllocation + props.incrementBy;
+        int startValueAfterAllocation = 101;
+        ResultSet rs =
+                conn.createStatement().executeQuery(
+                    "SELECT NEXT 5 VALUES FOR bulkalloc.alpha, NEXT " + props.numAllocated + " VALUES FOR bulkalloc.alpha FROM SYSTEM.\"SEQUENCE\"");
+        assertTrue(rs.next());
+        int firstValue = rs.getInt(1);
+        int secondValue = rs.getInt(2);
+        assertEquals("Expected both expressions to return the same value", firstValue, secondValue);
+        assertEquals("Expected the value returned to be the highest allocation", startValueAfterAllocation, firstValue);
+        assertBulkAllocationSucceeded(props, currentValueAfterAllocation, startValueAfterAllocation);
+        
+        // Assert standard Sequence Operations return expected values
+        assertExpectedCurrentValueForSequence(currentValueAfterAllocation);
+        assertExpectedNextValueForSequence(nextValueAfterAllocation);
+    }
+    
+    @Test
+    /**
+     * Validates that if we have NEXT VALUE FOR <seq> and NEXT <n> VALUES FOR <seq> expressions for the *same* sequence
+     * in a statement we only process way and honor the value of the highest value of <n>, where for 
+     * NEXT VALUE FOR <seq> is assumed to be 1.
+     */
+    public void testMultipleDifferentExpressionsForSameSequence() throws Exception {
+        final SequenceProperties props =
+                new SequenceProperties.Builder().incrementBy(1).startsWith(1).cacheSize(100)
+                        .numAllocated(1000).build();
+
+        nextConnection();
+        createSequenceWithNoMinMax(props);
+        nextConnection();
+        
+        // Pull First Batch from Sequence
+        assertExpectedNextValueForSequence(1);
+
+        // Bulk Allocate Sequence Slots and Get Next Value in Same Statement
+        int currentValueAfterAllocation = 1100;
+        int nextValueAfterAllocation = currentValueAfterAllocation + props.incrementBy;
+        int startValueAfterAllocation = 101;
+        ResultSet rs =
+                conn.createStatement().executeQuery(
+                    "SELECT NEXT VALUE FOR bulkalloc.alpha, "
+                    + "NEXT " + props.numAllocated + " VALUES FOR bulkalloc.alpha, "
+                    + "CURRENT VALUE FOR bulkalloc.alpha, "
+                    + "NEXT 999 VALUES FOR bulkalloc.alpha "
+                    + "FROM SYSTEM.\"SEQUENCE\"");
+        assertTrue(rs.next());
+        assertBulkAllocationSucceeded(props, currentValueAfterAllocation, startValueAfterAllocation);
+        
+        // Assert all values returned are the same
+        // Expect them to be the highest value from NEXT VALUE or NEXT <n> VALUES FOR
+        int previousVal = 0;
+        for (int i = 1; i <= 4; i++) {
+            int currentVal = rs.getInt(i);
+            if (i != 1) {
+                assertEquals(
+                    "Expected all NEXT VALUE FOR and NEXT <n> VALUES FOR expressions to return the same value",
+                    previousVal, currentVal);                
+            }
+            previousVal = currentVal;
+        }
+        
+        // Assert standard Sequence Operations return expected values
+        assertExpectedCurrentValueForSequence(currentValueAfterAllocation);
+        assertExpectedNextValueForSequence(nextValueAfterAllocation);
+    }
+    
+    
+    @Test
+    /**
+     * Validates that using NEXT <n> VALUES FOR on different sequences in the 
+     * same statement with *different* values of <n> works as expected. This
+     * test validates that we keep our numAllocated array and sequence keys in 
+     * sync during the sequence management process.
+     */
+    public void testMultipleNextValuesForExpressionsForDifferentSequences() throws Exception {
+
+        nextConnection();
+        conn.createStatement().execute("CREATE SEQUENCE bulkalloc.alpha START WITH 30 INCREMENT BY 3 CACHE 100");
+        conn.createStatement().execute("CREATE SEQUENCE bulkalloc.beta START WITH 100 INCREMENT BY 5 CACHE 50");
+        nextConnection();
+
+        // Bulk Allocate Sequence Slots for Two Sequences
+        ResultSet rs =
+                conn.createStatement().executeQuery(
+                    "SELECT NEXT 100 VALUES FOR bulkalloc.alpha, NEXT 1000 VALUES FOR bulkalloc.beta FROM SYSTEM.\"SEQUENCE\"");
+        assertTrue(rs.next());
+        assertEquals(30, rs.getInt(1));
+        assertEquals(100, rs.getInt(2));
+        
+        // Assert standard Sequence Operations return expected values
+        for (int i = 330; i < 330 + (2 * 100); i += 3) {
+            assertExpectedCurrentValueForSequence(i - 3, "bulkalloc.alpha");
+            assertExpectedNextValueForSequence(i, "bulkalloc.alpha");            
+        }
+        
+        for (int i = 5100; i < 5100 + (2 * 1000); i += 5) {
+            assertExpectedCurrentValueForSequence(i - 5, "bulkalloc.beta");
+            assertExpectedNextValueForSequence(i, "bulkalloc.beta");            
+        }
+    }
+    
+    @Test
+    /**
+     * Validates that calling NEXT <n> VALUES FOR with EXPLAIN PLAN doesn't use 
+     * allocate any slots. 
+     */
+    public void testExplainPlanValidatesSequences() throws Exception {
+        
+        final SequenceProperties props =
+                new SequenceProperties.Builder().incrementBy(3).startsWith(30).cacheSize(100)
+                        .numAllocated(1000).build();
+
+        nextConnection();
+        createSequenceWithNoMinMax(props);
+        
+        nextGenericConnection();
+        conn.createStatement().execute("CREATE TABLE bulkalloc.simpletbl (k BIGINT NOT NULL PRIMARY KEY)");
+        nextConnection();
+
+        // Bulk Allocate Sequence Slots
+        int startValueAfterAllocation = 30;
+        reserveSlotsInBulkAndAssertValue(startValueAfterAllocation, props.numAllocated);
+        
+        // Execute EXPLAIN PLAN multiple times, which should not change Sequence values
+        for (int i = 0; i < 3; i++) {
+            conn.createStatement().executeQuery("EXPLAIN SELECT NEXT 1000 VALUES FOR bulkalloc.alpha FROM bulkalloc.simpletbl");
+        }
+        
+        // Validate the current value was not advanced and was the starting value
+        assertExpectedStateInSystemSequence(props, 3030);
+        
+        // Assert standard Sequence Operations return expected values
+        int startValue = 3030;
+        for (int i = startValue; i < startValue + (2 * props.cacheSize); i += props.incrementBy) {
+            assertExpectedCurrentValueForSequence(i - props.incrementBy);
+            assertExpectedNextValueForSequence(i);            
+        }
+    }
+    
+    @Test
+    public void testExplainPlanForNextValuesFor() throws Exception {
+        
+        final SequenceProperties props =
+                new SequenceProperties.Builder().incrementBy(3).startsWith(30).cacheSize(100)
+                        .numAllocated(1000).build();
+
+        nextConnection();
+        createSequenceWithNoMinMax(props);
+        nextGenericConnection();
+        conn.createStatement().execute("CREATE TABLE bulkalloc.simpletbl (k BIGINT NOT NULL PRIMARY KEY)");
+        nextConnection();
+        
+        // Execute EXPLAIN PLAN which should not change Sequence values
+        ResultSet rs = conn.createStatement().executeQuery("EXPLAIN SELECT NEXT 1000 VALUES FOR bulkalloc.alpha FROM bulkalloc.simpletbl");
+
+        // Assert output for Explain Plain result is as expected
+        assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER BULKALLOC.SIMPLETBL\n" + 
+                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                "CLIENT RESERVE VALUES FROM 1 SEQUENCE", QueryUtil.getExplainPlan(rs));
+    }
+    
+    
+    /**
+     * Performs a multithreaded test whereby we interleave reads from the result set of
+     * NEXT VALUE FOR and NEXT <n> VALUES FOR to make sure we get expected values with the
+     * following order of execution:
+     * 
+     * 1) Execute expression NEXT <n> VALUES FOR <seq>
+     * 2) Execute expression NEXT VALUE FOR <seq>
+     * 3) Read back value from expression NEXT VALUE FOR <seq> via rs.next()
+     * 4) Read back value from expression NEXT <n> VALUES FOR <seq> via rs.next()
+     */
+    public void testNextValuesForMixedWithNextValueForMultiThreaded() throws Exception {
+        final SequenceProperties props =
+                new SequenceProperties.Builder().incrementBy(1).startsWith(1).cacheSize(100)
+                        .numAllocated(1000).build();
+
+        nextConnection();
+        createSequenceWithNoMinMax(props);
+        nextConnection();
+
+        assertExpectedNextValueForSequence(1);
+        assertExpectedCurrentValueForSequence(1);
+        assertExpectedNextValueForSequence(2);
+
+        // Bulk Allocate Sequence Slots
+        final long startValueAfterAllocation1 = 101;
+        final long startValueAfterAllocation2 = 1101;
+        final long numSlotToAllocate = props.numAllocated;
+        
+        // Setup and run tasks in independent Threads
+        ExecutorService executorService = Executors.newCachedThreadPool();
+        try {
+            final CountDownLatch latch1 = new CountDownLatch(1);
+            final CountDownLatch latch2 = new CountDownLatch(1);
+            
+            Callable<Long> task1 = new Callable<Long>() {
+    
+                @Override
+                public Long call() throws Exception {
+                    ResultSet rs =
+                            conn.createStatement().executeQuery(
+                                "SELECT NEXT " + numSlotToAllocate + " VALUES FOR bulkalloc.alpha FROM SYSTEM.\"SEQUENCE\"");
+                    latch1.countDown(); // Allows NEXT VALUE FOR thread to proceed
+                    latch2.await(); // Waits until NEXT VALUE FOR thread reads and increments currentValue
+                    rs.next();
+                    return rs.getLong(1);    
+                }
+                
+            };
+    
+            Callable<Long> task2 = new Callable<Long>() {
+    
+                @Override
+                public Long call() throws Exception {
+                    latch1.await(); // Wait for execution of NEXT <n> VALUES FOR expression
+                    ResultSet rs =
+                            conn.createStatement().executeQuery(
+                                "SELECT NEXT VALUE FOR bulkalloc.alpha FROM SYSTEM.\"SEQUENCE\"");
+                    rs.next();
+                    long retVal = rs.getLong(1);
+                    latch2.countDown(); // Allow NEXT <n> VALUES for thread to completed
+                    return retVal;
+                }
+                
+            };
+            
+            @SuppressWarnings("unchecked")
+            List<Future<Long>> futures = executorService.invokeAll(Lists.newArrayList(task1, task2), 20, TimeUnit.SECONDS);
+            assertEquals(startValueAfterAllocation1, futures.get(0).get(10, TimeUnit.SECONDS).longValue());
+            assertEquals(startValueAfterAllocation2, futures.get(1).get(10, TimeUnit.SECONDS).longValue());
+        } finally {
+            executorService.shutdown();
+        }
+    }
+
+    @Test
+    public void testMultipleNextValuesWithDiffAllocsForMultiThreaded() throws Exception {
+        final SequenceProperties props =
+                new SequenceProperties.Builder().incrementBy(1).startsWith(1).cacheSize(100)
+                        .numAllocated(1000).build();
+
+        nextConnection();
+        createSequenceWithNoMinMax(props);
+        nextConnection();
+
+        assertExpectedNextValueForSequence(1);
+        assertExpectedCurrentValueForSequence(1);
+        assertExpectedNextValueForSequence(2);
+
+        // Bulk Allocate Sequence Slots
+        final long startValueAfterAllocation1 = 101;
+        final long startValueAfterAllocation2 = 1101;
+        final long numSlotToAllocate1 = 1000;
+        final long numSlotToAllocate2 = 100;
+        
+        // Setup and run tasks in independent Threads
+        ExecutorService executorService = Executors.newCachedThreadPool();
+        try {
+            final CountDownLatch latch1 = new CountDownLatch(1);
+            final CountDownLatch latch2 = new CountDownLatch(1);
+            
+            Callable<Long> task1 = new Callable<Long>() {
+    
+                @Override
+                public Long call() throws Exception {
+                    ResultSet rs =
+                            conn.createStatement().executeQuery(
+                                "SELECT NEXT " + numSlotToAllocate1 + " VALUES FOR bulkalloc.alpha FROM SYSTEM.\"SEQUENCE\"");
+                    rs.next();
+                    latch1.countDown(); // Allows other thread to proceed
+                    latch2.await(); 
+                    return rs.getLong(1);    
+                }
+                
+            };
+    
+            Callable<Long> task2 = new Callable<Long>() {
+    
+                @Override
+                public Long call() throws Exception {
+                    latch1.await(); // Wait for other thread to execut of NEXT <n> VALUES FOR expression
+                    ResultSet rs =
+                            conn.createStatement().executeQuery(
+                                "SELECT NEXT " + numSlotToAllocate2 + " VALUES FOR bulkalloc.alpha FROM SYSTEM.\"SEQUENCE\"");
+                    rs.next();
+                    long retVal = rs.getLong(1);
+                    latch2.countDown(); // Allow thread to completed
+                    return retVal;
+                }
+                
+            };
+            
+            @SuppressWarnings("unchecked")
+            List<Future<Long>> futures = executorService.invokeAll(Lists.newArrayList(task1, task2), 5, TimeUnit.SECONDS);
+            
+            // Retrieve value from Thread running NEXT <n> VALUES FOR
+            Long retValue1 = futures.get(0).get(5, TimeUnit.SECONDS);
+            assertEquals(startValueAfterAllocation1, retValue1.longValue());
+    
+            // Retrieve value from Thread running NEXT VALUE FOR
+            Long retValue2 = futures.get(1).get(5, TimeUnit.SECONDS);
+            assertEquals(startValueAfterAllocation2, retValue2.longValue());
+
+        } finally {
+            executorService.shutdown();
+        }
+    }
+    
+    @Test
+    public void testMultipleNextValuesWithSameAllocsForMultiThreaded() throws Exception {
+        final SequenceProperties props =
+                new SequenceProperties.Builder().incrementBy(1).startsWith(1).cacheSize(100)
+                        .numAllocated(1000).build();
+
+        nextConnection();
+        createSequenceWithNoMinMax(props);
+        nextConnection();
+
+        // Bulk Allocate Sequence Slots
+        final long startValueAfterAllocation1 = 1;
+        final long startValueAfterAllocation2 = 1001;
+        final long numSlotToAllocate1 = 1000;
+        final long numSlotToAllocate2 = 1000;
+        
+        // Setup and run tasks in independent Threads
+        ExecutorService executorService = Executors.newCachedThreadPool();
+        try {
+            final CountDownLatch latch1 = new CountDownLatch(1);
+            final CountDownLatch latch2 = new CountDownLatch(1);
+            
+            Callable<Long> task1 = new Callable<Long>() {
+    
+                @Override
+                public Long call() throws Exception {
+                    ResultSet rs =
+                            conn.createStatement().executeQuery(
+                                "SELECT NEXT " + numSlotToAllocate1 + " VALUES FOR bulkalloc.alpha FROM SYSTEM.\"SEQUENCE\"");
+                    latch1.countDown(); // Allows other thread to proceed
+                    latch2.await(); 
+                    rs.next();
+                    return rs.getLong(1);    
+                }
+                
+            };
+    
+            Callable<Long> task2 = new Callable<Long>() {
+    
+                @Override
+                public Long call() throws Exception {
+                    latch1.await(); // Wait for other thread to execut of NEXT <n> VALUES FOR expression
+                    ResultSet rs =
+                            conn.createStatement().executeQuery(
+                                "SELECT NEXT " + numSlotToAllocate2 + " VALUES FOR bulkalloc.alpha FROM SYSTEM.\"SEQUENCE\"");
+                    rs.next();
+                    long retVal = rs.getLong(1);
+                    latch2.countDown(); // Allow thread to completed
+                    return retVal;
+                }
+                
+            };
+            
+            // Because of the way the threads are interleaved the ranges used by each thread will the reserve
+            // of the order to statement execution
+            @SuppressWarnings("unchecked")
+            List<Future<Long>> futures = executorService.invokeAll(Lists.newArrayList(task1, task2), 5, TimeUnit.SECONDS);
+            assertEquals(startValueAfterAllocation2, futures.get(0).get(5, TimeUnit.SECONDS).longValue());
+            assertEquals(startValueAfterAllocation1, futures.get(1).get(5, TimeUnit.SECONDS).longValue());
+
+        } finally {
+            executorService.shutdown();
+        }
+    }
+
+    // -----------------------------------------------------------------
+    // Private Helper Methods
+    // -----------------------------------------------------------------
+    private void assertBulkAllocationSucceeded(SequenceProperties props,
+            int currentValueAfterAllocation, int startValueAfterAllocation) throws SQLException {
+        int nextValueAfterAllocation = currentValueAfterAllocation + props.incrementBy;
+        assertExpectedStateInSystemSequence(props, nextValueAfterAllocation);
+        assertExpectedNumberOfValuesAllocated(startValueAfterAllocation, currentValueAfterAllocation, props.incrementBy, props.numAllocated);
+    }
+    
+    
+    private void createSequenceWithNoMinMax(final SequenceProperties props) throws SQLException {
+        conn.createStatement().execute(
+            String.format(CREATE_SEQUENCE_NO_MIN_MAX_TEMPLATE, props.startsWith,
+                props.incrementBy, props.cacheSize));
+    }
+
+    private void createSequenceWithMinMax(final SequenceProperties props) throws SQLException {
+        conn.createStatement().execute(
+            String.format(CREATE_SEQUENCE_WITH_MIN_MAX_TEMPLATE, props.startsWith,
+                props.incrementBy, props.minValue, props.maxValue, props.cacheSize));
+    }
+
+    private void createSequenceWithMinMaxAndCycle(final SequenceProperties props) throws SQLException {
+        conn.createStatement().execute(
+            String.format(CREATE_SEQUENCE_WITH_MIN_MAX_AND_CYCLE_TEMPLATE, props.startsWith,
+                props.incrementBy, props.minValue, props.maxValue, props.cacheSize));
+    }
+    
+    private void reserveSlotsInBulkAndAssertValue(long expectedValue, long numSlotToAllocate)
+            throws SQLException {
+        ResultSet rs =
+                conn.createStatement().executeQuery(
+                    "SELECT NEXT " + numSlotToAllocate + " VALUES FOR bulkalloc.alpha FROM SYSTEM.\"SEQUENCE\"");
+        assertTrue(rs.next());
+        assertEquals(expectedValue, rs.getInt(1));
+    }
+
+    private void reserveSlotsInBulkUsingBindsAndAssertValue(int expectedValue, long numSlotToAllocate)
+            throws SQLException {
+        PreparedStatement ps = conn.prepareStatement("SELECT NEXT ? VALUES FOR bulkalloc.alpha FROM SYSTEM.\"SEQUENCE\"");
+        ps.setLong(1, numSlotToAllocate);
+        ResultSet rs = ps.executeQuery();
+        assertTrue(rs.next());
+        int retValue = rs.getInt(1);
+        assertEquals(expectedValue, retValue);
+    }
+
+    private void assertExpectedCurrentValueForSequence(int expectedValue) throws SQLException {
+        assertExpectedCurrentValueForSequence(expectedValue, "bulkalloc.alpha");
+    }
+    
+    private void assertExpectedCurrentValueForSequence(int expectedValue, String sequenceName) throws SQLException {
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery(String.format(SELECT_CURRENT_VALUE_SQL, sequenceName));
+        assertTrue(rs.next());
+        assertEquals(expectedValue, rs.getInt(1));
+    }
+
+    private void assertExpectedNextValueForSequence(int expectedValue) throws SQLException {
+        assertExpectedNextValueForSequence(expectedValue, "bulkalloc.alpha");
+    }
+    
+    private void assertExpectedNextValueForSequence(int expectedValue, String sequenceName) throws SQLException {
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery(String.format(SELECT_NEXT_VALUE_SQL, sequenceName));
+        assertTrue(rs.next());
+        assertEquals(expectedValue, rs.getInt(1));
+    }
+
+    
+    /**
+     * Returns a non-tenant specific connection.
+     */
+    private void nextGenericConnection() throws Exception {
+        if (conn != null) conn.close();
+        long ts = nextTimestamp();
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts));
+        conn = DriverManager.getConnection(getUrl(), props);    
+    }
+    
+    private void nextConnection() throws Exception {
+        if (conn != null) conn.close();
+        long ts = nextTimestamp();
+        if (tenantId != null) {
+            // Create tenant specific connection
+            Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+            props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(nextTimestamp()));
+            this.conn =  DriverManager.getConnection(getUrl() + ';' + TENANT_ID_ATTRIB + '=' + "tenant1", props);
+
+        } else {
+            Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+            props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts));
+            conn = DriverManager.getConnection(getUrl(), props);
+        }
+    }
+
+    private void assertExpectedStateInSystemSequence(SequenceProperties props, long currentValue)
+            throws SQLException {
+        // Validate state in System.Sequence
+        ResultSet rs =
+                conn.createStatement()
+                        .executeQuery(
+                            "SELECT start_with, current_value, increment_by, cache_size, min_value, max_value, cycle_flag, sequence_schema, sequence_name FROM SYSTEM.\"SEQUENCE\"");
+        assertTrue(rs.next());
+        assertEquals(props.startsWith, rs.getLong("start_with"));
+        assertEquals(props.incrementBy, rs.getLong("increment_by"));
+        assertEquals(props.cacheSize, rs.getLong("cache_size"));
+        assertEquals(false, rs.getBoolean("cycle_flag"));
+        assertEquals("BULKALLOC", rs.getString("sequence_schema"));
+        assertEquals("ALPHA", rs.getString("sequence_name"));
+        assertEquals(currentValue, rs.getLong("current_value"));
+        assertEquals(props.minValue, rs.getLong("min_value"));
+        assertEquals(props.maxValue, rs.getLong("max_value"));
+        assertFalse(rs.next());
+    }
+
+    private void assertExpectedNumberOfValuesAllocated(long firstValue, long lastValue,
+            int incrementBy, long numAllocated) {
+        int cnt = 0;
+        for (long i = firstValue; (incrementBy > 0 ? i <= lastValue : i >= lastValue); i += incrementBy) {
+            cnt++;
+        }
+        assertEquals("Incorrect number of values allocated: " + cnt, numAllocated, cnt);
+    }
+
+    private static class SequenceProperties {
+
+       private final long numAllocated;
+       private final int incrementBy;
+       private final int startsWith;
+       private final int cacheSize;
+       private final long minValue;
+       private final long maxValue;
+
+        public SequenceProperties(Builder builder) {
+            this.numAllocated = builder.numAllocated;
+            this.incrementBy = builder.incrementBy;
+            this.startsWith = builder.startsWith;
+            this.cacheSize = builder.cacheSize;
+            this.minValue = builder.minValue;
+            this.maxValue = builder.maxValue;
+        }
+
+        private static class Builder {
+
+            long maxValue = Long.MAX_VALUE;
+            long minValue = Long.MIN_VALUE;
+            long numAllocated = 100;
+            int incrementBy = 1;
+            int startsWith = 1;
+            int cacheSize = 100;
+
+            public Builder numAllocated(long numAllocated) {
+                this.numAllocated = numAllocated;
+                return this;
+            }
+
+            public Builder startsWith(int startsWith) {
+                this.startsWith = startsWith;
+                return this;
+            }
+
+            public Builder cacheSize(int cacheSize) {
+                this.cacheSize = cacheSize;
+                return this;
+            }
+
+            public Builder incrementBy(int incrementBy) {
+                this.incrementBy = incrementBy;
+                return this;
+            }
+
+            public Builder minValue(long minValue) {
+                this.minValue = minValue;
+                return this;
+            }
+            
+            public Builder maxValue(long maxValue) {
+                this.maxValue = maxValue;
+                return this;
+            }
+            
+            public SequenceProperties build() {
+                return new SequenceProperties(this);
+            }
+
+        }
+
+    }
+    
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3b1bfa0d/phoenix-core/src/main/antlr3/PhoenixSQL.g
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/antlr3/PhoenixSQL.g b/phoenix-core/src/main/antlr3/PhoenixSQL.g
index ca5e778..10fda68 100644
--- a/phoenix-core/src/main/antlr3/PhoenixSQL.g
+++ b/phoenix-core/src/main/antlr3/PhoenixSQL.g
@@ -903,7 +903,10 @@ term returns [ParseNode ret]
         }
     |   (n=NEXT | CURRENT) VALUE FOR s=from_table_name 
         { contextStack.peek().hasSequences(true);
-          $ret = n==null ? factory.currentValueFor(s) : factory.nextValueFor(s); }    
+          $ret = n==null ? factory.currentValueFor(s) : factory.nextValueFor(s, null); }    
+    |   (n=NEXT) lorb=literal_or_bind VALUES FOR s=from_table_name 
+        { contextStack.peek().hasSequences(true);
+          $ret = factory.nextValueFor(s, lorb); }    
     ;
 
 one_or_more_expressions returns [List<ParseNode> ret]

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3b1bfa0d/phoenix-core/src/main/java/org/apache/phoenix/compile/SequenceManager.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/SequenceManager.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/SequenceManager.java
index ede6d9c..5ec8cd2 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/SequenceManager.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/SequenceManager.java
@@ -24,16 +24,23 @@ import java.util.List;
 import java.util.Map;
 
 import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.phoenix.exception.SQLExceptionCode;
+import org.apache.phoenix.expression.Expression;
 import org.apache.phoenix.jdbc.PhoenixStatement;
+import org.apache.phoenix.parse.ParseNode;
 import org.apache.phoenix.parse.SequenceValueParseNode;
 import org.apache.phoenix.parse.SequenceValueParseNode.Op;
 import org.apache.phoenix.parse.TableName;
 import org.apache.phoenix.query.ConnectionQueryServices;
 import org.apache.phoenix.schema.PName;
 import org.apache.phoenix.schema.Sequence;
+import org.apache.phoenix.schema.SequenceAllocation;
 import org.apache.phoenix.schema.SequenceKey;
 import org.apache.phoenix.schema.tuple.DelegateTuple;
 import org.apache.phoenix.schema.tuple.Tuple;
+import org.apache.phoenix.schema.types.PLong;
+import org.apache.phoenix.util.SequenceUtil;
 
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
@@ -41,7 +48,7 @@ import com.google.common.collect.Maps;
 public class SequenceManager {
     private final PhoenixStatement statement;
     private int[] sequencePosition;
-    private List<SequenceKey> nextSequences;
+    private List<SequenceAllocation> nextSequences;
     private List<SequenceKey> currentSequences;
     private final Map<SequenceKey,SequenceValueExpression> sequenceMap = Maps.newHashMap();
     private final BitSet isNextSequence = new BitSet();
@@ -113,20 +120,30 @@ public class SequenceManager {
         }
     }
 
-    public SequenceValueExpression newSequenceReference(SequenceValueParseNode node) {
+    public SequenceValueExpression newSequenceReference(SequenceValueParseNode node) throws SQLException {
         PName tenantName = statement.getConnection().getTenantId();
         String tenantId = tenantName == null ? null : tenantName.getString();
         TableName tableName = node.getTableName();
         int nSaltBuckets = statement.getConnection().getQueryServices().getSequenceSaltBuckets();
+        ParseNode numToAllocateNode = node.getNumToAllocateNode();
+        
+        long numToAllocate = determineNumToAllocate(tableName, numToAllocateNode);
         SequenceKey key = new SequenceKey(tenantId, tableName.getSchemaName(), tableName.getTableName(), nSaltBuckets);
         SequenceValueExpression expression = sequenceMap.get(key);
         if (expression == null) {
             int index = sequenceMap.size();
-            expression = new SequenceValueExpression(key, node.getOp(), index);
+            expression = new SequenceValueExpression(key, node.getOp(), index, numToAllocate);
             sequenceMap.put(key, expression);
-        } else if (expression.op != node.getOp()){
-            expression = new SequenceValueExpression(key, node.getOp(), expression.getIndex());
-        }
+        } else if (expression.op != node.getOp() || expression.getNumToAllocate() < numToAllocate) {
+            // Keep the maximum allocation size we see in a statement
+            SequenceValueExpression oldExpression = expression;
+            expression = new SequenceValueExpression(key, node.getOp(), expression.getIndex(), Math.max(expression.getNumToAllocate(), numToAllocate));
+            if (oldExpression.getNumToAllocate() < numToAllocate) {
+                // If we found a NEXT VALUE expression with a higher number to allocate
+                // We override the original expression
+                sequenceMap.put(key, expression);
+            }
+        } 
         // If we see a NEXT and a CURRENT, treat the CURRENT just like a NEXT
         if (node.getOp() == Op.NEXT_VALUE) {
             isNextSequence.set(expression.getIndex());
@@ -134,6 +151,38 @@ public class SequenceManager {
            
         return expression;
     }
+
+    /**
+     * If caller specified used NEXT <n> VALUES FOR <seq> expression then we have set the numToAllocate.
+     * If numToAllocate is > 1 we treat this as a bulk reservation of a block of sequence slots.
+     * 
+     * @throws a SQLException if we can't compile the expression
+     */
+    private long determineNumToAllocate(TableName sequenceName, ParseNode numToAllocateNode)
+            throws SQLException {
+
+        if (numToAllocateNode != null) {
+            final StatementContext context = new StatementContext(statement);
+            ExpressionCompiler expressionCompiler = new ExpressionCompiler(context);
+            Expression expression = numToAllocateNode.accept(expressionCompiler);
+            ImmutableBytesWritable ptr = context.getTempPtr();
+            expression.evaluate(null, ptr);
+            if (ptr.getLength() == 0 || !expression.getDataType().isCoercibleTo(PLong.INSTANCE)) {
+                throw SequenceUtil.getException(sequenceName.getSchemaName(), sequenceName.getTableName(), SQLExceptionCode.NUM_SEQ_TO_ALLOCATE_MUST_BE_CONSTANT);
+            }
+            
+            // Parse <n> and make sure it is greater than 0. We don't support allocating 0 or negative values!
+            long numToAllocate = (long) PLong.INSTANCE.toObject(ptr, expression.getDataType());
+            if (numToAllocate < 1) {
+                throw SequenceUtil.getException(sequenceName.getSchemaName(), sequenceName.getTableName(), SQLExceptionCode.NUM_SEQ_TO_ALLOCATE_MUST_BE_CONSTANT);
+            }
+            return numToAllocate;
+            
+        } else {
+            // Standard Sequence Allocation Behavior
+            return SequenceUtil.DEFAULT_NUM_SLOTS_TO_ALLOCATE;
+        }
+    }
     
     public void validateSequences(Sequence.ValueOp action) throws SQLException {
         if (sequenceMap.isEmpty()) {
@@ -146,14 +195,17 @@ public class SequenceManager {
         currentSequences = Lists.newArrayListWithExpectedSize(maxSize);
         for (Map.Entry<SequenceKey, SequenceValueExpression> entry : sequenceMap.entrySet()) {
             if (isNextSequence.get(entry.getValue().getIndex())) {
-                nextSequences.add(entry.getKey());
+                nextSequences.add(new SequenceAllocation(entry.getKey(), entry.getValue().getNumToAllocate()));
             } else {
                 currentSequences.add(entry.getKey());
             }
         }
         long[] srcSequenceValues = new long[nextSequences.size()];
         SQLException[] sqlExceptions = new SQLException[nextSequences.size()];
+        
+        // Sort the next sequences to prevent deadlocks
         Collections.sort(nextSequences);
+
         // Create reverse indexes
         for (int i = 0; i < nextSequences.size(); i++) {
             sequencePosition[i] = sequenceMap.get(nextSequences.get(i)).getIndex();
@@ -168,4 +220,6 @@ public class SequenceManager {
         services.validateSequences(nextSequences, timestamp, srcSequenceValues, sqlExceptions, action);
         setSequenceValues(srcSequenceValues, dstSequenceValues, sqlExceptions);
     }
-}
+    
+}    
+ 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3b1bfa0d/phoenix-core/src/main/java/org/apache/phoenix/compile/SequenceValueExpression.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/SequenceValueExpression.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/SequenceValueExpression.java
index cdaae68..71e2d02 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/SequenceValueExpression.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/SequenceValueExpression.java
@@ -32,13 +32,23 @@ public class SequenceValueExpression extends BaseTerminalExpression {
     private final SequenceKey key;
     final Op op;
     private final int index;
+    private final long numToAllocate;
 
-    public SequenceValueExpression(SequenceKey key, Op op, int index) {
+    public SequenceValueExpression(SequenceKey key, Op op, int index, long numToAllocate) {
         this.key = key;
         this.op = op;
         this.index = index;
+        this.numToAllocate = numToAllocate;
     }
 
+    public long getNumToAllocate() {
+        return numToAllocate;
+    }
+    
+    public SequenceKey getKey() {
+        return key;
+    }
+    
     public int getIndex() {
         return index;
     }
@@ -73,7 +83,7 @@ public class SequenceValueExpression extends BaseTerminalExpression {
 
     @Override
     public String toString() {
-        return op.getName() + " VALUE FOR " + SchemaUtil.getTableName(key.getSchemaName(),key.getSequenceName());
+        return op.getName() + (numToAllocate == 1 ? " VALUE " : (" " + numToAllocate + " VALUES " )) + "FOR " + SchemaUtil.getTableName(key.getSchemaName(),key.getSequenceName());
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3b1bfa0d/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/SequenceRegionObserver.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/SequenceRegionObserver.java b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/SequenceRegionObserver.java
index 9b5f040..bb6c4b5 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/SequenceRegionObserver.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/SequenceRegionObserver.java
@@ -41,17 +41,17 @@ import org.apache.hadoop.hbase.io.TimeRange;
 import org.apache.hadoop.hbase.regionserver.Region;
 import org.apache.hadoop.hbase.regionserver.Region.RowLock;
 import org.apache.hadoop.hbase.util.Bytes;
-import org.apache.phoenix.util.EnvironmentEdgeManager;
 import org.apache.phoenix.exception.SQLExceptionCode;
 import org.apache.phoenix.jdbc.PhoenixDatabaseMetaData;
 import org.apache.phoenix.query.QueryConstants;
+import org.apache.phoenix.schema.Sequence;
+import org.apache.phoenix.schema.SortOrder;
 import org.apache.phoenix.schema.types.PBoolean;
-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.PLong;
-import org.apache.phoenix.schema.Sequence;
-import org.apache.phoenix.schema.SortOrder;
 import org.apache.phoenix.util.ByteUtil;
+import org.apache.phoenix.util.EnvironmentEdgeManager;
 import org.apache.phoenix.util.KeyValueUtil;
 import org.apache.phoenix.util.MetaDataUtil;
 import org.apache.phoenix.util.SequenceUtil;
@@ -77,6 +77,7 @@ public class SequenceRegionObserver extends BaseRegionObserver {
     public static final String OPERATION_ATTRIB = "SEQUENCE_OPERATION";
     public static final String MAX_TIMERANGE_ATTRIB = "MAX_TIMERANGE";
     public static final String CURRENT_VALUE_ATTRIB = "CURRENT_VALUE";
+    public static final String NUM_TO_ALLOCATE = "NUM_TO_ALLOCATE";
     private static final byte[] SUCCESS_VALUE = PInteger.INSTANCE.toBytes(Integer.valueOf(Sequence.SUCCESS));
     
     private static Result getErrorResult(byte[] row, long timestamp, int errorCode) {
@@ -138,10 +139,8 @@ public class SequenceRegionObserver extends BaseRegionObserver {
                 if (result.isEmpty()) {
                     return getErrorResult(row, maxTimestamp, SQLExceptionCode.SEQUENCE_UNDEFINED.getErrorCode());
                 }
-                if (validateOnly) {
-                    return result;
-                }
                 
+                 
                 KeyValue currentValueKV = Sequence.getCurrentValueKV(result);
                 KeyValue incrementByKV = Sequence.getIncrementByKV(result);
                 KeyValue cacheSizeKV = Sequence.getCacheSizeKV(result);
@@ -224,6 +223,28 @@ public class SequenceRegionObserver extends BaseRegionObserver {
 	                            cycleKV.getValueOffset(), cycleKV.getValueLength());
 	                }
 	                
+	                long numSlotsToAllocate = calculateNumSlotsToAllocate(increment);
+
+                    // We don't support Bulk Allocations on sequences that have the CYCLE flag set to true
+	                if (cycle && !SequenceUtil.isCycleAllowed(numSlotsToAllocate)) {
+                        return getErrorResult(row, maxTimestamp, SQLExceptionCode.NUM_SEQ_TO_ALLOCATE_NOT_SUPPORTED.getErrorCode());
+	                }
+	                
+	                // Bulk Allocations are expressed by NEXT <n> VALUES FOR
+	                if (SequenceUtil.isBulkAllocation(numSlotsToAllocate)) {
+	                    if (SequenceUtil.checkIfLimitReached(currentValue, minValue, maxValue, incrementBy, cacheSize, numSlotsToAllocate)) {
+	                        // If we try to allocate more slots than the limit we return an error.
+	                        // Allocating sequence values in bulk should be an all or nothing operation.
+	                        // If the operation succeeds clients are guaranteed that they have reserved 
+	                        // all the slots requested.
+	                        return getErrorResult(row, maxTimestamp, SequenceUtil.getLimitReachedErrorCode(increasingSeq).getErrorCode());
+	                    }
+	                }
+	                
+	                if (validateOnly) {
+	                    return result;
+	                }
+	                
 	                // return if we have run out of sequence values 
 					if (limitReached) {
 						if (cycle) {
@@ -231,16 +252,15 @@ public class SequenceRegionObserver extends BaseRegionObserver {
 							currentValue = increasingSeq ? minValue : maxValue;
 						}
 						else {
-							SQLExceptionCode code = increasingSeq ? SQLExceptionCode.SEQUENCE_VAL_REACHED_MAX_VALUE
-									: SQLExceptionCode.SEQUENCE_VAL_REACHED_MIN_VALUE;
-							return getErrorResult(row, maxTimestamp, code.getErrorCode());
+							return getErrorResult(row, maxTimestamp, SequenceUtil.getLimitReachedErrorCode(increasingSeq).getErrorCode());
 						}
 					}
-	                
+						                
 	                // check if the limit was reached
-					limitReached = SequenceUtil.checkIfLimitReached(currentValue, minValue, maxValue, incrementBy, cacheSize);
-	                // update currentValue
-					currentValue += incrementBy * cacheSize;
+					limitReached = SequenceUtil.checkIfLimitReached(currentValue, minValue, maxValue, incrementBy, cacheSize, numSlotsToAllocate);
+					
+                    // update currentValue
+					currentValue += incrementBy * (SequenceUtil.isBulkAllocation(numSlotsToAllocate) ? numSlotsToAllocate : cacheSize);
 					// update the currentValue of the Result row
 					KeyValue newCurrentValueKV = createKeyValue(row, PhoenixDatabaseMetaData.CURRENT_VALUE_BYTES, currentValue, timestamp);
 		            Sequence.replaceCurrentValueKV(cells, newCurrentValueKV);
@@ -264,6 +284,7 @@ public class SequenceRegionObserver extends BaseRegionObserver {
             region.closeRegionOperation();
         }
     }
+
     
 	/**
 	 * Creates a new KeyValue for a long value
@@ -417,5 +438,20 @@ public class SequenceRegionObserver extends BaseRegionObserver {
             region.closeRegionOperation();
         }
     }
+    
+    /**
+     * Determines whether a request for incrementing the sequence was a bulk allocation and if so
+     * what the number of slots to allocate is. This is triggered by the NEXT <n> VALUES FOR expression.
+     * For backwards compatibility with older clients, we default the value to 1 which preserves
+     * existing behavior when invoking NEXT VALUE FOR. 
+     */
+    private long calculateNumSlotsToAllocate(final Increment increment) {
+        long numToAllocate = 1;
+        byte[] numToAllocateBytes = increment.getAttribute(SequenceRegionObserver.NUM_TO_ALLOCATE);
+        if (numToAllocateBytes != null) {
+            numToAllocate = Bytes.toLong(numToAllocateBytes);
+        }
+        return numToAllocate;
+    }
 
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3b1bfa0d/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 cc8b02a..b9f81fb 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
@@ -276,6 +276,8 @@ public enum SQLExceptionCode {
     SEQUENCE_VAL_REACHED_MAX_VALUE(1212, "42Z12", "Reached MAXVALUE of sequence"),
     SEQUENCE_VAL_REACHED_MIN_VALUE(1213, "42Z13", "Reached MINVALUE of sequence"),
     INCREMENT_BY_MUST_NOT_BE_ZERO(1214, "42Z14", "Sequence INCREMENT BY value cannot be zero"),
+    NUM_SEQ_TO_ALLOCATE_MUST_BE_CONSTANT(1215, "42Z15", "Sequence NEXT n VALUES FOR must be a postive integer or constant." ),
+    NUM_SEQ_TO_ALLOCATE_NOT_SUPPORTED(1216, "42Z16", "Sequence NEXT n VALUES FOR is not supported for Sequences with the CYCLE flag" ),
                     
     /** Parser error. (errorcode 06, sqlState 42P) */
     PARSER_ERROR(601, "42P00", "Syntax error.", Factory.SYTAX_ERROR),

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3b1bfa0d/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
index f323ec4..9689589 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
@@ -232,7 +232,7 @@ public class PhoenixStatement implements Statement, SQLCloseable, org.apache.pho
     }
     
     protected QueryPlan optimizeQuery(CompilableStatement stmt) throws SQLException {
-        QueryPlan plan = stmt.compilePlan(this, Sequence.ValueOp.RESERVE_SEQUENCE);
+        QueryPlan plan = stmt.compilePlan(this, Sequence.ValueOp.VALIDATE_SEQUENCE);
         return connection.getQueryServices().getOptimizer().optimize(this, plan);
     }
     
@@ -245,7 +245,7 @@ public class PhoenixStatement implements Statement, SQLCloseable, org.apache.pho
                     public PhoenixResultSet call() throws SQLException {
                     final long startTime = System.currentTimeMillis();
                     try {
-                        QueryPlan plan = stmt.compilePlan(PhoenixStatement.this, Sequence.ValueOp.RESERVE_SEQUENCE);
+                        QueryPlan plan = stmt.compilePlan(PhoenixStatement.this, Sequence.ValueOp.VALIDATE_SEQUENCE);
                         plan = connection.getQueryServices().getOptimizer().optimize(
                                 PhoenixStatement.this, plan);
                          // this will create its own trace internally, so we don't wrap this
@@ -303,7 +303,7 @@ public class PhoenixStatement implements Statement, SQLCloseable, org.apache.pho
                             // since they'd update data directly from coprocessors, and should thus operate on
                             // the latest state
                             try {
-                                MutationPlan plan = stmt.compilePlan(PhoenixStatement.this, Sequence.ValueOp.RESERVE_SEQUENCE);
+                                MutationPlan plan = stmt.compilePlan(PhoenixStatement.this, Sequence.ValueOp.VALIDATE_SEQUENCE);
                                 MutationState state = plan.execute();
                                 connection.getMutationState().join(state);
                                 if (connection.getAutoCommit()) {
@@ -1203,14 +1203,14 @@ public class PhoenixStatement implements Statement, SQLCloseable, org.apache.pho
         if (stmt.getOperation().isMutation()) {
             throw new ExecuteQueryNotApplicableException(query);
         }
-        return stmt.compilePlan(this, Sequence.ValueOp.RESERVE_SEQUENCE);
+        return stmt.compilePlan(this, Sequence.ValueOp.VALIDATE_SEQUENCE);
     }
 
     public MutationPlan compileMutation(CompilableStatement stmt, String query) throws SQLException {
         if (!stmt.getOperation().isMutation()) {
             throw new ExecuteUpdateNotApplicableException(query);
         }
-        return stmt.compilePlan(this, Sequence.ValueOp.RESERVE_SEQUENCE);
+        return stmt.compilePlan(this, Sequence.ValueOp.VALIDATE_SEQUENCE);
     }
 
     public MutationPlan compileMutation(String sql) throws SQLException {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3b1bfa0d/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
index 44359a7..49b14c6 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
@@ -310,11 +310,11 @@ public class ParseNodeFactory {
     }
 
     public SequenceValueParseNode currentValueFor(TableName tableName) {
-        return new SequenceValueParseNode(tableName, SequenceValueParseNode.Op.CURRENT_VALUE);
+        return new SequenceValueParseNode(tableName, SequenceValueParseNode.Op.CURRENT_VALUE, null);
     }
 
-    public SequenceValueParseNode nextValueFor(TableName tableName) {
-        return new SequenceValueParseNode(tableName, SequenceValueParseNode.Op.NEXT_VALUE);
+    public SequenceValueParseNode nextValueFor(TableName tableName, ParseNode numToAllocateNode) {
+        return new SequenceValueParseNode(tableName, SequenceValueParseNode.Op.NEXT_VALUE, numToAllocateNode);
     }
 
     public AddColumnStatement addColumn(NamedTableNode table,  PTableType tableType, List<ColumnDef> columnDefs, boolean ifNotExists, ListMultimap<String,Pair<String,Object>> props) {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3b1bfa0d/phoenix-core/src/main/java/org/apache/phoenix/parse/SequenceValueParseNode.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/SequenceValueParseNode.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/SequenceValueParseNode.java
index a5d60fe..1fc670c 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/SequenceValueParseNode.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/SequenceValueParseNode.java
@@ -39,10 +39,16 @@ public class SequenceValueParseNode extends TerminalParseNode {
     }
 	private final TableName tableName;
 	private final Op op;
+	private final ParseNode numToAllocate;
 
-	public SequenceValueParseNode(TableName tableName, Op op) {
+	public SequenceValueParseNode(TableName tableName, Op op, ParseNode numToAllocate) {
 		this.tableName = tableName;
 		this.op = op;
+		this.numToAllocate = numToAllocate;
+	}
+	
+	public ParseNode getNumToAllocateNode() {
+	    return numToAllocate;
 	}
 
 	@Override

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3b1bfa0d/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServices.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServices.java b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServices.java
index dc51b10..3b53309 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServices.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServices.java
@@ -40,6 +40,7 @@ import org.apache.phoenix.schema.PName;
 import org.apache.phoenix.schema.PTable;
 import org.apache.phoenix.schema.PTableType;
 import org.apache.phoenix.schema.Sequence;
+import org.apache.phoenix.schema.SequenceAllocation;
 import org.apache.phoenix.schema.SequenceKey;
 import org.apache.phoenix.schema.stats.PTableStats;
 
@@ -91,8 +92,8 @@ public interface ConnectionQueryServices extends QueryServices, MetaDataMutated
     
     long createSequence(String tenantId, String schemaName, String sequenceName, long startWith, long incrementBy, long cacheSize, long minValue, long maxValue, boolean cycle, long timestamp) throws SQLException;
     long dropSequence(String tenantId, String schemaName, String sequenceName, long timestamp) throws SQLException;
-    void validateSequences(List<SequenceKey> sequenceKeys, long timestamp, long[] values, SQLException[] exceptions, Sequence.ValueOp action) throws SQLException;
-    void incrementSequences(List<SequenceKey> sequenceKeys, long timestamp, long[] values, SQLException[] exceptions) throws SQLException;
+    void validateSequences(List<SequenceAllocation> sequenceAllocations, long timestamp, long[] values, SQLException[] exceptions, Sequence.ValueOp action) throws SQLException;
+    void incrementSequences(List<SequenceAllocation> sequenceAllocation, long timestamp, long[] values, SQLException[] exceptions) throws SQLException;
     long currentSequenceValue(SequenceKey sequenceKey, long timestamp) throws SQLException;
     void returnSequences(List<SequenceKey> sequenceKeys, long timestamp, SQLException[] exceptions) throws SQLException;