You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@phoenix.apache.org by ja...@apache.org on 2014/10/18 03:40:53 UTC

[1/4] PHOENIX-1297 Adding utility methods to get primary key information from the optimized query plan (Samarth Jain)

Repository: phoenix
Updated Branches:
  refs/heads/3.0 b95498c55 -> ef062ad25


http://git-wip-us.apache.org/repos/asf/phoenix/blob/878570d0/phoenix-core/src/test/java/org/apache/phoenix/schema/ValueBitSetTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/schema/ValueBitSetTest.java b/phoenix-core/src/test/java/org/apache/phoenix/schema/ValueBitSetTest.java
index 9b0ebff..766917c 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/schema/ValueBitSetTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/schema/ValueBitSetTest.java
@@ -18,6 +18,7 @@
 package org.apache.phoenix.schema;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
@@ -70,6 +71,48 @@ public class ValueBitSetTest {
     }
     
     @Test
+    public void testMinNullableIndex() {
+        final int minNullableIndex = 4; // first 4 fields are not nullable.
+        int numFields = 6;
+        KeyValueSchemaBuilder builder = new KeyValueSchemaBuilder(minNullableIndex);
+        for (int i = 0; i < numFields; i++) {
+            final int fieldIndex = i;
+            builder.addField(new PDatum() {
+                @Override
+                public boolean isNullable() {
+                    // not nullable till index reaches minNullableIndex
+                    return fieldIndex < minNullableIndex;
+                }
+
+                @Override
+                public SortOrder getSortOrder() {
+                    return SortOrder.getDefault();
+                }
+
+                @Override
+                public Integer getScale() {
+                    return null;
+                }
+
+                @Override
+                public Integer getMaxLength() {
+                    return null;
+                }
+
+                @Override
+                public PDataType getDataType() {
+                    return PDataType.values()[fieldIndex % PDataType.values().length];
+                }
+            });
+        }
+        KeyValueSchema kvSchema = builder.build();
+        assertFalse(kvSchema.getFields().get(0).isNullable());
+        assertFalse(kvSchema.getFields().get(minNullableIndex - 1).isNullable());
+        assertTrue(kvSchema.getFields().get(minNullableIndex).isNullable());
+        assertTrue(kvSchema.getFields().get(minNullableIndex + 1).isNullable());
+    }
+    
+    @Test
     public void testNullCount() {
         int nFields = 32;
         int nRepeating = 5;

http://git-wip-us.apache.org/repos/asf/phoenix/blob/878570d0/phoenix-core/src/test/java/org/apache/phoenix/util/PhoenixEncodeDecodeTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/util/PhoenixEncodeDecodeTest.java b/phoenix-core/src/test/java/org/apache/phoenix/util/PhoenixEncodeDecodeTest.java
new file mode 100644
index 0000000..7ddb235
--- /dev/null
+++ b/phoenix-core/src/test/java/org/apache/phoenix/util/PhoenixEncodeDecodeTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * 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 maynot 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 applicablelaw 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;
+
+import static org.junit.Assert.assertEquals;
+
+import java.sql.Connection;
+import java.sql.Date;
+import java.sql.DriverManager;
+import java.util.Arrays;
+
+import org.apache.hadoop.hbase.util.Pair;
+import org.apache.phoenix.query.BaseConnectionlessQueryTest;
+import org.junit.Test;
+
+import com.google.common.collect.Lists;
+
+public class PhoenixEncodeDecodeTest extends BaseConnectionlessQueryTest {
+    
+    @Test
+    public void testDecodeValues1() throws Exception {
+        testDecodeValues(false, false);
+    }
+    
+    @Test
+    public void testDecodeValues2() throws Exception {
+        testDecodeValues(true, false);
+    }
+    
+    @Test
+    public void testDecodeValues3() throws Exception {
+        testDecodeValues(true, true);
+    }
+    
+    @Test
+    public void testDecodeValues4() throws Exception {
+        testDecodeValues(false, true);
+    }
+    
+    @SuppressWarnings("unchecked")
+    private void testDecodeValues(boolean nullFixedWidth, boolean nullVariableWidth) throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        conn.createStatement().execute(
+                "CREATE TABLE T(pk1 CHAR(15) not null, pk2 VARCHAR not null, CF1.v1 DATE, CF2.v2 VARCHAR, CF2.v1 VARCHAR " +
+                "CONSTRAINT pk PRIMARY KEY (pk1, pk2)) ");
+        
+        Date d = nullFixedWidth ? null : new Date(100);
+        String s = nullVariableWidth ? null : "foo";
+        Object[] values = new Object[] {"def", "eid", d, s, s};
+        byte[] bytes = PhoenixRuntime.encodeValues(conn, "T", values, Lists.newArrayList(new Pair<String, String>(null, "pk1"), new Pair<String, String>(null, "pk2"), new Pair<String, String>("cf1", "v1"), new Pair<String, String>("cf2", "v2"), new Pair<String, String>("cf2", "v1")));
+        Object[] decodedValues = PhoenixRuntime.decodeValues(conn, "T", bytes, Lists.newArrayList(new Pair<String, String>(null, "pk1"), new Pair<String, String>(null, "pk2"), new Pair<String, String>("cf1", "v1"), new Pair<String, String>("cf2", "v2"), new Pair<String, String>("cf2", "v1")));
+        assertEquals(Lists.newArrayList("def", "eid", d, s, s), Arrays.asList(decodedValues));
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/878570d0/phoenix-core/src/test/java/org/apache/phoenix/util/PhoenixRuntimeTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/util/PhoenixRuntimeTest.java b/phoenix-core/src/test/java/org/apache/phoenix/util/PhoenixRuntimeTest.java
index 0e3da98..25da3aa 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/util/PhoenixRuntimeTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/util/PhoenixRuntimeTest.java
@@ -22,8 +22,20 @@ 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 static org.junit.Assert.fail;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.commons.lang.exception.ExceptionUtils;
+import org.apache.hadoop.hbase.util.Pair;
+import org.apache.phoenix.compile.QueryPlan;
 import org.apache.phoenix.query.BaseConnectionlessQueryTest;
+import org.apache.phoenix.schema.PDataType;
 import org.junit.Test;
 
 import com.google.common.collect.ImmutableList;
@@ -81,4 +93,67 @@ public class PhoenixRuntimeTest extends BaseConnectionlessQueryTest {
         assertTrue(execCmd.isStrict());
         assertEquals("!", execCmd.getArrayElementSeparator());
     }
+    
+    @Test
+    public void testGetPkColsDataTypes() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl(), new Properties());
+        int i = 0;
+        PDataType[] pTypes = PDataType.values();
+        int size = pTypes.length;
+        StringBuilder sb = null;
+        try {
+            for (i = 0 ; i < size; i++) {
+                PDataType pType = pTypes[i];
+                String sqlTypeName = pType.getSqlTypeName();
+                if (sqlTypeName.equalsIgnoreCase("VARBINARY ARRAY")) {
+                    // we don't support VARBINARY ARRAYS yet
+                    // JIRA - https://issues.apache.org/jira/browse/PHOENIX-1329
+                    continue;
+                }
+                if (pType.isArrayType() && PDataType.arrayBaseType(pType).isFixedWidth() && PDataType.arrayBaseType(pType).getByteSize() == null) {
+                    // Need to treat array type whose base type is of fixed width whose byte size is not known as a special case. 
+                    // Cannot just use the sql type name returned by PDataType.getSqlTypeName().
+                    String baseTypeName = PDataType.arrayBaseType(pType).getSqlTypeName();
+                    sqlTypeName = baseTypeName + "(15)" + " " + PDataType.ARRAY_TYPE_SUFFIX;
+                } else if (pType.isFixedWidth() && pType.getByteSize() == null) {
+                    sqlTypeName = sqlTypeName + "(15)";
+                }
+                String columnName = "col" + i;
+                String tableName = "t" + i;
+                
+                sb = new StringBuilder(100);
+                
+                // create a table by using the type name as returned by PDataType
+                sb.append("CREATE TABLE " + tableName + " (");
+                sb.append(columnName + " " + sqlTypeName + " NOT NULL PRIMARY KEY, V1 VARCHAR)");
+                conn.createStatement().execute(sb.toString());
+
+                // generate the optimized query plan by going through the pk of the table.
+                PreparedStatement stmt = conn.prepareStatement("SELECT * FROM " + tableName + " WHERE " + columnName  + " = ?");
+                Integer maxLength = pType.isFixedWidth() && pType.getByteSize() == null ? 15 : null;
+                stmt.setObject(1, pType.getSampleValue(maxLength));
+                QueryPlan plan = PhoenixRuntime.getOptimizedQueryPlan(stmt);
+
+                // now go through the utility method, get column name and type name and
+                // try creating another table with the returned info. Use the query plan generated above.
+                // If table can be created with the returned sql type name, then great!
+                // It would mean "Roundtrip" of column data type name works.
+                List<Pair<String, String>> pkCols = new ArrayList<Pair<String, String>>();
+                List<String> dataTypes = new ArrayList<String>();
+                PhoenixRuntime.getPkColsDataTypesForSql(pkCols, dataTypes, plan, conn, true);
+
+                tableName = "newt" + i;
+                columnName = "newCol" + i;
+                String roundTripSqlTypeName = dataTypes.get(0);
+
+                // create a table by using the type name as returned by the utility method
+                sb = new StringBuilder(100);
+                sb.append("CREATE TABLE " + tableName + " (");
+                sb.append(columnName + " " + roundTripSqlTypeName + " NOT NULL PRIMARY KEY)");
+                conn.createStatement().execute(sb.toString());
+            }
+        } catch (Exception e) {
+            fail("Failed sql: " + sb.toString() + ExceptionUtils.getStackTrace(e));
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/878570d0/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 6538d89..0e21ed1 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
@@ -616,7 +616,10 @@ public class PhoenixHBaseLoaderIT {
 
     @AfterClass
     public static void tearDownAfterClass() throws Exception {
-        conn.close();
-        hbaseTestUtil.shutdownMiniCluster();
+        try {
+            conn.close();
+        } finally {
+            hbaseTestUtil.shutdownMiniCluster();
+        }
     }
 }
\ No newline at end of file


[3/4] git commit: PHOENIX-1365 Make sequence salt buckets configurable

Posted by ja...@apache.org.
PHOENIX-1365 Make sequence salt buckets configurable

Conflicts:
	phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java


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

Branch: refs/heads/3.0
Commit: cf07a5dd5981e0b6dbb5d1b562bd47fee32f97dc
Parents: 878570d
Author: James Taylor <jt...@salesforce.com>
Authored: Fri Oct 17 13:35:17 2014 -0700
Committer: James Taylor <jt...@salesforce.com>
Committed: Fri Oct 17 18:44:13 2014 -0700

----------------------------------------------------------------------
 .../phoenix/query/ConnectionQueryServicesImpl.java       |  3 +--
 .../java/org/apache/phoenix/schema/MetaDataClient.java   | 11 +++++------
 .../phoenix/schema/NewerTableAlreadyExistsException.java |  9 +++++++++
 .../java/org/apache/phoenix/util/PhoenixRuntime.java     |  3 +--
 4 files changed, 16 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/cf07a5dd/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 f1d12fc..688cf42 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
@@ -1343,8 +1343,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
                             } catch (NewerTableAlreadyExistsException e) {
                                 // Ignore, as this will happen if the SYSTEM.SEQUENCE already exists at this fixed timestamp.
                                 // A TableAlreadyExistsException is not thrown, since the table only exists *after* this fixed timestamp.
-                                PTable sequenceTable = ConnectionQueryServicesImpl.this.latestMetaData.getTable(new PTableKey(null, PhoenixDatabaseMetaData.SEQUENCE_FULLNAME));
-                                Integer sequenceSaltBuckets = sequenceTable.getBucketNum();
+                                Integer sequenceSaltBuckets = e.getTable().getBucketNum();
                                 nSequenceSaltBuckets = sequenceSaltBuckets == null ? 0 : sequenceSaltBuckets;
                             } catch (TableAlreadyExistsException e) {
                                 // This will occur if we have an older SYSTEM.SEQUENCE, so we need to update it to include

http://git-wip-us.apache.org/repos/asf/phoenix/blob/cf07a5dd/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 e950903..712ef5c 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,9 @@ public class MetaDataClient {
             case PARENT_TABLE_NOT_FOUND:
                 throw new TableNotFoundException(schemaName, parent.getName().getString());
             case NEWER_TABLE_FOUND:
-                throw new NewerTableAlreadyExistsException(schemaName, tableName);
+                // Add table to ConnectionQueryServices so it's cached, but don't add
+                // it to this connection as we can't see it.
+                throw new NewerTableAlreadyExistsException(schemaName, tableName, result.getTable());
             case UNALLOWED_TABLE_MUTATION:
                 throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_MUTATE_TABLE)
                     .setSchemaName(schemaName).setTableName(tableName).build().buildException();
@@ -1468,7 +1470,7 @@ public class MetaDataClient {
                     }
                     break;
                 case NEWER_TABLE_FOUND:
-                    throw new NewerTableAlreadyExistsException(schemaName, tableName);
+                    throw new NewerTableAlreadyExistsException(schemaName, tableName, result.getTable());
                 case UNALLOWED_TABLE_MUTATION:
                     throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_MUTATE_TABLE)
                         .setSchemaName(schemaName).setTableName(tableName).build().buildException();
@@ -1579,10 +1581,7 @@ public class MetaDataClient {
             }
             throw new ConcurrentTableMutationException(schemaName, tableName);
         case NEWER_TABLE_FOUND:
-            if (result.getTable() != null) {
-                connection.addTable(result.getTable());
-            }
-            throw new NewerTableAlreadyExistsException(schemaName, tableName);
+            throw new NewerTableAlreadyExistsException(schemaName, tableName, result.getTable());
         case NO_PK_COLUMNS:
             throw new SQLExceptionInfo.Builder(SQLExceptionCode.PRIMARY_KEY_MISSING)
                 .setSchemaName(schemaName).setTableName(tableName).build().buildException();

http://git-wip-us.apache.org/repos/asf/phoenix/blob/cf07a5dd/phoenix-core/src/main/java/org/apache/phoenix/schema/NewerTableAlreadyExistsException.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/NewerTableAlreadyExistsException.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/NewerTableAlreadyExistsException.java
index c5afcec..5404485 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/NewerTableAlreadyExistsException.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/NewerTableAlreadyExistsException.java
@@ -19,9 +19,18 @@ package org.apache.phoenix.schema;
 
 public class NewerTableAlreadyExistsException extends TableAlreadyExistsException {
 	private static final long serialVersionUID = 1L;
+	private final PTable table;
 
     public NewerTableAlreadyExistsException(String schemaName, String tableName) {
+        this(schemaName, tableName, null);
+    }
+
+    public NewerTableAlreadyExistsException(String schemaName, String tableName, PTable table) {
         super(schemaName, tableName);
+        this.table = table;
     }
 
+    public PTable getTable() {
+        return table;
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/cf07a5dd/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 36c3ede..3d19692 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
@@ -61,6 +61,7 @@ import org.apache.phoenix.query.QueryConstants;
 import org.apache.phoenix.schema.AmbiguousColumnException;
 import org.apache.phoenix.schema.ColumnNotFoundException;
 import org.apache.phoenix.schema.KeyValueSchema;
+import org.apache.phoenix.schema.KeyValueSchema.KeyValueSchemaBuilder;
 import org.apache.phoenix.schema.MetaDataClient;
 import org.apache.phoenix.schema.PColumn;
 import org.apache.phoenix.schema.PColumnFamily;
@@ -70,10 +71,8 @@ import org.apache.phoenix.schema.PTableKey;
 import org.apache.phoenix.schema.PTableType;
 import org.apache.phoenix.schema.RowKeySchema;
 import org.apache.phoenix.schema.TableNotFoundException;
-import org.apache.phoenix.schema.KeyValueSchema.KeyValueSchemaBuilder;
 import org.apache.phoenix.schema.ValueBitSet;
 
-import com.google.common.base.Preconditions;
 import com.google.common.base.Splitter;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;


[2/4] git commit: PHOENIX-1297 Adding utility methods to get primary key information from the optimized query plan (Samarth Jain)

Posted by ja...@apache.org.
PHOENIX-1297 Adding utility methods to get primary key information from the optimized query plan (Samarth Jain)


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

Branch: refs/heads/3.0
Commit: 878570d077f98b4c381c19ec44c444e7b4d75fbc
Parents: b95498c
Author: James Taylor <jt...@salesforce.com>
Authored: Fri Oct 17 18:37:50 2014 -0700
Committer: James Taylor <jt...@salesforce.com>
Committed: Fri Oct 17 18:37:50 2014 -0700

----------------------------------------------------------------------
 .../org/apache/phoenix/end2end/ArrayIT.java     |   8 +-
 .../phoenix/end2end/PhoenixEncodeDecodeIT.java  | 215 ---------------
 .../org/apache/phoenix/end2end/QueryMoreIT.java | 141 ++++++----
 .../org/apache/phoenix/end2end/StddevIT.java    |  40 +--
 .../apache/phoenix/schema/KeyValueSchema.java   |   5 +-
 .../org/apache/phoenix/schema/PDataType.java    | 108 ++++----
 .../java/org/apache/phoenix/util/IndexUtil.java |  20 ++
 .../org/apache/phoenix/util/PhoenixRuntime.java | 274 +++++++++++++++++++
 .../org/apache/phoenix/util/SchemaUtil.java     |  27 ++
 .../phoenix/compile/QueryOptimizerTest.java     | 264 ++++++++++++++++++
 .../apache/phoenix/schema/ValueBitSetTest.java  |  43 +++
 .../phoenix/util/PhoenixEncodeDecodeTest.java   |  72 +++++
 .../apache/phoenix/util/PhoenixRuntimeTest.java |  77 +++++-
 .../phoenix/pig/PhoenixHBaseLoaderIT.java       |   7 +-
 14 files changed, 935 insertions(+), 366 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/878570d0/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 d2dbda6..f44a517 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
@@ -43,7 +43,6 @@ import org.apache.phoenix.util.PhoenixRuntime;
 import org.apache.phoenix.util.PropertiesUtil;
 import org.apache.phoenix.util.SchemaUtil;
 import org.apache.phoenix.util.StringUtil;
-import org.junit.Assert;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 
@@ -1220,13 +1219,8 @@ public class ArrayIT extends BaseClientManagedTimeIT {
 			PreparedStatement statement = conn.prepareStatement(query);
 			ResultSet rs = statement.executeQuery();
 			assertTrue(rs.next());
-			Double[] doubleArr = new Double[1];
-			doubleArr[0] = 36.763;
-			Array array = conn.createArrayOf("DOUBLE", doubleArr);
 			PhoenixArray resultArray = (PhoenixArray) rs.getArray(1);
-			assertEquals(resultArray, array);
-			Assert.fail("Should have failed");
-		} catch (Exception e) {
+			assertNull(resultArray);
 		} finally {
 			conn.close();
 		}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/878570d0/phoenix-core/src/it/java/org/apache/phoenix/end2end/PhoenixEncodeDecodeIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/PhoenixEncodeDecodeIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/PhoenixEncodeDecodeIT.java
deleted file mode 100644
index bdb0745..0000000
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/PhoenixEncodeDecodeIT.java
+++ /dev/null
@@ -1,215 +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 maynot 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 applicablelaw 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 java.sql.Connection;
-import java.sql.Date;
-import java.sql.DriverManager;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.util.Arrays;
-import java.util.Properties;
-
-import org.apache.phoenix.util.PhoenixRuntime;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-
-@Category(HBaseManagedTimeTest.class)
-public class PhoenixEncodeDecodeIT extends BaseHBaseManagedTimeIT {
-    
-    private static String tenantId = "ABC";
-    
-    @Test
-    public void testEncodeDecode() throws Exception {
-        Connection conn = DriverManager.getConnection(getUrl());
-        conn.createStatement().execute(
-                "CREATE TABLE t(org_id CHAR(3) not null, p_id CHAR(3) not null, date DATE not null, e_id CHAR(3) not null, old_value VARCHAR, new_value VARCHAR " +
-                "CONSTRAINT pk PRIMARY KEY (org_id, p_id, date, e_id))");
-        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO t VALUES (?, ?, ?, ?, ?)");
-        Date date = new Date(System.currentTimeMillis());
-        stmt.setString(1,  "abc");
-        stmt.setString(2,  "def");
-        stmt.setDate(3,  date);
-        stmt.setString(4,  "eid");
-        stmt.setString(5, "old");
-        stmt.executeUpdate();
-        conn.commit();
-
-        stmt = conn.prepareStatement("SELECT org_id, p_id, date, e_id FROM T");
-
-        Object[] retrievedValues = new Object[4];
-        ResultSet rs = stmt.executeQuery();
-        rs.next();
-        retrievedValues[0] = rs.getString(1);
-        retrievedValues[1] = rs.getString(2);
-        retrievedValues[2] = rs.getDate(3);
-        retrievedValues[3] = rs.getString(4);
-
-        byte[] value = PhoenixRuntime.encodePK(conn, "T", retrievedValues);
-        Object[] decodedValues = PhoenixRuntime.decodePK(conn, "T", value);
-
-        assertEquals(Arrays.asList(decodedValues), Arrays.asList(retrievedValues));
-    }
-
-    @Test
-    public void testEncodeDecodeSalted() throws Exception {
-        Connection conn = DriverManager.getConnection(getUrl());
-        conn.createStatement().execute(
-                "CREATE TABLE t(org_id CHAR(3) not null, p_id CHAR(3) not null, date DATE not null, e_id CHAR(3) not null, old_value VARCHAR, new_value VARCHAR " +
-                "CONSTRAINT pk PRIMARY KEY (org_id, p_id, date, e_id)) SALT_BUCKETS = 2");
-        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO t VALUES (?, ?, ?, ?, ?)");
-        Date date = new Date(System.currentTimeMillis());
-        stmt.setString(1,  "abc");
-        stmt.setString(2,  "def");
-        stmt.setDate(3,  date);
-        stmt.setString(4,  "eid");
-        stmt.setString(5, "old");
-        stmt.executeUpdate();
-        conn.commit();
-
-        stmt = conn.prepareStatement("SELECT org_id, p_id, date, e_id FROM T");
-
-        Object[] retrievedValues = new Object[4];
-        ResultSet rs = stmt.executeQuery();
-        rs.next();
-        retrievedValues[0] = rs.getString(1);
-        retrievedValues[1] = rs.getString(2);
-        retrievedValues[2] = rs.getDate(3);
-        retrievedValues[3] = rs.getString(4);
-
-        byte[] value = PhoenixRuntime.encodePK(conn, "T", retrievedValues);
-        Object[] decodedValues = PhoenixRuntime.decodePK(conn, "T", value);
-
-        assertEquals(Arrays.asList(decodedValues), Arrays.asList(retrievedValues));
-    }
-
-    @Test
-    public void testEncodeDecodeMultiTenant() throws Exception {
-        Connection globalConn = DriverManager.getConnection(getUrl());
-        try {
-            globalConn.createStatement().execute(
-                    "CREATE TABLE T(tenant_id CHAR(3) not null, p_id CHAR(3) not null, date DATE not null, e_id CHAR(3) not null, old_value VARCHAR, new_value VARCHAR " +
-                    "CONSTRAINT pk PRIMARY KEY (tenant_id, p_id, date, e_id)) MULTI_TENANT = true");
-        } finally {
-            globalConn.close();
-        }
-
-        Connection tenantConn = getTenantSpecificConnection();
-
-        //create tenant-specific view. 
-        tenantConn.createStatement().execute("CREATE VIEW TENANT_TABLE AS SELECT * FROM T");
-        
-        PreparedStatement stmt = tenantConn.prepareStatement("UPSERT INTO TENANT_TABLE (p_id, date, e_id) VALUES (?, ?, ?)");
-        Date date = new Date(System.currentTimeMillis());
-        stmt.setString(1,  "def");
-        stmt.setDate(2,  date);
-        stmt.setString(3,  "eid");
-        stmt.executeUpdate();
-        tenantConn.commit();
-
-        stmt = tenantConn.prepareStatement("SELECT p_id, date, e_id FROM TENANT_TABLE");
-
-        Object[] retrievedValues = new Object[3];
-        ResultSet rs = stmt.executeQuery();
-        rs.next();
-        retrievedValues[0] = rs.getString(1);
-        retrievedValues[1] = rs.getDate(2);
-        retrievedValues[2] = rs.getString(3);
-
-        byte[] value = PhoenixRuntime.encodePK(tenantConn, "TENANT_TABLE", retrievedValues);
-        Object[] decodedValues = PhoenixRuntime.decodePK(tenantConn, "TENANT_TABLE", value);
-
-        assertEquals(Arrays.asList(decodedValues), Arrays.asList(retrievedValues));
-    }
-
-    @Test
-    public void testEncodeDecodeSaltedMultiTenant() throws Exception {
-        Connection globalConn = DriverManager.getConnection(getUrl());
-        try {
-            globalConn.createStatement().execute(
-                    "CREATE TABLE T(tenant_id CHAR(3) not null, p_id CHAR(3) not null, date DATE not null, e_id CHAR(3) not null, old_value VARCHAR, new_value VARCHAR " +
-                    "CONSTRAINT pk PRIMARY KEY (tenant_id, p_id, date, e_id)) MULTI_TENANT = true, SALT_BUCKETS = 2");
-        } finally {
-            globalConn.close();
-        }
-
-        Connection tenantConn = getTenantSpecificConnection();
-
-        //create tenant-specific view. 
-        tenantConn.createStatement().execute("CREATE VIEW TENANT_TABLE AS SELECT * FROM T");
-        
-        PreparedStatement stmt = tenantConn.prepareStatement("UPSERT INTO TENANT_TABLE (p_id, date, e_id) VALUES (?, ?, ?)");
-        Date date = new Date(System.currentTimeMillis());
-        stmt.setString(1,  "def");
-        stmt.setDate(2,  date);
-        stmt.setString(3,  "eid");
-        stmt.executeUpdate();
-        tenantConn.commit();
-
-        stmt = tenantConn.prepareStatement("SELECT p_id, date, e_id FROM TENANT_TABLE");
-
-        Object[] retrievedValues = new Object[3];
-        ResultSet rs = stmt.executeQuery();
-        rs.next();
-        retrievedValues[0] = rs.getString(1);
-        retrievedValues[1] = rs.getDate(2);
-        retrievedValues[2] = rs.getString(3);
-
-        byte[] value = PhoenixRuntime.encodePK(tenantConn, "TENANT_TABLE", retrievedValues);
-        Object[] decodedValues = PhoenixRuntime.decodePK(tenantConn, "TENANT_TABLE", value);
-
-        assertEquals(Arrays.asList(decodedValues), Arrays.asList(retrievedValues));
-    }
-    
-    @Test
-    public void testEncodeDecodePaddingPks() throws Exception {
-        Connection conn = DriverManager.getConnection(getUrl());
-        conn.createStatement().execute(
-                "CREATE TABLE T(pk1 CHAR(15) not null, pk2 CHAR(15) not null, v1 DATE " +
-                "CONSTRAINT pk PRIMARY KEY (pk1, pk2))");
-        
-        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO T (pk1, pk2, v1) VALUES (?, ?, ?)");
-        stmt.setString(1,  "def");
-        stmt.setString(2,  "eid");
-        stmt.setDate(3, new Date(100));
-        stmt.executeUpdate();
-        conn.commit();
-
-        stmt = conn.prepareStatement("SELECT pk1, pk2 FROM T");
-
-        Object[] retrievedValues = new Object[2];
-        ResultSet rs = stmt.executeQuery();
-        rs.next();
-        retrievedValues[0] = rs.getString(1);
-        retrievedValues[1] = rs.getString(2);
-        
-        byte[] value = PhoenixRuntime.encodePK(conn, "T", retrievedValues);
-        Object[] decodedValues = PhoenixRuntime.decodePK(conn, "T", value);
-
-        assertEquals(Arrays.asList(decodedValues), Arrays.asList(retrievedValues));
-    }
-
-    private static Connection getTenantSpecificConnection() throws Exception {
-        Properties props = new Properties();
-        props.setProperty(PhoenixRuntime.TENANT_ID_ATTRIB, tenantId);
-        return DriverManager.getConnection(getUrl(), props);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/878570d0/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryMoreIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryMoreIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryMoreIT.java
index 5173b0e..e82abbb 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryMoreIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryMoreIT.java
@@ -31,6 +31,7 @@ import java.util.Map;
 import java.util.Properties;
 
 import org.apache.hadoop.hbase.util.Base64;
+import org.apache.hadoop.hbase.util.Pair;
 import org.apache.phoenix.util.PhoenixRuntime;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
@@ -40,40 +41,41 @@ import com.google.common.collect.Lists;
 @Category(HBaseManagedTimeTest.class)
 public class QueryMoreIT extends BaseHBaseManagedTimeIT {
     
-    //Data table - multi-tenant = true, salted = true 
+    private String dataTableName;
+    //queryAgainstTenantSpecificView = true, dataTableSalted = true 
     @Test
     public void testQueryMore1() throws Exception {
         testQueryMore(true, true);
     }
     
-    //Data table - multi-tenant = false, salted = true 
+    //queryAgainstTenantSpecificView = false, dataTableSalted = true 
     @Test
     public void testQueryMore2() throws Exception {
         testQueryMore(false, true);
     }
     
-    //Data table - multi-tenant = false, salted = false
+    //queryAgainstTenantSpecificView = false, dataTableSalted = false
     @Test
     public void testQueryMore3() throws Exception {
         testQueryMore(false, false);
     }
     
-    //Data table - multi-tenant = true, salted = false 
+    //queryAgainstTenantSpecificView = true, dataTableSalted = false 
     @Test
     public void testQueryMore4() throws Exception {
         testQueryMore(true, false);
     }
     
-    private void testQueryMore(boolean dataTableMultiTenant, boolean dataTableSalted) throws Exception {
+    private void testQueryMore(boolean queryAgainstTenantSpecificView, boolean dataTableSalted) throws Exception {
         String[] tenantIds = new String[] {"00Dxxxxxtenant1", "00Dxxxxxtenant2", "00Dxxxxxtenant3"};
         int numRowsPerTenant = 10;
         String cursorTableName = "CURSOR_TABLE";
-        String dataTableName = "BASE_HISTORY_TABLE" + (dataTableMultiTenant ? "_MULTI" : "") + (dataTableSalted ? "_SALTED" : "");
+        this.dataTableName = "BASE_HISTORY_TABLE" + (dataTableSalted ? "_SALTED" : "");
         String cursorTableDDL = "CREATE TABLE IF NOT EXISTS " + 
                 cursorTableName +  " (\n" +  
                 "TENANT_ID VARCHAR(15) NOT NULL\n," +  
                 "QUERY_ID VARCHAR(15) NOT NULL,\n" +
-                "CURSOR_ORDER BIGINT NOT NULL\n" + 
+                "CURSOR_ORDER BIGINT NOT NULL \n" + 
                 "CONSTRAINT CURSOR_TABLE_PK PRIMARY KEY (TENANT_ID, QUERY_ID, CURSOR_ORDER)) "+
                 "SALT_BUCKETS = 4, TTL=86400";
         String baseDataTableDDL = "CREATE TABLE IF NOT EXISTS " +
@@ -86,7 +88,7 @@ public class QueryMoreIT extends BaseHBaseManagedTimeIT {
                 "OLDVAL_STRING VARCHAR,\n" + 
                 "NEWVAL_STRING VARCHAR\n" + 
                 "CONSTRAINT PK PRIMARY KEY(TENANT_ID, PARENT_ID, CREATED_DATE DESC, ENTITY_HISTORY_ID)) " + 
-                "VERSIONS = 1, MULTI_TENANT = true, SALT_BUCKETS = 4";
+                "VERSIONS = 1, MULTI_TENANT = true" + (dataTableSalted ? ", SALT_BUCKETS = 4" : "");
         
         //create cursor and data tables.
         Connection conn = DriverManager.getConnection(getUrl());
@@ -94,14 +96,36 @@ public class QueryMoreIT extends BaseHBaseManagedTimeIT {
         conn.createStatement().execute(baseDataTableDDL);
         conn.close();
         
-        //upsert rows in the data table.
+        //upsert rows in the data table for all the tenantIds
         Map<String, List<String>> historyIdsPerTenant = createHistoryTableRows(dataTableName, tenantIds, numRowsPerTenant);
         
+        // assert query more for tenantId -> tenantIds[0]
         String tenantId = tenantIds[0];
         String cursorQueryId = "00TcursrqueryId";
-        String tenantViewName = dataTableMultiTenant ? ("HISTORY_TABLE" + "_" + tenantId) : null;
-        assertEquals(numRowsPerTenant, upsertSelectRecordsInCursorTableForTenant(dataTableName, dataTableMultiTenant, tenantId, tenantViewName, cursorQueryId));
+        String tableOrViewName = queryAgainstTenantSpecificView ? ("\"HISTORY_TABLE" + "_" + tenantId + "\"") : dataTableName;
         
+        assertEquals(numRowsPerTenant, upsertSelectRecordsInCursorTableForTenant(tableOrViewName, queryAgainstTenantSpecificView, tenantId, cursorQueryId));
+        
+        /*// assert that the data inserted in cursor table matches the data in the data table for tenantId.
+        String selectDataTable = "SELECT TENANT_ID, PARENT_ID, CREATED_DATE, ENTITY_HISTORY_ID FROM BASE_HISTORY_TABLE WHERE TENANT_ID = ? ";
+        String selectCursorTable = "SELECT TENANT_ID, PARENT_ID, CREATED_DATE, ENTITY_HISTORY_ID FROM  CURSOR_TABLE (PARENT_ID CHAR(15), CREATED_DATE DATE, ENTITY_HISTORY_ID CHAR(15)) WHERE TENANT_ID = ? ";
+        
+        PreparedStatement stmtData = DriverManager.getConnection(getUrl()).prepareStatement(selectDataTable);
+        stmtData.setString(1, tenantId);
+        ResultSet rsData = stmtData.executeQuery();
+        
+        PreparedStatement stmtCursor = DriverManager.getConnection(getUrl()).prepareStatement(selectCursorTable);
+        stmtCursor.setString(1, tenantId);
+        ResultSet rsCursor = stmtCursor.executeQuery();
+        
+        while(rsData.next() && rsCursor.next()) {
+            assertEquals(rsData.getString("TENANT_ID"), rsCursor.getString("TENANT_ID"));
+            assertEquals(rsData.getString("PARENT_ID"), rsCursor.getString("PARENT_ID"));
+            assertEquals(rsData.getDate("CREATED_DATE"), rsCursor.getDate("CREATED_DATE"));
+            assertEquals(rsData.getString("ENTITY_HISTORY_ID"), rsCursor.getString("ENTITY_HISTORY_ID"));
+        }
+        
+        */
         Connection conn2 = DriverManager.getConnection(getUrl());
         ResultSet rs = conn2.createStatement().executeQuery("SELECT count(*) from " + cursorTableName);
         rs.next();
@@ -110,20 +134,28 @@ public class QueryMoreIT extends BaseHBaseManagedTimeIT {
         
         int startOrder = 0;
         int endOrder = 5;
-        int numRecordsThatShouldBeRetrieved = 5;
+        int numRecordsThatShouldBeRetrieved = numRowsPerTenant/2; // we will test for two rounds of query more.
         
-        //get first batch of cursor ids out of the cursor table.
-        String[] cursorIds = getRecordsOutofCursorTable(dataTableName, tenantId, cursorQueryId, startOrder, endOrder, numRecordsThatShouldBeRetrieved);
+        // get first batch of cursor ids out of the cursor table.
+        String[] cursorIds = getRecordsOutofCursorTable(tableOrViewName, queryAgainstTenantSpecificView, tenantId, cursorQueryId, startOrder, endOrder);
         assertEquals(numRecordsThatShouldBeRetrieved, cursorIds.length);
-        
-        //now query against the tenant view and fetch first batch of records.
-        List<String> historyIds = doQueryMore(dataTableName, dataTableMultiTenant, tenantId, tenantViewName, cursorIds);
+        // now query and fetch first batch of records.
+        List<String> historyIds = doQueryMore(queryAgainstTenantSpecificView, tenantId, tableOrViewName, cursorIds);
+        // assert that history ids match for this tenant
         assertEquals(historyIdsPerTenant.get(tenantId).subList(startOrder, endOrder), historyIds);
         
-        cursorIds = getRecordsOutofCursorTable(dataTableName, tenantId, cursorQueryId, startOrder + 5, endOrder + 5, numRecordsThatShouldBeRetrieved);
+        // get the next batch of cursor ids out of the cursor table.
+        cursorIds = getRecordsOutofCursorTable(tableOrViewName, queryAgainstTenantSpecificView, tenantId, cursorQueryId, startOrder + numRecordsThatShouldBeRetrieved, endOrder + numRecordsThatShouldBeRetrieved);
         assertEquals(numRecordsThatShouldBeRetrieved, cursorIds.length);
-        historyIds = doQueryMore(dataTableName, dataTableMultiTenant, tenantId, tenantViewName, cursorIds);
-        assertEquals(historyIdsPerTenant.get(tenantId).subList(startOrder + 5, endOrder+ 5), historyIds);
+        // now query and fetch the next batch of records.
+        historyIds = doQueryMore(queryAgainstTenantSpecificView, tenantId, tableOrViewName, cursorIds);
+        // assert that the history ids match for this tenant
+        assertEquals(historyIdsPerTenant.get(tenantId).subList(startOrder + numRecordsThatShouldBeRetrieved, endOrder+ numRecordsThatShouldBeRetrieved), historyIds);
+        
+         // get the next batch of cursor ids out of the cursor table.
+        cursorIds = getRecordsOutofCursorTable(tableOrViewName, queryAgainstTenantSpecificView, tenantId, cursorQueryId, startOrder + 2 * numRecordsThatShouldBeRetrieved, endOrder + 2 * numRecordsThatShouldBeRetrieved);
+        // assert that there are no more cursorids left for this tenant.
+        assertEquals(0, cursorIds.length);
     }
     
     private Map<String, List<String>> createHistoryTableRows(String dataTableName, String[] tenantIds, int numRowsPerTenant) throws Exception {
@@ -133,7 +165,7 @@ public class QueryMoreIT extends BaseHBaseManagedTimeIT {
         try {
             PreparedStatement stmt = conn.prepareStatement(upsertDML);
             for (int j = 0; j < tenantIds.length; j++) {
-                List<String> parentIds = new ArrayList<String>();
+                List<String> historyIds = new ArrayList<String>();
                 for (int i = 0; i < numRowsPerTenant; i++) {
                     stmt.setString(1, tenantIds[j]);
                     String parentId = "parentId" + i;
@@ -145,9 +177,9 @@ public class QueryMoreIT extends BaseHBaseManagedTimeIT {
                     stmt.setString(6, "oldval");
                     stmt.setString(7, "newval");
                     stmt.executeUpdate();
-                    parentIds.add(historyId);
+                    historyIds.add(historyId);
                 }
-                historyIdsForTenant.put(tenantIds[j], parentIds);
+                historyIdsForTenant.put(tenantIds[j], historyIds);
             }
             conn.commit();
             return historyIdsForTenant;
@@ -156,29 +188,29 @@ public class QueryMoreIT extends BaseHBaseManagedTimeIT {
         }
     }
     
-    private int upsertSelectRecordsInCursorTableForTenant(String baseTableName, boolean dataTableMultiTenant, String tenantId, String tenantViewName, String cursorQueryId) throws Exception {
+    private int upsertSelectRecordsInCursorTableForTenant(String tableOrViewName, boolean queryAgainstTenantView, String tenantId, String cursorQueryId) throws Exception {
         String sequenceName = "\"" + tenantId + "_SEQ\"";
-        Connection conn = dataTableMultiTenant ? getTenantSpecificConnection(tenantId) : DriverManager.getConnection(getUrl());
+        Connection conn = queryAgainstTenantView ? getTenantSpecificConnection(tenantId) : DriverManager.getConnection(getUrl());
         
         // Create a sequence. This sequence is used to fill cursor_order column for each row inserted in the cursor table.
         conn.createStatement().execute("CREATE SEQUENCE " + sequenceName + " CACHE " + Long.MAX_VALUE);
         conn.setAutoCommit(true);
-        if (dataTableMultiTenant) {
-            createTenantSpecificViewIfNecessary(baseTableName, tenantViewName, conn);
+        if (queryAgainstTenantView) {
+            createTenantSpecificViewIfNecessary(tableOrViewName, conn);
         }
         try {
-            String tableName = dataTableMultiTenant ? tenantViewName : baseTableName;
-            String tenantIdFilter = dataTableMultiTenant ? "" : " WHERE TENANT_ID = ? ";
+            String tenantIdFilter = queryAgainstTenantView ? "" : " WHERE TENANT_ID = ? ";
             
             // Using dynamic columns, we can use the same cursor table for storing primary keys for all the tables.  
             String upsertSelectDML = "UPSERT INTO CURSOR_TABLE " +
                                      "(TENANT_ID, QUERY_ID, CURSOR_ORDER, PARENT_ID CHAR(15), CREATED_DATE DATE, ENTITY_HISTORY_ID CHAR(15)) " + 
                                      "SELECT ?, ?, NEXT VALUE FOR " + sequenceName + ", PARENT_ID, CREATED_DATE, ENTITY_HISTORY_ID " +
-                                     " FROM " + tableName + tenantIdFilter;
+                                     " FROM " + tableOrViewName + tenantIdFilter;
+            
             PreparedStatement stmt = conn.prepareStatement(upsertSelectDML);
             stmt.setString(1, tenantId);
             stmt.setString(2, cursorQueryId);
-            if (!dataTableMultiTenant)  {
+            if (!queryAgainstTenantView)  {
                 stmt.setString(3, tenantId);
             }
             int numRecords = stmt.executeUpdate();
@@ -198,18 +230,19 @@ public class QueryMoreIT extends BaseHBaseManagedTimeIT {
         return DriverManager.getConnection(getUrl(), props);
     }
     
-    private String createTenantSpecificViewIfNecessary(String baseTableName, String tenantViewName, Connection tenantConn) throws Exception {
-        tenantConn.createStatement().execute("CREATE VIEW IF NOT EXISTS " + tenantViewName + " AS SELECT * FROM " + baseTableName);
+    private String createTenantSpecificViewIfNecessary(String tenantViewName, Connection tenantConn) throws Exception {
+        tenantConn.createStatement().execute("CREATE VIEW IF NOT EXISTS " + tenantViewName + " AS SELECT * FROM " + dataTableName);
         return tenantViewName;
     }
     
-    private String[] getRecordsOutofCursorTable(String dataTableName, String tenantId, String cursorQueryId, int startOrder, int endOrder, int numRecordsThatShouldBeRetrieved) throws Exception {
+    private String[] getRecordsOutofCursorTable(String tableOrViewName, boolean queryAgainstTenantSpecificView, String tenantId, String cursorQueryId, int startOrder, int endOrder) throws Exception {
         Connection conn = DriverManager.getConnection(getUrl());
-        List<String> pkIds = Lists.newArrayListWithCapacity(numRecordsThatShouldBeRetrieved);
-
-        String selectCursorSql = "SELECT TENANT_ID, PARENT_ID, CREATED_DATE, ENTITY_HISTORY_ID " +
+        List<String> pkIds = new ArrayList<String>();
+        String cols = queryAgainstTenantSpecificView ? "PARENT_ID, CREATED_DATE, ENTITY_HISTORY_ID" : "TENANT_ID, PARENT_ID, CREATED_DATE, ENTITY_HISTORY_ID";
+        String dynCols = queryAgainstTenantSpecificView ? "(PARENT_ID CHAR(15), CREATED_DATE DATE, ENTITY_HISTORY_ID CHAR(15))" : "(TENANT_ID CHAR(15), PARENT_ID CHAR(15), CREATED_DATE DATE, ENTITY_HISTORY_ID CHAR(15))";
+        String selectCursorSql = "SELECT " + cols + " " +
                 "FROM CURSOR_TABLE \n" +
-                "(TENANT_ID CHAR(15), PARENT_ID CHAR(15), CREATED_DATE DATE, ENTITY_HISTORY_ID CHAR(15)) \n" + 
+                 dynCols +   " \n" + 
                 "WHERE TENANT_ID = ? AND \n" +  
                 "QUERY_ID = ? AND \n" + 
                 "CURSOR_ORDER > ? AND \n" + 
@@ -222,33 +255,34 @@ public class QueryMoreIT extends BaseHBaseManagedTimeIT {
         stmt.setInt(4, endOrder);
 
         ResultSet rs = stmt.executeQuery();
+        @SuppressWarnings("unchecked")
+        List<Pair<String, String>> columns = queryAgainstTenantSpecificView ? Lists.newArrayList(new Pair<String, String>(null, "PARENT_ID"), new Pair<String, String>(null, "CREATED_DATE"), new Pair<String, String>(null, "ENTITY_HISTORY_ID")) : Lists.newArrayList(new Pair<String, String>(null, "TENANT_ID"), new Pair<String, String>(null, "PARENT_ID"), new Pair<String, String>(null, "CREATED_DATE"), new Pair<String, String>(null, "ENTITY_HISTORY_ID"));
         while(rs.next()) {
-            Object[] values = new Object[4];
-            for (int i = 0; i < 4; i++) {
+            Object[] values = new Object[columns.size()];
+            for (int i = 0; i < columns.size(); i++) {
                 values[i] = rs.getObject(i + 1);
             }
-            pkIds.add(Base64.encodeBytes(PhoenixRuntime.encodePK(conn, dataTableName, values)));
+            conn = getTenantSpecificConnection(tenantId);
+            pkIds.add(Base64.encodeBytes(PhoenixRuntime.encodeValues(conn, tableOrViewName, values, columns)));
         }
         return pkIds.toArray(new String[pkIds.size()]);
     }
     
-    private List<String> doQueryMore(String dataTableName, boolean dataTableMultiTenant, String tenantId, String tenantViewName, String[] cursorIds) throws Exception {
-        Connection tenantConn = dataTableMultiTenant ? getTenantSpecificConnection(tenantId) : DriverManager.getConnection(getUrl());
-        String tableName = dataTableMultiTenant ? tenantViewName : dataTableName;
+    private List<String> doQueryMore(boolean queryAgainstTenantView, String tenantId, String tenantViewName, String[] cursorIds) throws Exception {
+        Connection conn = queryAgainstTenantView ? getTenantSpecificConnection(tenantId) : DriverManager.getConnection(getUrl());
+        String tableName = queryAgainstTenantView ? tenantViewName : dataTableName;
+        @SuppressWarnings("unchecked")
+        List<Pair<String, String>> columns = queryAgainstTenantView ? Lists.newArrayList(new Pair<String, String>(null, "PARENT_ID"), new Pair<String, String>(null, "CREATED_DATE"), new Pair<String, String>(null, "ENTITY_HISTORY_ID")) : Lists.newArrayList(new Pair<String, String>(null, "TENANT_ID"), new Pair<String, String>(null, "PARENT_ID"), new Pair<String, String>(null, "CREATED_DATE"), new Pair<String, String>(null, "ENTITY_HISTORY_ID"));
         StringBuilder sb = new StringBuilder();
-        String where = dataTableMultiTenant ? " WHERE (PARENT_ID, CREATED_DATE, ENTITY_HISTORY_ID) IN " : " WHERE (TENANT_ID, PARENT_ID, CREATED_DATE, ENTITY_HISTORY_ID) IN ";
+        String where = queryAgainstTenantView ? " WHERE (PARENT_ID, CREATED_DATE, ENTITY_HISTORY_ID) IN " : " WHERE (TENANT_ID, PARENT_ID, CREATED_DATE, ENTITY_HISTORY_ID) IN ";
         sb.append("SELECT ENTITY_HISTORY_ID FROM " + tableName +  where);
-        int numPkCols = dataTableMultiTenant ? 3 : 4;
+        int numPkCols = columns.size();
         String query = addRvcInBinds(sb, cursorIds.length, numPkCols);
-        PreparedStatement stmt = tenantConn.prepareStatement(query);
+        PreparedStatement stmt = conn.prepareStatement(query);
         int bindCounter = 1;
         for (int i = 0; i < cursorIds.length; i++) {
-            Connection globalConn = DriverManager.getConnection(getUrl());
-            Object[] pkParts = PhoenixRuntime.decodePK(globalConn, dataTableName, Base64.decode(cursorIds[i]));
-            globalConn.close();
-            //start at index 1 to  ignore organizationId.
-            int offset = dataTableMultiTenant ? 1 : 0;
-            for (int j = offset; j < pkParts.length; j++) {
+            Object[] pkParts = PhoenixRuntime.decodeValues(conn, tableName, Base64.decode(cursorIds[i]), columns);
+            for (int j = 0; j < pkParts.length; j++) {
                 stmt.setObject(bindCounter++, pkParts[j]);
             }
         }
@@ -281,5 +315,4 @@ public class QueryMoreIT extends BaseHBaseManagedTimeIT {
         sb.append(")");
         return sb.toString();
     }
-    
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/878570d0/phoenix-core/src/it/java/org/apache/phoenix/end2end/StddevIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/StddevIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/StddevIT.java
index b4384f6..f3fef4b 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/StddevIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/StddevIT.java
@@ -17,7 +17,6 @@
  */
 package org.apache.phoenix.end2end;
 
-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;
@@ -28,27 +27,20 @@ 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.apache.phoenix.util.PropertiesUtil;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
-@Category(ClientManagedTimeTest.class)
-public class StddevIT extends BaseClientManagedTimeIT {
+@Category(HBaseManagedTimeTest.class)
+public class StddevIT extends BaseHBaseManagedTimeIT {
 
     @Test
     public void testSTDDEV_POP() throws Exception {
-        long ts = nextTimestamp();
         String tenantId = getOrganizationId();
-        initATableValues(tenantId, getDefaultSplits(tenantId), null, ts);
+        initATableValues(tenantId, getDefaultSplits(tenantId), getUrl());
 
         String query = "SELECT STDDEV_POP(A_INTEGER) FROM aTable";
 
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 2)); // Execute at
-                                                                                     // timestamp 2
-        Connection conn = DriverManager.getConnection(getUrl(), props);
+        Connection conn = DriverManager.getConnection(getUrl());
         try {
             PreparedStatement statement = conn.prepareStatement(query);
             ResultSet rs = statement.executeQuery();
@@ -64,16 +56,12 @@ public class StddevIT extends BaseClientManagedTimeIT {
     
     @Test
     public void testSTDDEV_SAMP() throws Exception {
-        long ts = nextTimestamp();
         String tenantId = getOrganizationId();
-        initATableValues(tenantId, getDefaultSplits(tenantId), null, ts);
+        initATableValues(tenantId, getDefaultSplits(tenantId), getUrl());
 
         String query = "SELECT STDDEV_SAMP(x_decimal) FROM aTable";
 
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 2)); // Execute at
-                                                                                     // timestamp 2
-        Connection conn = DriverManager.getConnection(getUrl(), props);
+        Connection conn = DriverManager.getConnection(getUrl());
         try {
             PreparedStatement statement = conn.prepareStatement(query);
             ResultSet rs = statement.executeQuery();
@@ -89,16 +77,12 @@ public class StddevIT extends BaseClientManagedTimeIT {
 
     @Test
     public void testSTDDEV_POPOnDecimalColType() throws Exception {
-        long ts = nextTimestamp();
         String tenantId = getOrganizationId();
-        initATableValues(tenantId, getDefaultSplits(tenantId), null, ts);
+        initATableValues(tenantId, getDefaultSplits(tenantId), getUrl());
 
         String query = "SELECT STDDEV_POP(x_decimal) FROM aTable";
 
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 2)); // Execute at
-                                                                                     // timestamp 2
-        Connection conn = DriverManager.getConnection(getUrl(), props);
+        Connection conn = DriverManager.getConnection(getUrl());
         try {
             PreparedStatement statement = conn.prepareStatement(query);
             ResultSet rs = statement.executeQuery();
@@ -114,16 +98,12 @@ public class StddevIT extends BaseClientManagedTimeIT {
 
     @Test
     public void testSTDDEV_SAMPOnDecimalColType() throws Exception {
-        long ts = nextTimestamp();
         String tenantId = getOrganizationId();
-        initATableValues(tenantId, getDefaultSplits(tenantId), null, ts);
+        initATableValues(tenantId, getDefaultSplits(tenantId), getUrl());
 
         String query = "SELECT STDDEV_SAMP(x_decimal) FROM aTable";
 
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 2)); // Execute at
-                                                                                     // timestamp 2
-        Connection conn = DriverManager.getConnection(getUrl(), props);
+        Connection conn = DriverManager.getConnection(getUrl());
         try {
             PreparedStatement statement = conn.prepareStatement(query);
             ResultSet rs = statement.executeQuery();

http://git-wip-us.apache.org/repos/asf/phoenix/blob/878570d0/phoenix-core/src/main/java/org/apache/phoenix/schema/KeyValueSchema.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/KeyValueSchema.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/KeyValueSchema.java
index b668f5f..d6c36c0 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/KeyValueSchema.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/KeyValueSchema.java
@@ -22,7 +22,6 @@ import java.util.List;
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.hadoop.io.WritableUtils;
 import org.apache.http.annotation.Immutable;
-
 import org.apache.phoenix.expression.Expression;
 import org.apache.phoenix.schema.tuple.Tuple;
 import org.apache.phoenix.util.ByteUtil;
@@ -65,7 +64,7 @@ public class KeyValueSchema extends ValueSchema {
         }
         
         public KeyValueSchemaBuilder addField(PDatum datum) {
-            super.addField(datum, fields.size() <  this.minNullable, SortOrder.getDefault());
+            super.addField(datum, fields.size() >=  this.minNullable, SortOrder.getDefault());
             return this;
         }
     }
@@ -107,7 +106,7 @@ public class KeyValueSchema extends ValueSchema {
             Field field = fields.get(i);
             PDataType type = field.getDataType();
             for (int j = 0; j < field.getCount(); j++) {
-                if (expressions[index].evaluate(tuple, ptr)) { // Skip null values
+                if (expressions[index].evaluate(tuple, ptr) && ptr.getLength() > 0) { // Skip null values
                     if (index >= minNullableIndex) {
                         valueSet.set(index - minNullableIndex);
                     }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/878570d0/phoenix-core/src/main/java/org/apache/phoenix/schema/PDataType.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/PDataType.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/PDataType.java
index 614eb6a..fa588b8 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/PDataType.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/PDataType.java
@@ -3402,7 +3402,7 @@ public enum PDataType {
             return VARBINARY.getSampleValue(maxLength, arrayLength);
         }
     },
-    INTEGER_ARRAY("INTEGER_ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.INTEGER.getSqlType(), PhoenixArray.class, null) {
+    INTEGER_ARRAY("INTEGER ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.INTEGER.getSqlType(), PhoenixArray.class, null) {
     	@Override
     	public boolean isArrayType() {
     		return true;
@@ -3491,7 +3491,7 @@ public enum PDataType {
         }
 		
 	},
-    BOOLEAN_ARRAY("BOOLEAN_ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.BOOLEAN.getSqlType(), PhoenixArray.class, null) {
+	BOOLEAN_ARRAY("BOOLEAN ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.BOOLEAN.getSqlType(), PhoenixArray.class, null) {
     	@Override
     	public boolean isArrayType() {
     		return true;
@@ -3579,7 +3579,7 @@ public enum PDataType {
         }
 		
 	},
-	VARCHAR_ARRAY("VARCHAR_ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.VARCHAR.getSqlType(), PhoenixArray.class, null) {
+	VARCHAR_ARRAY("VARCHAR ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.VARCHAR.getSqlType(), PhoenixArray.class, null) {
 		@Override
     	public boolean isArrayType() {
     		return true;
@@ -3673,7 +3673,7 @@ public enum PDataType {
         }
 		
 	},
-	VARBINARY_ARRAY("VARBINARY_ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.VARBINARY.getSqlType(), PhoenixArray.class, null) {
+	VARBINARY_ARRAY("VARBINARY ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.VARBINARY.getSqlType(), PhoenixArray.class, null) {
 		@Override
     	public boolean isArrayType() {
     		return true;
@@ -3767,7 +3767,7 @@ public enum PDataType {
             return pDataTypeForArray.getSampleValue(PDataType.VARBINARY, arrayLength, maxLength);
         }
 	},
-	BINARY_ARRAY("BINARY_ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.BINARY.getSqlType(), PhoenixArray.class, null) {
+	BINARY_ARRAY("BINARY ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.BINARY.getSqlType(), PhoenixArray.class, null) {
 		@Override
     	public boolean isArrayType() {
     		return true;
@@ -3861,7 +3861,7 @@ public enum PDataType {
             return pDataTypeForArray.getSampleValue(PDataType.BINARY, arrayLength, maxLength);
         }
     },
-	CHAR_ARRAY("CHAR_ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.CHAR.getSqlType(), PhoenixArray.class, null) {
+    CHAR_ARRAY("CHAR ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.CHAR.getSqlType(), PhoenixArray.class, null) {
 		@Override
     	public boolean isArrayType() {
     		return true;
@@ -3956,7 +3956,7 @@ public enum PDataType {
         }
 		
 	},
-	LONG_ARRAY("LONG_ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.LONG.getSqlType(), PhoenixArray.class, null) {
+	LONG_ARRAY("BIGINT ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.LONG.getSqlType(), PhoenixArray.class, null) {
 		@Override
     	public boolean isArrayType() {
     		return true;
@@ -4043,7 +4043,7 @@ public enum PDataType {
         }
 		
 	},
-	SMALLINT_ARRAY("SMALLINT_ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.SMALLINT.getSqlType(), PhoenixArray.class, null) {
+	SMALLINT_ARRAY("SMALLINT ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.SMALLINT.getSqlType(), PhoenixArray.class, null) {
 		@Override
     	public boolean isArrayType() {
     		return true;
@@ -4130,7 +4130,7 @@ public enum PDataType {
         }
 		
 	},
-	TINYINT_ARRAY("TINYINT_ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.TINYINT.getSqlType(), PhoenixArray.class, null) {
+	TINYINT_ARRAY("TINYINT ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.TINYINT.getSqlType(), PhoenixArray.class, null) {
 		@Override
     	public boolean isArrayType() {
     		return true;
@@ -4217,7 +4217,7 @@ public enum PDataType {
         }
 		
 	},
-	FLOAT_ARRAY("FLOAT_ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.FLOAT.getSqlType(), PhoenixArray.class, null) {
+	FLOAT_ARRAY("FLOAT ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.FLOAT.getSqlType(), PhoenixArray.class, null) {
 		@Override
     	public boolean isArrayType() {
     		return true;
@@ -4305,7 +4305,7 @@ public enum PDataType {
         }
 		
 	},
-	DOUBLE_ARRAY("DOUBLE_ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.DOUBLE.getSqlType(), PhoenixArray.class, null) {
+	DOUBLE_ARRAY("DOUBLE ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.DOUBLE.getSqlType(), PhoenixArray.class, null) {
 	    final PArrayDataType pDataTypeForArray = new PArrayDataType();
 		@Override
     	public boolean isArrayType() {
@@ -4394,7 +4394,7 @@ public enum PDataType {
 
 	},
 	
-	DECIMAL_ARRAY("DECIMAL_ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.DECIMAL.getSqlType(), PhoenixArray.class, null) {
+	DECIMAL_ARRAY("DECIMAL ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.DECIMAL.getSqlType(), PhoenixArray.class, null) {
 		@Override
     	public boolean isArrayType() {
     		return true;
@@ -4489,8 +4489,7 @@ public enum PDataType {
         }
 	
 	},
-	TIMESTAMP_ARRAY("TIMESTAMP_ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.TIMESTAMP.getSqlType(), PhoenixArray.class,
-			null) {
+	TIMESTAMP_ARRAY("TIMESTAMP ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.TIMESTAMP.getSqlType(), PhoenixArray.class, null) {
 		@Override
     	public boolean isArrayType() {
     		return true;
@@ -4577,8 +4576,7 @@ public enum PDataType {
         }
 
 	},
-	UNSIGNED_TIMESTAMP_ARRAY("UNSIGNED_TIMESTAMP_ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.UNSIGNED_TIMESTAMP.getSqlType(), PhoenixArray.class,
-			null) {
+	UNSIGNED_TIMESTAMP_ARRAY("UNSIGNED_TIMESTAMP ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.UNSIGNED_TIMESTAMP.getSqlType(), PhoenixArray.class, null) {
 		@Override
     	public boolean isArrayType() {
     		return true;
@@ -4665,7 +4663,7 @@ public enum PDataType {
         }
 
 	},
-	TIME_ARRAY("TIME_ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.TIME.getSqlType(), PhoenixArray.class, null) {
+	TIME_ARRAY("TIME ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.TIME.getSqlType(), PhoenixArray.class, null) {
 		@Override
     	public boolean isArrayType() {
     		return true;
@@ -4752,7 +4750,7 @@ public enum PDataType {
         }
 
 	},
-	UNSIGNED_TIME_ARRAY("UNSIGNED_TIME_ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.UNSIGNED_TIME.getSqlType(), PhoenixArray.class, null) {
+	UNSIGNED_TIME_ARRAY("UNSIGNED_TIME ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.UNSIGNED_TIME.getSqlType(), PhoenixArray.class, null) {
 		@Override
     	public boolean isArrayType() {
     		return true;
@@ -4839,7 +4837,7 @@ public enum PDataType {
         }
 
 	},
-	DATE_ARRAY("DATE_ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.DATE.getSqlType(), PhoenixArray.class, null) {
+	DATE_ARRAY("DATE ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.DATE.getSqlType(), PhoenixArray.class, null) {
 		@Override
     	public boolean isArrayType() {
     		return true;
@@ -4926,7 +4924,7 @@ public enum PDataType {
         }
 
 	},
-	UNSIGNED_DATE_ARRAY("UNSIGNED_DATE_ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.UNSIGNED_DATE.getSqlType(), PhoenixArray.class, null) {
+	UNSIGNED_DATE_ARRAY("UNSIGNED_DATE ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.UNSIGNED_DATE.getSqlType(), PhoenixArray.class, null) {
 		@Override
     	public boolean isArrayType() {
     		return true;
@@ -5013,7 +5011,7 @@ public enum PDataType {
         }
 
 	},
-	UNSIGNED_LONG_ARRAY("UNSIGNED_LONG_ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.UNSIGNED_LONG.getSqlType(), PhoenixArray.class, null) {
+	UNSIGNED_LONG_ARRAY("UNSIGNED_LONG ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.UNSIGNED_LONG.getSqlType(), PhoenixArray.class, null) {
 		@Override
     	public boolean isArrayType() {
     		return true;
@@ -5100,7 +5098,7 @@ public enum PDataType {
         }
 	
 	},
-	UNSIGNED_INT_ARRAY("UNSIGNED_INT_ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.UNSIGNED_INT.getSqlType(), PhoenixArray.class, null) {
+	UNSIGNED_INT_ARRAY("UNSIGNED_INT ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.UNSIGNED_INT.getSqlType(), PhoenixArray.class, null) {
 		@Override
     	public boolean isArrayType() {
     		return true;
@@ -5187,8 +5185,7 @@ public enum PDataType {
         }
 
 	},
-	UNSIGNED_SMALLINT_ARRAY("UNSIGNED_SMALLINT_ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.UNSIGNED_SMALLINT.getSqlType(),
-			PhoenixArray.class, null) {
+	UNSIGNED_SMALLINT_ARRAY("UNSIGNED_SMALLINT ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.UNSIGNED_SMALLINT.getSqlType(), PhoenixArray.class, null) {
 		@Override
     	public boolean isArrayType() {
     		return true;
@@ -5275,8 +5272,7 @@ public enum PDataType {
         }
 
 	},
-	UNSIGNED_TINYINT_ARRAY("UNSIGNED_TINYINT__ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.UNSIGNED_TINYINT.getSqlType(), PhoenixArray.class,
-			null) {
+	UNSIGNED_TINYINT_ARRAY("UNSIGNED_TINYINT ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.UNSIGNED_TINYINT.getSqlType(), PhoenixArray.class, null) {
 		@Override
     	public boolean isArrayType() {
     		return true;
@@ -5362,7 +5358,7 @@ public enum PDataType {
             return pDataTypeForArray.getSampleValue(PDataType.UNSIGNED_TINYINT, arrayLength, maxLength);
         }
 	},
-	UNSIGNED_FLOAT_ARRAY("UNSIGNED_FLOAT_ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.UNSIGNED_FLOAT.getSqlType(), PhoenixArray.class, null) {
+	UNSIGNED_FLOAT_ARRAY("UNSIGNED_FLOAT ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.UNSIGNED_FLOAT.getSqlType(), PhoenixArray.class, null) {
 		@Override
     	public boolean isArrayType() {
     		return true;
@@ -5449,8 +5445,7 @@ public enum PDataType {
         }
 
 	},
-	UNSIGNED_DOUBLE_ARRAY("UNSIGNED_DOUBLE__ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.UNSIGNED_DOUBLE.getSqlType(), PhoenixArray.class,
-			null) {
+	UNSIGNED_DOUBLE_ARRAY("UNSIGNED_DOUBLE ARRAY", PDataType.ARRAY_TYPE_BASE + PDataType.UNSIGNED_DOUBLE.getSqlType(), PhoenixArray.class, null) {
 		@Override
     	public boolean isArrayType() {
     		return true;
@@ -5551,6 +5546,28 @@ public enum PDataType {
     private final PDataCodec codec;
     final PArrayDataType pDataTypeForArray = new PArrayDataType();
     
+    private static final int SQL_TYPE_OFFSET;
+    private static final PDataType[] SQL_TYPE_TO_PCOLUMN_DATA_TYPE;
+    static {
+        int minSqlType = Integer.MAX_VALUE;
+        int maxSqlType = Integer.MIN_VALUE;
+        for (PDataType dataType : PDataType.values()) {
+            int sqlType = dataType.getSqlType();
+            if (sqlType < minSqlType) {
+                minSqlType = sqlType;
+            }
+            if (sqlType > maxSqlType) {
+                maxSqlType = sqlType;
+            }
+        }
+        SQL_TYPE_OFFSET = minSqlType;
+        SQL_TYPE_TO_PCOLUMN_DATA_TYPE = new PDataType[maxSqlType-minSqlType+1];
+        for (PDataType dataType : PDataType.values()) {
+            int sqlType = dataType.getSqlType();
+            SQL_TYPE_TO_PCOLUMN_DATA_TYPE[sqlType-SQL_TYPE_OFFSET] = dataType;
+        }
+    }
+    
     private PDataType(String sqlTypeName, int sqlType, Class clazz, PDataCodec codec) {
         this.sqlTypeName = sqlTypeName;
         this.sqlType = sqlType;
@@ -5559,7 +5576,7 @@ public enum PDataType {
         this.sqlTypeNameBytes = Bytes.toBytes(sqlTypeName);
         this.codec = codec;
     }
-
+    
     public boolean isCastableTo(PDataType targetType) {
         return isComparableTo(targetType);
     }
@@ -6767,6 +6784,7 @@ public enum PDataType {
     public final static Integer BYTE_PRECISION = 3;
 
     public static final int ARRAY_TYPE_BASE = 3000;
+    public static final String ARRAY_TYPE_SUFFIX = "ARRAY";
     
     private static final ThreadLocal<Random> RANDOM = new ThreadLocal<Random>(){
         @Override 
@@ -7232,30 +7250,7 @@ public enum PDataType {
     	return fromSqlTypeName.getSqlType() + PDataType.ARRAY_TYPE_BASE;
     }
 
-    private static final int SQL_TYPE_OFFSET;
-    private static final PDataType[] SQL_TYPE_TO_PCOLUMN_DATA_TYPE;
-    static {
-        int minSqlType = Integer.MAX_VALUE;
-        int maxSqlType = Integer.MIN_VALUE;
-        for (PDataType dataType : PDataType.values()) {
-            int sqlType = dataType.getSqlType();
-            if (sqlType < minSqlType) {
-                minSqlType = sqlType;
-            }
-            if (sqlType > maxSqlType) {
-                maxSqlType = sqlType;
-            }
-        }
-        SQL_TYPE_OFFSET = minSqlType;
-        SQL_TYPE_TO_PCOLUMN_DATA_TYPE = new PDataType[maxSqlType-minSqlType+1];
-        for (PDataType dataType : PDataType.values()) {
-            int sqlType = dataType.getSqlType();
-            SQL_TYPE_TO_PCOLUMN_DATA_TYPE[sqlType-SQL_TYPE_OFFSET] = dataType;
-        }
-    }
-
-         
-	private static interface PhoenixArrayFactory {
+    private static interface PhoenixArrayFactory {
 		PhoenixArray newArray(PDataType type, Object[] elements);
 	}
 
@@ -7402,4 +7397,9 @@ public enum PDataType {
     public void pad(ImmutableBytesWritable ptr, Integer maxLength) {
     }
     
+    public static PDataType arrayBaseType(PDataType arrayType) {
+        Preconditions.checkArgument(arrayType.isArrayType(), "Not a phoenix array type");
+        return fromTypeId(arrayType.getSqlType() - ARRAY_TYPE_BASE);
+    }
+    
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/878570d0/phoenix-core/src/main/java/org/apache/phoenix/util/IndexUtil.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/IndexUtil.java b/phoenix-core/src/main/java/org/apache/phoenix/util/IndexUtil.java
index 58709d8..80b8688 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/util/IndexUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/util/IndexUtil.java
@@ -19,6 +19,7 @@ package org.apache.phoenix.util;
 
 import java.io.IOException;
 import java.sql.SQLException;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
@@ -34,6 +35,7 @@ import org.apache.phoenix.hbase.index.covered.update.ColumnReference;
 import org.apache.phoenix.hbase.index.util.ImmutableBytesPtr;
 import org.apache.phoenix.hbase.index.util.KeyValueBuilder;
 import org.apache.phoenix.index.IndexMaintainer;
+import org.apache.phoenix.jdbc.PhoenixConnection;
 import org.apache.phoenix.query.QueryConstants;
 import org.apache.phoenix.schema.ColumnFamilyNotFoundException;
 import org.apache.phoenix.schema.ColumnNotFoundException;
@@ -209,4 +211,22 @@ public class IndexUtil {
         }
         return false;
     }
+    
+    /**
+     * Return a list of {@code PColumn} for the associated data columns given the corresponding index columns. For a tenant
+     * specific view, the connection needs to be tenant specific too. 
+     * @param dataTableName
+     * @param indexColumns
+     * @param conn
+     * @return
+     * @throws TableNotFoundException if table cannot be found in the connection's metdata cache
+     */
+    public static List<PColumn> getDataColumns(String dataTableName, List<PColumn> indexColumns, PhoenixConnection conn) throws SQLException {
+    	PTable dataTable = PhoenixRuntime.getTable(conn, dataTableName);
+        List<PColumn> dataColumns = new ArrayList<PColumn>(indexColumns.size());
+        for (PColumn indexColumn : indexColumns) {
+            dataColumns.add(getDataColumn(dataTable, indexColumn.getName().getString()));
+        }
+        return dataColumns;
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/878570d0/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 064ca62..36c3ede 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
@@ -17,13 +17,18 @@
  */
 package org.apache.phoenix.util;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.apache.phoenix.schema.PDataType.ARRAY_TYPE_SUFFIX;
+
 import java.io.File;
 import java.io.FileReader;
 import java.io.IOException;
 import java.io.Reader;
 import java.sql.Connection;
 import java.sql.DriverManager;
+import java.sql.PreparedStatement;
 import java.sql.SQLException;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
@@ -31,6 +36,8 @@ import java.util.Properties;
 import java.util.Set;
 import java.util.TreeSet;
 
+import javax.annotation.Nullable;
+
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.CommandLineParser;
 import org.apache.commons.cli.HelpFormatter;
@@ -42,21 +49,31 @@ import org.apache.hadoop.hbase.KeyValue;
 import org.apache.hadoop.hbase.client.Mutation;
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.hadoop.hbase.util.Pair;
+import org.apache.phoenix.compile.QueryPlan;
 import org.apache.phoenix.coprocessor.MetaDataProtocol.MetaDataMutationResult;
 import org.apache.phoenix.coprocessor.MetaDataProtocol.MutationCode;
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.expression.LiteralExpression;
+import org.apache.phoenix.expression.OrderByExpression;
 import org.apache.phoenix.jdbc.PhoenixConnection;
+import org.apache.phoenix.jdbc.PhoenixPreparedStatement;
 import org.apache.phoenix.query.QueryConstants;
 import org.apache.phoenix.schema.AmbiguousColumnException;
 import org.apache.phoenix.schema.ColumnNotFoundException;
+import org.apache.phoenix.schema.KeyValueSchema;
 import org.apache.phoenix.schema.MetaDataClient;
 import org.apache.phoenix.schema.PColumn;
 import org.apache.phoenix.schema.PColumnFamily;
 import org.apache.phoenix.schema.PDataType;
 import org.apache.phoenix.schema.PTable;
 import org.apache.phoenix.schema.PTableKey;
+import org.apache.phoenix.schema.PTableType;
 import org.apache.phoenix.schema.RowKeySchema;
 import org.apache.phoenix.schema.TableNotFoundException;
+import org.apache.phoenix.schema.KeyValueSchema.KeyValueSchemaBuilder;
+import org.apache.phoenix.schema.ValueBitSet;
 
+import com.google.common.base.Preconditions;
 import com.google.common.base.Splitter;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
@@ -640,4 +657,261 @@ public class PhoenixRuntime {
             return strict;
         }
     }
+    
+    /**
+     * Returns the opitmized query plan used by phoenix for executing the sql.
+     * @param stmt to return the plan for
+     * @throws SQLException
+     */
+    public static QueryPlan getOptimizedQueryPlan(PreparedStatement stmt) throws SQLException {
+        checkNotNull(stmt);
+        QueryPlan plan = stmt.unwrap(PhoenixPreparedStatement.class).optimizeQuery();
+        return plan;
+    }
+    
+    /**
+     * Whether or not the query plan has any order by expressions.
+     * @param plan
+     * @return
+     */
+    public static boolean hasOrderBy(QueryPlan plan) {
+        checkNotNull(plan);
+        List<OrderByExpression> orderBys = plan.getOrderBy().getOrderByExpressions();
+        return orderBys != null && !orderBys.isEmpty(); 
+    }
+    
+    public static int getLimit(QueryPlan plan) {
+        checkNotNull(plan);
+        return plan.getLimit() == null ? 0 : plan.getLimit();
+    }
+    
+    private static String addQuotes(String str) {
+        return str == null ? str : "\"" + str + "\"";
+    }
+    /**
+     *
+     * @param columns - Initialized empty list to be filled with the pairs of column family name and column name for columns that are used 
+     * as row key for the query plan. Column family names are optional and hence the first part of the pair is nullable.
+     * Column names and family names are enclosed in double quotes to allow for case sensitivity and for presence of 
+     * special characters. Salting column and view index id column are not included. If the connection is tenant specific 
+     * and the table used by the query plan is multi-tenant, then the tenant id column is not included as well.
+     * @param plan - query plan to get info for.
+     * @param conn - connection used to generate the query plan. Caller should take care of closing the connection appropriately.
+     * @param forDataTable - if true, then family names and column names correspond to the data table even if the query plan uses
+     * the secondary index table. If false, and if the query plan uses the secondary index table, then the family names and column 
+     * names correspond to the index table.
+     * @throws SQLException
+     */
+    public static void getPkColsForSql(List<Pair<String, String>> columns, QueryPlan plan, Connection conn, boolean forDataTable) throws SQLException {
+        checkNotNull(columns);
+        checkNotNull(plan);
+        checkNotNull(conn);
+        List<PColumn> pkColumns = getPkColumns(plan.getTableRef().getTable(), conn, forDataTable);
+        String columnName;
+        String familyName;
+        for (PColumn pCol : pkColumns ) {
+            columnName = addQuotes(pCol.getName().getString());
+            familyName = pCol.getFamilyName() != null ? addQuotes(pCol.getFamilyName().getString()) : null;
+            columns.add(new Pair<String, String>(familyName, columnName));
+        }
+    }
+
+    /**
+     * @param columns - Initialized empty list to be filled with the pairs of column family name and column name for columns that are used 
+     * as row key for the query plan. Column family names are optional and hence the first part of the pair is nullable.
+     * Column names and family names are enclosed in double quotes to allow for case sensitivity and for presence of 
+     * special characters. Salting column and view index id column are not included. If the connection is tenant specific 
+     * and the table used by the query plan is multi-tenant, then the tenant id column is not included as well.
+     * @param datatypes - Initialized empty list to be filled with the corresponding data type for the columns in @param columns. 
+     * @param plan - query plan to get info for
+     * @param conn - phoenix connection used to generate the query plan. Caller should take care of closing the connection appropriately.
+     * @param forDataTable - if true, then column names and data types correspond to the data table even if the query plan uses
+     * the secondary index table. If false, and if the query plan uses the secondary index table, then the column names and data 
+     * types correspond to the index table.
+     * @throws SQLException
+     */
+    public static void getPkColsDataTypesForSql(List<Pair<String, String>> columns, List<String> dataTypes, QueryPlan plan, Connection conn, boolean forDataTable) throws SQLException {
+        checkNotNull(columns);
+        checkNotNull(dataTypes);
+        checkNotNull(plan);
+        checkNotNull(conn);
+        List<PColumn> pkColumns = getPkColumns(plan.getTableRef().getTable(), conn, forDataTable);
+        String columnName;
+        String familyName;
+        for (PColumn pCol : pkColumns) {
+            String sqlTypeName = getSqlTypeName(pCol);
+            dataTypes.add(sqlTypeName);
+            columnName = addQuotes(pCol.getName().getString());
+            familyName = pCol.getFamilyName() != null ? addQuotes(pCol.getFamilyName().getString()) : null;
+            columns.add(new Pair<String, String>(familyName, columnName));
+        }
+    }
+    
+    /**
+     * 
+     * @param pCol
+     * @return sql type name that could be used in DDL statements, dynamic column types etc. 
+     */
+    public static String getSqlTypeName(PColumn pCol) {
+        PDataType dataType = pCol.getDataType();
+        Integer maxLength = pCol.getMaxLength();
+        Integer scale = pCol.getScale();
+        return dataType.isArrayType() ? getArraySqlTypeName(maxLength, scale, dataType) : appendMaxLengthAndScale(maxLength, scale, dataType.getSqlTypeName());
+    }
+    
+    public static String getArraySqlTypeName(@Nullable Integer maxLength, @Nullable Integer scale, PDataType arrayType) {
+        String baseTypeSqlName = PDataType.arrayBaseType(arrayType).getSqlTypeName();
+        return appendMaxLengthAndScale(maxLength, scale, baseTypeSqlName) + " " + ARRAY_TYPE_SUFFIX; // for ex - decimal(10,2) ARRAY
+    }
+
+    private static String appendMaxLengthAndScale(@Nullable Integer maxLength, @Nullable Integer scale, String sqlTypeName) {
+        if (maxLength != null) {
+             sqlTypeName = sqlTypeName + "(" + maxLength;
+             if (scale != null) {
+               sqlTypeName = sqlTypeName + "," + scale; // has both max length and scale. For ex- decimal(10,2)
+             }       
+             sqlTypeName = sqlTypeName + ")";
+        }
+        return sqlTypeName;
+    }
+    
+    private static List<PColumn> getPkColumns(PTable ptable, Connection conn, boolean forDataTable) throws SQLException {
+        PhoenixConnection pConn = conn.unwrap(PhoenixConnection.class);
+        List<PColumn> pkColumns = ptable.getPKColumns();
+        
+        // Skip the salting column and the view index id column if present.
+        // Skip the tenant id column too if the connection is tenant specific and the table used by the query plan is multi-tenant
+        int offset = (ptable.getBucketNum() == null ? 0 : 1) + (ptable.isMultiTenant() && pConn.getTenantId() != null ? 1 : 0) + (ptable.getViewIndexId() == null ? 0 : 1);
+        
+        // get a sublist of pkColumns by skipping the offset columns.
+        pkColumns = pkColumns.subList(offset, pkColumns.size());
+        
+        if (ptable.getType() == PTableType.INDEX && forDataTable) {
+            // index tables have the same schema name as their parent/data tables.
+            String fullDataTableName = ptable.getParentName().getString();
+            
+            // Get the corresponding columns of the data table.
+            List<PColumn> dataColumns = IndexUtil.getDataColumns(fullDataTableName, pkColumns, pConn);
+            pkColumns = dataColumns;
+        }
+        return pkColumns;
+    }
+    
+    /**
+     * 
+     * @param conn connection that was used for reading/generating value.
+     * @param fullTableName fully qualified table name
+     * @param values values of the columns
+     * @param columns list of pair of column that includes column family as first part and column name as the second part.
+     * Column family is optional and hence nullable. Columns in the list have to be in the same order as the order of occurence
+     * of their values in the object array.
+     * @return values encoded in a byte array 
+     * @throws SQLException
+     * @see {@link #decodeValues(Connection, String, byte[], List)}
+     */
+    public static byte[] encodeValues(Connection conn, String fullTableName, Object[] values, List<Pair<String, String>> columns) throws SQLException {
+        PTable table = getTable(conn, fullTableName);
+        List<PColumn> pColumns = getPColumns(table, columns);
+        List<Expression> expressions = new ArrayList<Expression>(pColumns.size());
+        int i = 0;
+        for (PColumn col : pColumns) {
+            Object value = values[i];
+            // for purposes of encoding, sort order of the columns doesn't matter.
+            Expression expr = LiteralExpression.newConstant(value, col.getDataType(), col.getMaxLength(), col.getScale());
+            expressions.add(expr);
+            i++;
+        }
+        KeyValueSchema kvSchema = buildKeyValueSchema(pColumns);
+        ImmutableBytesWritable ptr = new ImmutableBytesWritable();
+        ValueBitSet valueSet = ValueBitSet.newInstance(kvSchema);
+        return kvSchema.toBytes(expressions.toArray(new Expression[0]), valueSet, ptr);
+    }
+    
+    
+    /**
+     * 
+     * @param conn connection that was used for reading/generating value.
+     * @param fullTableName fully qualified table name
+     * @param value byte value of the columns concatenated as a single byte array. @see {@link #encodeValues(Connection, String, Object[], List)}
+     * @param columns list of column names for the columns that have their respective values
+     * present in the byte array. The column names should be in the same order as their values are in the byte array.
+     * The column name includes both family name, if present, and column name.
+     * @return decoded values for each column
+     * @throws SQLException
+     * 
+     */
+    public static Object[] decodeValues(Connection conn, String fullTableName, byte[] value, List<Pair<String, String>> columns) throws SQLException {
+        PTable table = getTable(conn, fullTableName);
+        KeyValueSchema kvSchema = buildKeyValueSchema(getPColumns(table, columns));
+        ImmutableBytesWritable ptr = new ImmutableBytesWritable(value);
+        ValueBitSet valueSet = ValueBitSet.newInstance(kvSchema);
+        valueSet.clear();
+        valueSet.or(ptr);
+        int maxOffset = ptr.getOffset() + ptr.getLength();
+        Boolean hasValue;
+        kvSchema.iterator(ptr);
+        int i = 0;
+        List<Object> values = new ArrayList<Object>();
+        while(hasValue = kvSchema.next(ptr, i, maxOffset, valueSet) != null) {
+            if(hasValue) {
+                values.add(kvSchema.getField(i).getDataType().toObject(ptr));
+            }
+            i++;
+        }
+        return values.toArray();
+    }
+    
+    private static KeyValueSchema buildKeyValueSchema(List<PColumn> columns) {
+        KeyValueSchemaBuilder builder = new KeyValueSchemaBuilder(getMinNullableIndex(columns));
+        for (PColumn col : columns) {
+            builder.addField(col);
+        }
+        return builder.build();
+    }
+    
+    private static int getMinNullableIndex(List<PColumn> columns) {
+        int minNullableIndex = columns.size();
+        for (int i = 0; i < columns.size(); i++) {
+            if (columns.get(i).isNullable()) {
+                minNullableIndex = i;
+                break;
+            }
+         }
+        return minNullableIndex;
+    }
+    
+   /**
+     * @param table table to get the {@code PColumn} for
+     * @param columns list of pair of column that includes column family as first part and column name as the second part.
+     * Column family is optional and hence nullable. 
+     * @return list of {@code PColumn} for fullyQualifiedColumnNames
+     * @throws SQLException 
+     */
+    private static List<PColumn> getPColumns(PTable table, List<Pair<String, String>> columns) throws SQLException {
+        List<PColumn> pColumns = new ArrayList<PColumn>(columns.size());
+        for (Pair<String, String> column : columns) {
+            pColumns.add(getPColumn(table, column.getFirst(), column.getSecond()));
+        }
+        return pColumns;
+    }
+    
+    private static PColumn getPColumn(PTable table, @Nullable String familyName, String columnName) throws SQLException {
+        if (table==null) {
+            throw new SQLException("Table must not be null.");
+        }
+        if (columnName==null) {
+            throw new SQLException("columnName must not be null.");
+        }
+        // normalize and remove quotes from family and column names before looking up.
+        familyName = SchemaUtil.normalizeIdentifier(familyName);
+        columnName = SchemaUtil.normalizeIdentifier(columnName);
+        PColumn pColumn = null;
+        if (familyName != null) {
+            PColumnFamily family = table.getColumnFamily(familyName);
+            pColumn = family.getColumn(columnName);
+        } else {
+            pColumn = table.getColumn(columnName);
+        }
+        return pColumn;
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/878570d0/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 4fc78df..e5be16a 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
@@ -17,6 +17,8 @@
  */
 package org.apache.phoenix.util;
 
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SYSTEM_CATALOG_NAME_BYTES;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SYSTEM_STATS_NAME_BYTES;
 
@@ -28,6 +30,8 @@ import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Properties;
 
+import javax.annotation.Nullable;
+
 import org.apache.hadoop.hbase.io.encoding.DataBlockEncoding;
 import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.phoenix.exception.SQLExceptionCode;
@@ -54,6 +58,7 @@ import org.apache.phoenix.schema.SortOrder;
 import org.apache.phoenix.schema.ValueSchema.Field;
 
 import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
 
 
 
@@ -611,4 +616,26 @@ public class SchemaUtil {
         Preconditions.checkNotNull(argument,"Argument passed cannot be null");
         return ESCAPE_CHARACTER + argument + ESCAPE_CHARACTER;
     }
+    
+    /**
+     * 
+     * @return a fully qualified column name in the format: "CFNAME"."COLNAME" or "COLNAME" depending on whether or not
+     * there is a column family name present. 
+     */
+    public static String getQuotedFullColumnName(PColumn pCol) {
+        checkNotNull(pCol);
+        String columnName = pCol.getName().getString();
+        String columnFamilyName = pCol.getFamilyName() != null ? pCol.getFamilyName().getString() : null;
+        return getQuotedFullColumnName(columnFamilyName, columnName);
+    }
+    
+    /**
+     * 
+     * @return a fully qualified column name in the format: "CFNAME"."COLNAME" or "COLNAME" depending on whether or not
+     * there is a column family name present. 
+     */
+    public static String getQuotedFullColumnName(@Nullable String columnFamilyName, String columnName) {
+        checkArgument(!Strings.isNullOrEmpty(columnName), "Column name cannot be null or empty");
+        return columnFamilyName == null ? ("\"" + columnName + "\"") : ("\"" + columnFamilyName + "\"" + QueryConstants.NAME_SEPARATOR + "\"" + columnName + "\"");
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/878570d0/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryOptimizerTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryOptimizerTest.java b/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryOptimizerTest.java
index 45a61a7..81f4a45 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryOptimizerTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryOptimizerTest.java
@@ -20,15 +20,31 @@ package org.apache.phoenix.compile;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 
+import java.sql.Array;
 import java.sql.Connection;
+import java.sql.Date;
 import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
 
+import org.apache.hadoop.hbase.util.Pair;
 import org.apache.phoenix.compile.OrderByCompiler.OrderBy;
+import org.apache.phoenix.jdbc.PhoenixPreparedStatement;
 import org.apache.phoenix.jdbc.PhoenixStatement;
 import org.apache.phoenix.query.BaseConnectionlessQueryTest;
+import org.apache.phoenix.query.QueryConstants;
+import org.apache.phoenix.schema.PTableType;
+import org.apache.phoenix.util.PhoenixRuntime;
 import org.apache.phoenix.util.SchemaUtil;
+import org.junit.Ignore;
 import org.junit.Test;
 
+import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
+
 public class QueryOptimizerTest extends BaseConnectionlessQueryTest {
     
     public static final String SCHEMA_NAME = "";
@@ -306,4 +322,252 @@ public class QueryOptimizerTest extends BaseConnectionlessQueryTest {
         QueryPlan plan = stmt.optimizeQuery(query);
         assertEquals("T", plan.getTableRef().getTable().getTableName().getString());
     }
+    
+    @Test
+    // Multi-tenant = false; Query uses index = false; Salted = true
+    public void testAssertQueryPlanDetails1() throws Exception {
+        testAssertQueryPlanDetails(false, false, true);
+    }
+    
+    @Test
+    // Multi-tenant = true; Query uses index = false; Salted = true
+    public void testAssertQueryPlanDetails2() throws Exception {
+        testAssertQueryPlanDetails(true, false, true);
+    }
+    
+    @Test
+    @Ignore // FIXME : https://issues.apache.org/jira/browse/PHOENIX-1302
+    // Multi-tenant = true; Query uses index = true; Salted = false
+    public void testAssertQueryPlanDetails3() throws Exception {
+        testAssertQueryPlanDetails(true, true, true);
+    }
+    
+    @Test
+    // Multi-tenant = false; Query uses index = true; Salted = true
+    public void testAssertQueryPlanDetails4() throws Exception {
+        testAssertQueryPlanDetails(false, true, true);
+    }
+    
+    @Test
+    // Multi-tenant = false; Query uses index = false; Salted = false
+    public void testAssertQueryPlanDetails5() throws Exception {
+        testAssertQueryPlanDetails(false, false, false);
+    }
+    
+    @Test
+    // Multi-tenant = true; Query uses index = false; Salted = false
+    public void testAssertQueryPlanDetails6() throws Exception {
+        testAssertQueryPlanDetails(true, false, false);
+    }
+    
+    @Test
+    @Ignore // FIXME : https://issues.apache.org/jira/browse/PHOENIX-1302
+    // Multi-tenant = true; Query uses index = true; Salted = false
+    public void testAssertQueryPlanDetails7() throws Exception {
+        testAssertQueryPlanDetails(true, true, false);
+    }
+    
+    @Test
+    // Multi-tenant = false; Query uses index = true; Salted = false
+    public void testAssertQueryPlanDetails8() throws Exception {
+        testAssertQueryPlanDetails(false, true, false);
+    }
+
+    private void testAssertQueryPlanDetails(boolean multitenant, boolean useIndex, boolean salted) throws Exception {
+        String sql;
+        PreparedStatement stmt;
+        Connection conn = DriverManager.getConnection(getUrl(), new Properties());
+        try {
+            // create table
+            conn.createStatement().execute("create table "
+                    + "XYZ.ABC"
+                    + "   (organization_id char(15) not null, \n"
+                    + "    dec DECIMAL(10,2) not null,\n"
+                    + "    a_string_array varchar(100) array[] not null,\n"
+                    + "    b_string varchar(100),\n"
+                    + "    CF.a_integer integer,\n"
+                    + "    a_date date,\n"
+                    + "    CONSTRAINT pk PRIMARY KEY (organization_id, dec, a_string_array)\n"
+                    + ")" + (salted ? "SALT_BUCKETS=4" : "") + (multitenant == true ? (salted ? ",MULTI_TENANT=true" : "MULTI_TENANT=true") : ""));
+
+            
+            if (useIndex) {
+                // create index
+                conn.createStatement().execute("CREATE INDEX ABC_IDX ON XYZ.ABC (CF.a_integer) INCLUDE (a_date)");
+            }
+            
+            // switch to a tenant specific connection if multi-tenant.
+            conn = multitenant ? DriverManager.getConnection(getUrl("tenantId")) : conn;
+            
+            // create a tenant specific view if multi-tenant
+            if (multitenant) {
+                conn.createStatement().execute("CREATE VIEW ABC_VIEW (ORGANIZATION_ID VARCHAR) AS SELECT * FROM XYZ.ABC");
+            }
+            
+            String expectedColNames = multitenant ? addQuotes(null, "DEC,A_STRING_ARRAY") : addQuotes(null,"ORGANIZATION_ID,DEC,A_STRING_ARRAY");
+            String expectedColumnNameDataTypes = multitenant ? "\"DEC\" DECIMAL(10,2),\"A_STRING_ARRAY\" VARCHAR(100) ARRAY" : "\"ORGANIZATION_ID\" CHAR(15),\"DEC\" DECIMAL(10,2),\"A_STRING_ARRAY\" VARCHAR(100) ARRAY";
+            String tableName = multitenant ? "ABC_VIEW" : "XYZ.ABC";
+            String tenantFilter = multitenant ? "" : "organization_id = ? AND ";
+            String orderByRowKeyClause = multitenant ? "dec" : "organization_id";
+            
+            // Filter on row key columns of data table. No order by. No limit.
+            sql = "SELECT CF.a_integer FROM " + tableName + " where " + tenantFilter + " dec = ? and a_string_array = ?";
+            stmt = conn.prepareStatement(sql);
+            int counter = 1;
+            if (!multitenant) {
+                stmt.setString(counter++, "ORGID");
+            }
+            stmt.setDouble(counter++, 1.23);
+            String[] strArray = new String[2];
+            strArray[0] = "AB";
+            strArray[1] = "CD";
+            Array array = conn.createArrayOf("VARCHAR", strArray);
+            stmt.setArray(counter++, array);
+            assertPlanDetails(stmt, expectedColNames, expectedColumnNameDataTypes, false, 0);
+            
+            counter = 1;
+            // Filter on row key columns of data table. Order by row key columns. Limit specified.
+            sql = "SELECT CF.a_integer FROM " + tableName + " where " + tenantFilter + " dec = ? and a_string_array = ? ORDER BY " + orderByRowKeyClause + " LIMIT 100";
+            stmt = conn.prepareStatement(sql);
+            if (!multitenant) {
+                stmt.setString(counter++, "ORGID");
+            }
+            stmt.setDouble(counter++, 1.23);
+            array = conn.createArrayOf("VARCHAR", strArray);
+            stmt.setArray(counter++, array);
+            assertPlanDetails(stmt, expectedColNames, expectedColumnNameDataTypes, false, 100);
+            
+            counter = 1;
+            // Filter on row key columns of data table. Order by non-row key columns. Limit specified.
+            sql = "SELECT CF.a_integer FROM " + tableName + " where " + tenantFilter + " dec = ? and a_string_array = ? ORDER BY a_date LIMIT 100";
+            stmt = conn.prepareStatement(sql);
+            if (!multitenant) {
+                stmt.setString(counter++, "ORGID");
+            }
+            stmt.setDouble(counter++, 1.23);
+            array = conn.createArrayOf("VARCHAR", strArray);
+            stmt.setArray(counter++, array);
+            assertPlanDetails(stmt, expectedColNames, expectedColumnNameDataTypes, true, 100);
+            
+            if (useIndex) {
+                
+                expectedColNames = multitenant ? ("\"CF\".\"A_INTEGER\"" + ",\"DEC\"" + ",\"A_STRING_ARRAY\"") : ("\"CF\".\"A_INTEGER\"" + ",\"ORGANIZATION_ID\"" + ",\"DEC\"" + ",\"A_STRING_ARRAY\"");
+                expectedColumnNameDataTypes = multitenant ? ("\"CF\".\"A_INTEGER\"" + " " + "INTEGER" + ",\"DEC\"" + " " + "DECIMAL(10,2)" + ",\"A_STRING_ARRAY\""+ " " + "VARCHAR(100) ARRAY") : ("\"CF\".\"A_INTEGER\"" + " " + "INTEGER" + ",\"ORGANIZATION_ID\"" + " " + "CHAR(15)" + ",\"DEC\"" + " " + "DECIMAL(10,2)" + ",\"A_STRING_ARRAY\""+ " " + "VARCHAR(100) ARRAY");
+                
+                // Filter on columns that the secondary index is on. No order by. No limit.
+                sql = "SELECT a_date FROM " + tableName + " where CF.a_integer = ?";
+                stmt = conn.prepareStatement(sql);
+                stmt.setInt(1, 1000);
+                assertPlanDetails(stmt, expectedColNames, expectedColumnNameDataTypes, false, 0);
+
+                // Filter on columns that the secondary index is on. Order by on the indexed column. Limit specified.
+                sql = "SELECT a_date FROM " + tableName + " where CF.a_integer = ? ORDER BY CF.a_integer LIMIT 100";
+                stmt = conn.prepareStatement(sql);
+                stmt.setInt(1, 1000);
+                assertPlanDetails(stmt, expectedColNames, expectedColumnNameDataTypes, false, 100);
+
+                // Filter on columns that the secondary index is on. Order by on the non-indexed column. Limit specified.
+                sql = "SELECT a_integer FROM " + tableName + " where CF.a_integer = ? and a_date = ? ORDER BY a_date LIMIT 100";
+                stmt = conn.prepareStatement(sql);
+                stmt.setInt(1, 1000);
+                stmt.setDate(2, new Date(909000));
+                assertPlanDetails(stmt, expectedColNames, expectedColumnNameDataTypes, true, 100);
+            }
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    @Ignore // FIXME : https://issues.apache.org/jira/browse/PHOENIX-1302
+    public void testAssertQueryAgainstTenantSpecificViewGoesThroughIndex() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl(), new Properties());
+        
+        // create table
+        conn.createStatement().execute("create table "
+                + "XYZ.ABC"
+                + "   (organization_id char(15) not null, \n"
+                + "    entity_id char(15) not null,\n"
+                + "    a_string_array varchar(100) array[] not null,\n"
+                + "    b_string varchar(100),\n"
+                + "    a_string varchar,\n"
+                + "    a_date date,\n"
+                + "    CONSTRAINT pk PRIMARY KEY (organization_id, entity_id, a_string_array)\n"
+                + ")" + "MULTI_TENANT=true");
+
+        
+        // create index
+        conn.createStatement().execute("CREATE INDEX ABC_IDX ON XYZ.ABC (a_string) INCLUDE (a_date)");
+        
+        conn.close();
+        
+        // switch to a tenant specific connection
+        conn = DriverManager.getConnection(getUrl("tenantId"));
+        
+        // create a tenant specific view
+        conn.createStatement().execute("CREATE VIEW ABC_VIEW AS SELECT * FROM XYZ.ABC");
+        
+        // query against the tenant specific view
+        String sql = "SELECT a_date FROM ABC_VIEW where a_string = ?";
+        PreparedStatement stmt = conn.prepareStatement(sql);
+        stmt.setString(1, "1000");
+        QueryPlan plan = stmt.unwrap(PhoenixPreparedStatement.class).optimizeQuery();
+        assertEquals("Query should use index", PTableType.INDEX, plan.getTableRef().getTable().getType());
+        
+    }
+    
+    private void assertPlanDetails(PreparedStatement stmt, String expectedPkCols, String expectedPkColsDataTypes, boolean expectedHasOrderBy, int expectedLimit) throws SQLException {
+        Connection conn = stmt.getConnection();
+        QueryPlan plan = PhoenixRuntime.getOptimizedQueryPlan(stmt);
+        
+        List<Pair<String, String>> columns = new ArrayList<Pair<String, String>>();
+        PhoenixRuntime.getPkColsForSql(columns, plan, conn, true);
+        assertEquals(expectedPkCols, Joiner.on(",").join(getColumnNames(columns)));
+        List<String> dataTypes = new ArrayList<String>();
+        columns = new ArrayList<Pair<String,String>>();
+        PhoenixRuntime.getPkColsDataTypesForSql(columns, dataTypes, plan, conn, true);
+        
+        assertEquals(expectedPkColsDataTypes, appendColNamesDataTypes(columns, dataTypes));
+        assertEquals(expectedHasOrderBy, PhoenixRuntime.hasOrderBy(plan));
+        assertEquals(expectedLimit, PhoenixRuntime.getLimit(plan));
+    }
+    
+    private static List<String> getColumnNames(List<Pair<String, String>> columns) {
+        List<String> columnNames = new ArrayList<String>(columns.size());
+        for (Pair<String, String> col : columns) {
+            String familyName = col.getFirst();
+            String columnName = col.getSecond();
+            if (familyName != null) {
+                columnName = familyName + QueryConstants.NAME_SEPARATOR + columnName;
+            }
+            columnNames.add(columnName);
+        }
+        return columnNames;
+    }
+    
+    private String addQuotes(String familyName, String columnNames) {
+        Iterable<String> columnNamesList = Splitter.on(",").split(columnNames);
+        List<String> quotedColumnNames = new ArrayList<String>();
+        for (String columnName : columnNamesList) {
+            String quotedColumnName = SchemaUtil.getQuotedFullColumnName(familyName, columnName);
+            quotedColumnNames.add(quotedColumnName);
+        }
+        return Joiner.on(",").join(quotedColumnNames);
+    }
+    
+    private String appendColNamesDataTypes(List<Pair<String, String>> columns, List<String> dataTypes) {
+        int size = columns.size();
+        assertEquals(size, dataTypes.size()); // they will be equal, but what the heck?
+        List<String> pkColsDataTypes = new ArrayList<String>(size);
+        for (int i = 0; i < size; i++) {
+            String familyName = columns.get(i).getFirst();
+            String columnName = columns.get(i).getSecond();
+            if (familyName != null) {
+                columnName = familyName + QueryConstants.NAME_SEPARATOR + columnName;
+            }
+            pkColsDataTypes.add(columnName + " " + dataTypes.get(i));
+        }
+        return Joiner.on(",").join(pkColsDataTypes);
+    }
+    
 }
\ No newline at end of file


[4/4] git commit: PHOENIX-973 Lexer skips unexpected characters

Posted by ja...@apache.org.
PHOENIX-973 Lexer skips unexpected characters


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

Branch: refs/heads/3.0
Commit: ef062ad250fc6203cf613dea7417160a68efabd4
Parents: cf07a5d
Author: James Taylor <jt...@salesforce.com>
Authored: Fri Oct 17 18:12:15 2014 -0700
Committer: James Taylor <jt...@salesforce.com>
Committed: Fri Oct 17 18:44:58 2014 -0700

----------------------------------------------------------------------
 phoenix-core/src/main/antlr3/PhoenixSQL.g       | 13 ++-
 .../query/ConnectionQueryServicesImpl.java      |  2 +-
 .../org/apache/phoenix/schema/SequenceKey.java  |  4 +-
 .../org/apache/phoenix/util/MetaDataUtil.java   | 14 +--
 .../org/apache/phoenix/util/UpgradeUtil.java    | 95 ++++++++++++--------
 .../apache/phoenix/parse/QueryParserTest.java   | 16 +++-
 .../java/org/apache/phoenix/query/BaseTest.java |  6 +-
 7 files changed, 96 insertions(+), 54 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/ef062ad2/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 48049c9..4a3a8d8 100644
--- a/phoenix-core/src/main/antlr3/PhoenixSQL.g
+++ b/phoenix-core/src/main/antlr3/PhoenixSQL.g
@@ -950,13 +950,12 @@ SL_COMMENT2: '--';
 
 // Bind names start with a colon and followed by 1 or more letter/digit/underscores
 BIND_NAME
-    : COLON (LETTER|DIGIT|'_')+
+    : COLON (DIGIT)+
     ;
 
-// Valid names can have a single underscore, but not multiple
-// Turn back on literal testing, all names are literals.
+
 NAME
-    :    LETTER (FIELDCHAR)* ('\"' (DBL_QUOTE_CHAR)* '\"')?
+    :    LETTER (FIELDCHAR)*
     |    '\"' (DBL_QUOTE_CHAR)* '\"'
     ;
 
@@ -1174,4 +1173,10 @@ SL_COMMENT
 DOT
     : '.'
     ;
+    
+OTHER      
+    : . { if (true) // to prevent compile error
+              throw new RuntimeException("Unexpected char: '" + $text + "'"); } 
+    ;
+
 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/ef062ad2/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 688cf42..edeb206 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
@@ -1348,7 +1348,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
                             } catch (TableAlreadyExistsException e) {
                                 // This will occur if we have an older SYSTEM.SEQUENCE, so we need to update it to include
                                 // any new columns we've added.
-                                if (UpgradeUtil.addSaltByteToSequenceTable(metaConnection, nSaltBuckets)) {
+                                if (UpgradeUtil.upgradeSequenceTable(metaConnection, nSaltBuckets)) {
                                     metaConnection.removeTable(null,
                                             PhoenixDatabaseMetaData.SYSTEM_CATALOG_SCHEMA,
                                             PhoenixDatabaseMetaData.TYPE_SEQUENCE,

http://git-wip-us.apache.org/repos/asf/phoenix/blob/ef062ad2/phoenix-core/src/main/java/org/apache/phoenix/schema/SequenceKey.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/SequenceKey.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/SequenceKey.java
index 94ca549..6f82630 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/SequenceKey.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/SequenceKey.java
@@ -32,9 +32,9 @@ public class SequenceKey implements Comparable<SequenceKey> {
         this.tenantId = tenantId;
         this.schemaName = schemaName;
         this.sequenceName = sequenceName;
-        this.key = ByteUtil.concat(nBuckets <= 0 ? ByteUtil.EMPTY_BYTE_ARRAY : QueryConstants.SEPARATOR_BYTE_ARRAY, tenantId == null  ? ByteUtil.EMPTY_BYTE_ARRAY : Bytes.toBytes(tenantId), QueryConstants.SEPARATOR_BYTE_ARRAY, schemaName == null ? ByteUtil.EMPTY_BYTE_ARRAY : Bytes.toBytes(schemaName), QueryConstants.SEPARATOR_BYTE_ARRAY, Bytes.toBytes(sequenceName));
+        this.key = ByteUtil.concat((nBuckets <= 0 ? ByteUtil.EMPTY_BYTE_ARRAY : QueryConstants.SEPARATOR_BYTE_ARRAY), tenantId == null  ? ByteUtil.EMPTY_BYTE_ARRAY : Bytes.toBytes(tenantId), QueryConstants.SEPARATOR_BYTE_ARRAY, schemaName == null ? ByteUtil.EMPTY_BYTE_ARRAY : Bytes.toBytes(schemaName), QueryConstants.SEPARATOR_BYTE_ARRAY, Bytes.toBytes(sequenceName));
         if (nBuckets > 0) {
-            key[0] = SaltingUtil.getSaltingByte(key, SaltingUtil.NUM_SALTING_BYTES, key.length - SaltingUtil.NUM_SALTING_BYTES, SaltingUtil.MAX_BUCKET_NUM);
+            key[0] = SaltingUtil.getSaltingByte(key, SaltingUtil.NUM_SALTING_BYTES, key.length - SaltingUtil.NUM_SALTING_BYTES, nBuckets);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/ef062ad2/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 b7d6d98..608bfc2 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
@@ -61,6 +61,7 @@ public class MetaDataUtil {
     public static final String VIEW_INDEX_TABLE_PREFIX = "_IDX_";
     public static final byte[] VIEW_INDEX_TABLE_PREFIX_BYTES = Bytes.toBytes(VIEW_INDEX_TABLE_PREFIX);
     public static final String VIEW_INDEX_SEQUENCE_PREFIX = "_SEQ_";
+    public static final String VIEW_INDEX_SEQUENCE_NAME_PREFIX = "_ID_";
     public static final byte[] VIEW_INDEX_SEQUENCE_PREFIX_BYTES = Bytes.toBytes(VIEW_INDEX_SEQUENCE_PREFIX);
     public static final String VIEW_INDEX_ID_COLUMN_NAME = "_INDEX_ID";
     
@@ -260,13 +261,17 @@ public class MetaDataUtil {
     }
 
 
+    public static String getViewIndexSchemaName(PName physicalName) {
+        return VIEW_INDEX_SEQUENCE_PREFIX + physicalName.getString();
+    }
+    
     public static SequenceKey getViewIndexSequenceKey(String tenantId, PName physicalName, int nSaltBuckets) {
         // Create global sequence of the form: <prefixed base table name><tenant id>
         // rather than tenant-specific sequence, as it makes it much easier
         // to cleanup when the physical table is dropped, as we can delete
         // all global sequences leading with <prefix> + physical name.
-        String schemaName = VIEW_INDEX_SEQUENCE_PREFIX + physicalName.getString();
-        String tableName = tenantId == null ? "" : tenantId;
+        String schemaName = getViewIndexSchemaName(physicalName);
+        String tableName = VIEW_INDEX_SEQUENCE_NAME_PREFIX + (tenantId == null ? "" : tenantId);
         return new SequenceKey(null, schemaName, tableName, nSaltBuckets);
     }
 
@@ -297,11 +302,10 @@ public class MetaDataUtil {
     }
     
     public static void deleteViewIndexSequences(PhoenixConnection connection, PName name) throws SQLException {
-        int nSequenceSaltBuckets = connection.getQueryServices().getSequenceSaltBuckets();
-        SequenceKey key = getViewIndexSequenceKey(null, name, nSequenceSaltBuckets);
+        String schemaName = getViewIndexSchemaName(name);
         connection.createStatement().executeUpdate("DELETE FROM " + PhoenixDatabaseMetaData.SEQUENCE_FULLNAME_ESCAPED + 
                 " WHERE " + PhoenixDatabaseMetaData.TENANT_ID + " IS NULL AND " + 
-                PhoenixDatabaseMetaData.SEQUENCE_SCHEMA + " = '" + key.getSchemaName() + "'");
+                PhoenixDatabaseMetaData.SEQUENCE_SCHEMA + " = '" + schemaName + "'");
         
     }
     

http://git-wip-us.apache.org/repos/asf/phoenix/blob/ef062ad2/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 3054200..b51b455 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
@@ -29,10 +29,14 @@ import org.apache.hadoop.hbase.client.Put;
 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.MetaDataProtocol;
 import org.apache.phoenix.jdbc.PhoenixConnection;
 import org.apache.phoenix.jdbc.PhoenixDatabaseMetaData;
+import org.apache.phoenix.query.QueryConstants;
 import org.apache.phoenix.schema.PDataType;
+import org.apache.phoenix.schema.PName;
+import org.apache.phoenix.schema.PNameFactory;
 import org.apache.phoenix.schema.SaltingUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -41,15 +45,13 @@ import com.google.common.collect.Lists;
 
 public class UpgradeUtil {
     private static final Logger logger = LoggerFactory.getLogger(UpgradeUtil.class);
+    private static final byte[] SEQ_PREFIX_BYTES = ByteUtil.concat(QueryConstants.SEPARATOR_BYTE_ARRAY, Bytes.toBytes("_SEQ_"));
 
     private UpgradeUtil() {
     }
 
-    public static boolean addSaltByteToSequenceTable(PhoenixConnection conn, int nSaltBuckets) throws SQLException {
-        if (nSaltBuckets <= 0) {
-            logger.info("Not upgrading SYSTEM.SEQUENCE table because SALT_BUCKETS is zero");
-            return false;
-        }
+    @SuppressWarnings("deprecation")
+    public static boolean upgradeSequenceTable(PhoenixConnection conn, int nSaltBuckets) throws SQLException {
         logger.info("Upgrading SYSTEM.SEQUENCE table");
 
         byte[] seqTableKey = SchemaUtil.getTableKey(null, PhoenixDatabaseMetaData.SYSTEM_CATALOG_SCHEMA, PhoenixDatabaseMetaData.TYPE_SEQUENCE);
@@ -89,31 +91,33 @@ public class UpgradeUtil {
                     Result result;
                      while ((result = scanner.next()) != null) {
                         for (KeyValue keyValue : result.raw()) {
-                            KeyValue newKeyValue = addSaltByte(keyValue);
-                            sizeBytes += newKeyValue.getLength();
-                            if (KeyValue.Type.codeToType(newKeyValue.getType()) == KeyValue.Type.Put) {
-                                // Delete old value
-                                byte[] buf = keyValue.getBuffer();
-                                Delete delete = new Delete(keyValue.getRow());
-                                KeyValue deleteKeyValue = new KeyValue(buf, keyValue.getRowOffset(), keyValue.getRowLength(),
-                                        buf, keyValue.getFamilyOffset(), keyValue.getFamilyLength(),
-                                        buf, keyValue.getQualifierOffset(), keyValue.getQualifierLength(),
-                                        keyValue.getTimestamp(), KeyValue.Type.Delete,
-                                        ByteUtil.EMPTY_BYTE_ARRAY,0,0);
-                                delete.addDeleteMarker(deleteKeyValue);
-                                mutations.add(delete);
-                                sizeBytes += deleteKeyValue.getLength();
-                                // Put new value
-                                Put put = new Put(newKeyValue.getRow());
-                                put.add(newKeyValue);
-                                mutations.add(put);
-                            } else if (KeyValue.Type.codeToType(newKeyValue.getType()) == KeyValue.Type.Delete){
-                                // Copy delete marker using new key so that it continues
-                                // to delete the key value preceding it that will be updated
-                                // as well.
-                                Delete delete = new Delete(newKeyValue.getRow());
-                                delete.addDeleteMarker(newKeyValue);
-                                mutations.add(delete);
+                            KeyValue newKeyValue = addSaltByte(keyValue, nSaltBuckets);
+                            if (newKeyValue != null) {
+                                sizeBytes += newKeyValue.getLength();
+                                if (KeyValue.Type.codeToType(newKeyValue.getType()) == KeyValue.Type.Put) {
+                                    // Delete old value
+                                    byte[] buf = keyValue.getBuffer();
+                                    Delete delete = new Delete(keyValue.getRow());
+                                    KeyValue deleteKeyValue = new KeyValue(buf, keyValue.getRowOffset(), keyValue.getRowLength(),
+                                            buf, keyValue.getFamilyOffset(), keyValue.getFamilyLength(),
+                                            buf, keyValue.getQualifierOffset(), keyValue.getQualifierLength(),
+                                            keyValue.getTimestamp(), KeyValue.Type.Delete,
+                                            ByteUtil.EMPTY_BYTE_ARRAY,0,0);
+                                    delete.addDeleteMarker(deleteKeyValue);
+                                    mutations.add(delete);
+                                    sizeBytes += deleteKeyValue.getLength();
+                                    // Put new value
+                                    Put put = new Put(newKeyValue.getRow());
+                                    put.add(newKeyValue);
+                                    mutations.add(put);
+                                } else if (KeyValue.Type.codeToType(newKeyValue.getType()) == KeyValue.Type.Delete){
+                                    // Copy delete marker using new key so that it continues
+                                    // to delete the key value preceding it that will be updated
+                                    // as well.
+                                    Delete delete = new Delete(newKeyValue.getRow());
+                                    delete.addDeleteMarker(newKeyValue);
+                                    mutations.add(delete);
+                                }
                             }
                             if (sizeBytes >= batchSizeBytes) {
                                 logger.info("Committing bactch of SYSTEM.SEQUENCE rows");
@@ -179,13 +183,34 @@ public class UpgradeUtil {
         }
     }
     
-    private static KeyValue addSaltByte(KeyValue keyValue) {
+    @SuppressWarnings("deprecation")
+    private static KeyValue addSaltByte(KeyValue keyValue, int nSaltBuckets) {
+        byte[] buf = keyValue.getBuffer();
         int length = keyValue.getRowLength();
         int offset = keyValue.getRowOffset();
-        byte[] buf = keyValue.getBuffer();
-        byte[] newBuf = new byte[length + 1];
-        System.arraycopy(buf, offset, newBuf, SaltingUtil.NUM_SALTING_BYTES, length);
-        newBuf[0] = SaltingUtil.getSaltingByte(newBuf, SaltingUtil.NUM_SALTING_BYTES, length, SaltingUtil.MAX_BUCKET_NUM);
+        boolean isViewSeq = length > SEQ_PREFIX_BYTES.length && Bytes.compareTo(SEQ_PREFIX_BYTES, 0, SEQ_PREFIX_BYTES.length, buf, offset, SEQ_PREFIX_BYTES.length) == 0;
+        if (!isViewSeq && nSaltBuckets == 0) {
+            return null;
+        }
+        byte[] newBuf;
+        if (isViewSeq) { // We messed up the name for the sequences for view indexes so we'll take this opportunity to fix it
+            if (buf[length-1] == 0) { // Global indexes on views have trailing null byte
+                length--;
+            }
+            byte[][] rowKeyMetaData = new byte[3][];
+            SchemaUtil.getVarChars(buf, offset, length, 0, rowKeyMetaData);
+            byte[] schemaName = rowKeyMetaData[PhoenixDatabaseMetaData.SCHEMA_NAME_INDEX];
+            byte[] unprefixedSchemaName = new byte[schemaName.length - MetaDataUtil.VIEW_INDEX_SEQUENCE_PREFIX_BYTES.length];
+            System.arraycopy(schemaName, MetaDataUtil.VIEW_INDEX_SEQUENCE_PREFIX_BYTES.length, unprefixedSchemaName, 0, unprefixedSchemaName.length);
+            byte[] tableName = rowKeyMetaData[PhoenixDatabaseMetaData.TABLE_NAME_INDEX];
+            PName physicalName = PNameFactory.newName(unprefixedSchemaName);
+            // Reformulate key based on correct data
+            newBuf = MetaDataUtil.getViewIndexSequenceKey(tableName == null ? null : Bytes.toString(tableName), physicalName, nSaltBuckets).getKey();
+        } else {
+            newBuf = new byte[length + 1];
+            System.arraycopy(buf, offset, newBuf, SaltingUtil.NUM_SALTING_BYTES, length);
+            newBuf[0] = SaltingUtil.getSaltingByte(newBuf, SaltingUtil.NUM_SALTING_BYTES, length, nSaltBuckets);
+        }
         return new KeyValue(newBuf, 0, newBuf.length,
                 buf, keyValue.getFamilyOffset(), keyValue.getFamilyLength(),
                 buf, keyValue.getQualifierOffset(), keyValue.getQualifierLength(),

http://git-wip-us.apache.org/repos/asf/phoenix/blob/ef062ad2/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 9bf3896..201172b 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
@@ -28,10 +28,9 @@ import java.sql.SQLFeatureNotSupportedException;
 import java.util.List;
 
 import org.apache.hadoop.hbase.util.Pair;
-import org.junit.Test;
-
 import org.apache.phoenix.exception.SQLExceptionCode;
 import org.apache.phoenix.schema.SortOrder;
+import org.junit.Test;
 
 
 public class QueryParserTest {
@@ -629,6 +628,19 @@ public class QueryParserTest {
     }
 
     @Test
+    public void testTableNameStartsWithUnderscore() throws Exception {
+        SQLParser parser = new SQLParser(
+                new StringReader(
+                        "select* from _t where k in ( 1,2 )"));
+        try {
+            parser.parseStatement();
+            fail();
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.PARSER_ERROR.getErrorCode(), e.getErrorCode());
+        }
+    }
+
+    @Test
     public void testValidUpsertSelectHint() throws Exception {
         SQLParser parser = new SQLParser(
                 new StringReader(

http://git-wip-us.apache.org/repos/asf/phoenix/blob/ef062ad2/phoenix-core/src/test/java/org/apache/phoenix/query/BaseTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/query/BaseTest.java b/phoenix-core/src/test/java/org/apache/phoenix/query/BaseTest.java
index cfa8787..b7ecfeb 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/query/BaseTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/query/BaseTest.java
@@ -795,11 +795,7 @@ public abstract class BaseTest {
                 + PhoenixDatabaseMetaData.SEQUENCE_NAME 
                 + " FROM " + PhoenixDatabaseMetaData.SEQUENCE_FULLNAME_ESCAPED);
         while (rs.next()) {
-            try {
-                conn.createStatement().execute("DROP SEQUENCE " + SchemaUtil.getTableName(rs.getString(1), rs.getString(2)));
-            } catch (Exception e) {
-                //FIXME: see https://issues.apache.org/jira/browse/PHOENIX-973
-            }
+            conn.createStatement().execute("DROP SEQUENCE " + SchemaUtil.getEscapedTableName(rs.getString(1), rs.getString(2)));
         }
     }