You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@phoenix.apache.org by ra...@apache.org on 2015/01/14 05:09:04 UTC

[1/2] phoenix git commit: Phoenix-1453 - Collect row counts per region in stats table (Ramkrishna)

Repository: phoenix
Updated Branches:
  refs/heads/master ddcb591f3 -> 223a14840


http://git-wip-us.apache.org/repos/asf/phoenix/blob/223a1484/phoenix-core/src/main/java/org/apache/phoenix/schema/stats/GuidePostsInfo.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/stats/GuidePostsInfo.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/stats/GuidePostsInfo.java
index b4aa0b8..c3cc8fd 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/stats/GuidePostsInfo.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/stats/GuidePostsInfo.java
@@ -1,19 +1,3 @@
-package org.apache.phoenix.schema.stats;
-
-import java.io.ByteArrayInputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.util.Collections;
-import java.util.List;
-
-import org.apache.hadoop.hbase.util.Bytes;
-import org.apache.hadoop.io.WritableUtils;
-import org.apache.phoenix.util.TrustedByteArrayOutputStream;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterators;
-import com.google.common.collect.Lists;
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
@@ -31,26 +15,116 @@ import com.google.common.collect.Lists;
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package org.apache.phoenix.schema.stats;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.io.WritableUtils;
+import org.apache.phoenix.util.TrustedByteArrayOutputStream;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
 /**
- *  A simple POJO class that holds the information related to GuidePosts serDe.
+ *  A class that holds the guidePosts of a region and also allows combining the 
+ *  guidePosts of different regions when the GuidePostsInfo is formed for a table.
  */
 public class GuidePostsInfo {
-	public static final GuidePostsInfo EMPTY_GUIDE_POSTS_INFO = new GuidePostsInfo(0L, Collections.<byte[]>emptyList());
-	
-    private long byteCount; // Number of bytes traversed in the region
-    private long keyByteSize; // Total number of bytes in keys stored in guidePosts
+
+    /**
+     * the total number of guidePosts for the table combining all the guidePosts per region per cf.
+     */
     private List<byte[]> guidePosts;
+    /**
+     * The bytecount that is flattened across the total number of guide posts.
+     */
+    private long byteCount = 0;
+    
+    /**
+     * The rowCount that is flattened across the total number of guide posts.
+     */
+    private long rowCount = 0;
+    
+    private long keyByteSize; // Total number of bytes in keys stored in guidePosts
 
-    public GuidePostsInfo(long byteCount, List<byte[]> guidePosts) {
-        this.byteCount = byteCount;
+    /**
+     * Constructor that creates GuidePostsInfo per region
+     * @param byteCount
+     * @param guidePosts
+     * @param rowCount
+     */
+    public GuidePostsInfo(long byteCount, List<byte[]> guidePosts, long rowCount) {
         this.guidePosts = ImmutableList.copyOf(guidePosts);
         int size = 0;
         for (byte[] key : guidePosts) {
             size += key.length;
         }
         this.keyByteSize = size;
+        this.byteCount = byteCount;
+        this.rowCount = rowCount;
+    }
+    
+    public long getByteCount() {
+        return byteCount;
     }
 
+    public List<byte[]> getGuidePosts() {
+        return guidePosts;
+    }
+
+    public long getRowCount() {
+        return this.rowCount;
+    }
+    
+    public void incrementRowCount() {
+        this.rowCount++;
+    }
+    
+    /**
+     * Combines the GuidePosts per region into one.
+     * @param oldInfo
+     */
+    public void combine(GuidePostsInfo oldInfo) {
+        byte[] newFirstKey = oldInfo.getGuidePosts().get(0);
+        byte[] existingLastKey;
+        if (!this.getGuidePosts().isEmpty()) {
+            existingLastKey = this.getGuidePosts().get(this.getGuidePosts().size() - 1);
+        } else {
+            existingLastKey = HConstants.EMPTY_BYTE_ARRAY;
+        }
+        int size = oldInfo.getGuidePosts().size();
+        // If the existing guidePosts is lesser than the new RegionInfo that we are combining
+        // then add the new Region info to the end of the current GuidePosts.
+        // If the new region info is smaller than the existing guideposts then add the existing
+        // guide posts after the new guideposts.
+        List<byte[]> newTotalGuidePosts = new ArrayList<byte[]>(this.getGuidePosts().size() + size);
+        if (Bytes.compareTo(existingLastKey, newFirstKey) <= 0) {
+            newTotalGuidePosts.addAll(this.getGuidePosts());
+            newTotalGuidePosts.addAll(oldInfo.getGuidePosts());
+        } else {
+            newTotalGuidePosts.addAll(oldInfo.getGuidePosts());
+            newTotalGuidePosts.addAll(this.getGuidePosts());
+        }
+        this.guidePosts = ImmutableList.copyOf(newTotalGuidePosts);
+        this.byteCount += oldInfo.getByteCount();
+        this.keyByteSize += oldInfo.keyByteSize;
+        this.rowCount += oldInfo.getRowCount();
+    }
+    
+    /**
+     * The guide posts, rowCount and byteCount are accumulated every time a guidePosts depth is
+     * reached while collecting stats.
+     * @param row
+     * @param byteCount
+     * @param rowCount
+     * @return
+     */
     public boolean addGuidePost(byte[] row, long byteCount) {
         if (guidePosts.isEmpty() || Bytes.compareTo(row, guidePosts.get(guidePosts.size() - 1)) > 0) {
             List<byte[]> newGuidePosts = Lists.newArrayListWithExpectedSize(this.getGuidePosts().size() + 1);
@@ -64,23 +138,14 @@ public class GuidePostsInfo {
         return false;
     }
     
-    public void combine(GuidePostsInfo oldInfo) {
-        // FIXME: I don't think we need to do a merge sort here, as the keys won't be interleaved.
-        // We just need to concatenate them in the correct way.
-        this.guidePosts = ImmutableList.copyOf(Iterators.mergeSorted(ImmutableList.of(this.getGuidePosts().iterator(), oldInfo.getGuidePosts().iterator()), Bytes.BYTES_COMPARATOR));
-        this.byteCount += oldInfo.getByteCount();
-        this.keyByteSize += oldInfo.keyByteSize;
-    }
-    
-    public long getByteCount() {
-        return byteCount;
-    }
-
-    public List<byte[]> getGuidePosts() {
-        return guidePosts;
-    }
-
-    public static GuidePostsInfo fromBytes(byte[] buf, int offset, int l) {
+    /**
+     * Deserializes the per row guidePosts info from the value part of each cell in the SYSTEM.STATS table
+     * @param buf
+     * @param offset
+     * @param l
+     * @return
+     */
+    public static GuidePostsInfo deserializeGuidePostsInfo(byte[] buf, int offset, int l, long rowCount) {
         try {
             ByteArrayInputStream bytesIn = new ByteArrayInputStream(buf, offset, l);
             try {
@@ -99,7 +164,7 @@ public class GuidePostsInfo {
                             }
                         }
                     }
-                    return new GuidePostsInfo(byteCount, guidePosts);
+                    return new GuidePostsInfo(byteCount, guidePosts, rowCount);
                 } catch (IOException e) {
                     throw new RuntimeException(e); // not possible
                 } finally {
@@ -116,18 +181,29 @@ public class GuidePostsInfo {
             throw new RuntimeException(e); // not possible
         }
     }
-    
-    public byte[] toBytes() {
+
+    /**
+     * Serailizes the guidePosts info as value in the SYSTEM.STATS table.
+     * <br>
+     * The format is,
+     * <br>
+     *  - number of bytes traversed
+     * <br>
+     *  - number of key bytes in the region
+     * <br>
+     *  - number of guideposts for that family
+     * <br> u
+     *  - [guidepostSize][guidePostsArray],[guidePostsSize][guidePostArray]
+     * @return the byte[] to be serialized in the cell
+     */
+    public byte[] serializeGuidePostsInfo() {
         int size = guidePosts.size();
-        // Serialize the number of bytes traversed, number of key bytes in the region,
-        // number of guideposts for that family, <<guidepostSize><guidePostsArray>,<guidePostsSize> <guidePostArray>>
         // We will lose precision here?
-        TrustedByteArrayOutputStream bs = new TrustedByteArrayOutputStream(
-                (int)(Bytes.SIZEOF_LONG + Bytes.SIZEOF_LONG + Bytes.SIZEOF_INT + this.keyByteSize + (WritableUtils
-                        .getVIntSize(size) * size)));
+        TrustedByteArrayOutputStream bs = new TrustedByteArrayOutputStream((int)(Bytes.SIZEOF_LONG + Bytes.SIZEOF_LONG
+                + Bytes.SIZEOF_INT + this.keyByteSize + (WritableUtils.getVIntSize(size) * size)));
         DataOutputStream os = new DataOutputStream(bs);
         try {
-            os.writeLong(this.getByteCount());
+            os.writeLong(byteCount);
             os.writeInt(size);
             for (byte[] element : guidePosts) {
                 WritableUtils.writeVInt(os, element.length);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/223a1484/phoenix-core/src/main/java/org/apache/phoenix/schema/stats/StatisticsCollector.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/stats/StatisticsCollector.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/stats/StatisticsCollector.java
index b943b97..ade0fba 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/stats/StatisticsCollector.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/stats/StatisticsCollector.java
@@ -33,7 +33,6 @@ import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.hadoop.hbase.regionserver.HRegion;
 import org.apache.hadoop.hbase.regionserver.InternalScanner;
 import org.apache.hadoop.hbase.regionserver.Store;
-import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.hadoop.hbase.util.Pair;
 import org.apache.phoenix.coprocessor.MetaDataProtocol;
 import org.apache.phoenix.hbase.index.util.ImmutableBytesPtr;
@@ -62,8 +61,6 @@ public class StatisticsCollector {
     private long guidepostDepth;
     private long maxTimeStamp = MetaDataProtocol.MIN_TABLE_TIMESTAMP;
     private Map<ImmutableBytesPtr, Pair<Long,GuidePostsInfo>> guidePostsMap = Maps.newHashMap();
-    // Tracks the bytecount per family if it has reached the guidePostsDepth
-    private Map<ImmutableBytesPtr, Boolean> familyMap = Maps.newHashMap();
     protected StatisticsWriter statsTable;
     private Pair<Long,GuidePostsInfo> cachedGps = null;
 
@@ -84,8 +81,8 @@ public class StatisticsCollector {
         // in a compaction we know the one family ahead of time
         if (family != null) {
             ImmutableBytesPtr cfKey = new ImmutableBytesPtr(family);
-            familyMap.put(cfKey, true);
-            cachedGps = new Pair<Long,GuidePostsInfo>(0L,new GuidePostsInfo(0, Collections.<byte[]>emptyList()));
+            cachedGps = new Pair<Long, GuidePostsInfo>(0l, new GuidePostsInfo(0,
+                    Collections.<byte[]> emptyList(), 0l));
             guidePostsMap.put(cfKey, cachedGps);
         }
     }
@@ -117,7 +114,7 @@ public class StatisticsCollector {
             boolean delete, List<Mutation> mutations, long currentTime) throws IOException {
         try {
             // update the statistics table
-            for (ImmutableBytesPtr fam : familyMap.keySet()) {
+            for (ImmutableBytesPtr fam : guidePostsMap.keySet()) {
                 if (delete) {
                     if(logger.isDebugEnabled()) {
                         logger.debug("Deleting the stats for the region "+region.getRegionInfo());
@@ -148,9 +145,49 @@ public class StatisticsCollector {
      *            next batch of {@link KeyValue}s
      */
     public void collectStatistics(final List<Cell> results) {
-        for (Cell c : results) {
-            KeyValue kv = KeyValueUtil.ensureKeyValue(c);
-            updateStatistic(kv);
+        Map<ImmutableBytesPtr, Boolean> famMap = Maps.newHashMap();
+        List<GuidePostsInfo> rowTracker = null;
+        if(cachedGps == null) {
+            rowTracker = 
+                    new ArrayList<GuidePostsInfo>();
+        }
+        for (Cell cell : results) {
+            KeyValue kv = KeyValueUtil.ensureKeyValue(cell);
+            maxTimeStamp = Math.max(maxTimeStamp, kv.getTimestamp());
+            Pair<Long, GuidePostsInfo> gps;
+            if (cachedGps == null) {
+                ImmutableBytesPtr cfKey = new ImmutableBytesPtr(kv.getFamilyArray(), kv.getFamilyOffset(),
+                        kv.getFamilyLength());
+                gps = guidePostsMap.get(cfKey);
+                if (gps == null) {
+                    gps = new Pair<Long, GuidePostsInfo>(0l, new GuidePostsInfo(0,
+                            Collections.<byte[]> emptyList(), 0l));
+                    guidePostsMap.put(cfKey, gps);
+                }
+                if (famMap.get(cfKey) == null) {
+                    famMap.put(cfKey, true);
+                    rowTracker.add(gps.getSecond());
+                }
+            } else {
+                gps = cachedGps;
+            }
+            int kvLength = kv.getLength();
+            long byteCount = gps.getFirst() + kvLength;
+            gps.setFirst(byteCount);
+            if (byteCount >= guidepostDepth) {
+                byte[] row = ByteUtil.copyKeyBytesIfNecessary(new ImmutableBytesWritable(kv.getRowArray(), kv
+                        .getRowOffset(), kv.getRowLength()));
+                if (gps.getSecond().addGuidePost(row, byteCount)) {
+                    gps.setFirst(0l);
+                }
+            }
+        }
+        if(cachedGps == null) {
+            for (GuidePostsInfo s : rowTracker) {
+                s.incrementRowCount();
+            }
+        } else {
+            cachedGps.getSecond().incrementRowCount();
         }
     }
 
@@ -189,47 +226,17 @@ public class StatisticsCollector {
 
     public void clear() {
         this.guidePostsMap.clear();
-        this.familyMap.clear();
         maxTimeStamp = MetaDataProtocol.MIN_TABLE_TIMESTAMP;
     }
 
     public void addGuidePost(ImmutableBytesPtr cfKey, GuidePostsInfo info, long byteSize, long timestamp) {
-    	Pair<Long,GuidePostsInfo> newInfo = new Pair<Long,GuidePostsInfo>(byteSize,info);
-    	Pair<Long,GuidePostsInfo> oldInfo = guidePostsMap.put(cfKey, newInfo);
-    	if (oldInfo != null) {
-    		info.combine(oldInfo.getSecond());
-    		newInfo.setFirst(oldInfo.getFirst() + newInfo.getFirst());
-    	}
-        maxTimeStamp = Math.max(maxTimeStamp, timestamp);
-    }
-    
-    public void updateStatistic(KeyValue kv) {
-        maxTimeStamp = Math.max(maxTimeStamp, kv.getTimestamp());
-
-        Pair<Long,GuidePostsInfo> gps;
-        if (cachedGps == null) {
-            ImmutableBytesPtr cfKey = new ImmutableBytesPtr(kv.getFamilyArray(), kv.getFamilyOffset(), kv.getFamilyLength());
-            familyMap.put(cfKey, true);
-
-            // TODO : This can be moved to an interface so that we could collect guide posts in different ways
-            gps = guidePostsMap.get(cfKey);
-            if (gps == null) {
-                gps = new Pair<Long,GuidePostsInfo>(0L,new GuidePostsInfo(0, Collections.<byte[]>emptyList()));
-                guidePostsMap.put(cfKey, gps);
-            }
-        } else {
-            gps = cachedGps;
-        }
-        int kvLength = kv.getLength();
-        long byteCount = gps.getFirst() + kvLength;
-        gps.setFirst(byteCount);
-        if (byteCount >= guidepostDepth) {
-            byte[] row = ByteUtil.copyKeyBytesIfNecessary(
-                    new ImmutableBytesWritable(kv.getRowArray(), kv.getRowOffset(), kv.getRowLength()));
-            if (gps.getSecond().addGuidePost(row, byteCount)) {
-                gps.setFirst(0L);
-            }
+        Pair<Long, GuidePostsInfo> newInfo = new Pair<Long, GuidePostsInfo>(byteSize, info);
+        Pair<Long, GuidePostsInfo> oldInfo = guidePostsMap.put(cfKey, newInfo);
+        if (oldInfo != null) {
+            info.combine(oldInfo.getSecond());
+            newInfo.setFirst(oldInfo.getFirst() + newInfo.getFirst());
         }
+        maxTimeStamp = Math.max(maxTimeStamp, timestamp);
     }
 
     public GuidePostsInfo getGuidePosts(ImmutableBytesPtr fam) {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/223a1484/phoenix-core/src/main/java/org/apache/phoenix/schema/stats/StatisticsScanner.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/stats/StatisticsScanner.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/stats/StatisticsScanner.java
index dddfc52..e012040 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/stats/StatisticsScanner.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/stats/StatisticsScanner.java
@@ -1,10 +1,19 @@
 /*
- * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version
- * 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for
- * the specific language governing permissions and limitations under the License.
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 package org.apache.phoenix.schema.stats;
 
@@ -16,7 +25,6 @@ import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.hadoop.hbase.Cell;
 import org.apache.hadoop.hbase.KeyValue;
-import org.apache.hadoop.hbase.KeyValueUtil;
 import org.apache.hadoop.hbase.client.Mutation;
 import org.apache.hadoop.hbase.regionserver.HRegion;
 import org.apache.hadoop.hbase.regionserver.InternalScanner;
@@ -63,12 +71,7 @@ public class StatisticsScanner implements InternalScanner {
      *            next batch of {@link KeyValue}s
      */
     protected void updateStat(final List<Cell> results) {
-        for (Cell c : results) {
-            KeyValue kv = KeyValueUtil.ensureKeyValue(c);
-            if (c.getTypeByte() == KeyValue.Type.Put.getCode()) {
-                tracker.updateStatistic(kv);
-            }
-        }
+        tracker.collectStatistics(results);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/phoenix/blob/223a1484/phoenix-core/src/main/java/org/apache/phoenix/schema/stats/StatisticsUtil.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/stats/StatisticsUtil.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/stats/StatisticsUtil.java
index bf9d80e..aab3a11 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/stats/StatisticsUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/stats/StatisticsUtil.java
@@ -38,6 +38,8 @@ import org.apache.phoenix.coprocessor.MetaDataProtocol;
 import org.apache.phoenix.hbase.index.util.ImmutableBytesPtr;
 import org.apache.phoenix.jdbc.PhoenixDatabaseMetaData;
 import org.apache.phoenix.query.QueryConstants;
+import org.apache.phoenix.schema.SortOrder;
+import org.apache.phoenix.schema.types.PLong;
 import org.apache.phoenix.util.ByteUtil;
 import org.apache.phoenix.util.MetaDataUtil;
 /**
@@ -50,6 +52,7 @@ public class StatisticsUtil {
 
     /** Number of parts in our complex key */
     protected static final int NUM_KEY_PARTS = 3;
+    
 
     public static byte[] getRowKey(byte[] table, ImmutableBytesPtr fam, byte[] region) {
         // always starts with the source table
@@ -76,6 +79,7 @@ public class StatisticsUtil {
         get.setTimeRange(MetaDataProtocol.MIN_TABLE_TIMESTAMP, clientTimeStamp);
         get.addColumn(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES, PhoenixDatabaseMetaData.GUIDE_POSTS_WIDTH_BYTES);
         get.addColumn(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES, PhoenixDatabaseMetaData.GUIDE_POSTS_BYTES);
+        get.addColumn(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES, PhoenixDatabaseMetaData.GUIDE_POSTS_ROW_COUNT_BYTES);
         return statsHTable.get(get);
     }
     
@@ -84,29 +88,54 @@ public class StatisticsUtil {
         ImmutableBytesWritable ptr = new ImmutableBytesWritable();
         Scan s = MetaDataUtil.newTableRowsScan(tableNameBytes, MetaDataProtocol.MIN_TABLE_TIMESTAMP, clientTimeStamp);
         s.addColumn(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES, PhoenixDatabaseMetaData.GUIDE_POSTS_BYTES);
+        s.addColumn(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES, PhoenixDatabaseMetaData.GUIDE_POSTS_ROW_COUNT_BYTES);
         ResultScanner scanner = statsHTable.getScanner(s);
         Result result = null;
         long timeStamp = MetaDataProtocol.MIN_TABLE_TIMESTAMP;
-        TreeMap<byte[], GuidePostsInfo> guidePostsPerCf = new TreeMap<byte[], GuidePostsInfo>(Bytes.BYTES_COMPARATOR);
+        TreeMap<byte[], GuidePostsInfo> guidePostsPerCf = new TreeMap<byte[], GuidePostsInfo>(
+                Bytes.BYTES_COMPARATOR);
         while ((result = scanner.next()) != null) {
             CellScanner cellScanner = result.cellScanner();
+            long rowCount = 0;
+            ImmutableBytesPtr valuePtr = new ImmutableBytesPtr(HConstants.EMPTY_BYTE_ARRAY);
+            byte[] cfName = null;
+            int tableNameLength;
+            int cfOffset;
+            int cfLength;
+            boolean valuesSet = false;
+            // Only the two cells with quals GUIDE_POSTS_ROW_COUNT_BYTES and GUIDE_POSTS_BYTES would be retrieved
             while (cellScanner.advance()) {
                 Cell current = cellScanner.current();
-                int tableNameLength = tableNameBytes.length + 1;
-                int cfOffset = current.getRowOffset() + tableNameLength;
-                int cfLength = getVarCharLength(current.getRowArray(), cfOffset, current.getRowLength()
-                        - tableNameLength);
-                ptr.set(current.getRowArray(), cfOffset, cfLength);
-                byte[] cfName = ByteUtil.copyKeyBytesIfNecessary(ptr);
-                GuidePostsInfo newInfo = GuidePostsInfo.fromBytes(current.getValueArray(), current.getValueOffset(), current.getValueLength());
-                GuidePostsInfo oldInfo = guidePostsPerCf.put(cfName, newInfo);
-                if (oldInfo != null) {
-                    newInfo.combine(oldInfo);
+                if (!valuesSet) {
+                    tableNameLength = tableNameBytes.length + 1;
+                    cfOffset = current.getRowOffset() + tableNameLength;
+                    cfLength = getVarCharLength(current.getRowArray(), cfOffset, current.getRowLength()
+                            - tableNameLength);
+                    ptr.set(current.getRowArray(), cfOffset, cfLength);
+                    valuesSet = true;
+                }
+                cfName = ByteUtil.copyKeyBytesIfNecessary(ptr);
+                if (Bytes.equals(current.getQualifierArray(), current.getQualifierOffset(),
+                        current.getQualifierLength(), PhoenixDatabaseMetaData.GUIDE_POSTS_ROW_COUNT_BYTES, 0,
+                        PhoenixDatabaseMetaData.GUIDE_POSTS_ROW_COUNT_BYTES.length)) {
+                    rowCount = PLong.INSTANCE.getCodec().decodeLong(current.getValueArray(),
+                            current.getValueOffset(), SortOrder.getDefault());
+                } else {
+                    valuePtr.set(current.getValueArray(), current.getValueOffset(),
+                        current.getValueLength());
                 }
                 if (current.getTimestamp() > timeStamp) {
                     timeStamp = current.getTimestamp();
                 }
             }
+            if (cfName != null) {
+                GuidePostsInfo newGPInfo = GuidePostsInfo.deserializeGuidePostsInfo(
+                        valuePtr.get(), valuePtr.getOffset(), valuePtr.getLength(), rowCount);
+                GuidePostsInfo oldInfo = guidePostsPerCf.put(cfName, newGPInfo);
+                if (oldInfo != null) {
+                    newGPInfo.combine(oldInfo);
+                }
+            }
         }
         if (!guidePostsPerCf.isEmpty()) {
             return new PTableStatsImpl(guidePostsPerCf, timeStamp);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/223a1484/phoenix-core/src/main/java/org/apache/phoenix/schema/stats/StatisticsWriter.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/stats/StatisticsWriter.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/stats/StatisticsWriter.java
index bb624f8..f46c3c1 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/stats/StatisticsWriter.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/stats/StatisticsWriter.java
@@ -43,10 +43,10 @@ import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.phoenix.hbase.index.util.ImmutableBytesPtr;
 import org.apache.phoenix.jdbc.PhoenixDatabaseMetaData;
 import org.apache.phoenix.query.QueryConstants;
+import org.apache.phoenix.schema.SortOrder;
 import org.apache.phoenix.schema.types.PDate;
 import org.apache.phoenix.schema.types.PLong;
 import org.apache.phoenix.schema.types.PVarbinary;
-import org.apache.phoenix.schema.SortOrder;
 import org.apache.phoenix.util.ByteUtil;
 import org.apache.phoenix.util.ServerUtil;
 import org.apache.phoenix.util.TimeKeeper;
@@ -100,49 +100,81 @@ public class StatisticsWriter implements Closeable {
         statsWriterTable.close();
     }
 
-    public void splitStats(HRegion p, HRegion l, HRegion r, StatisticsCollector tracker, ImmutableBytesPtr cfKey, List<Mutation> mutations) throws IOException {
+    public void splitStats(HRegion p, HRegion l, HRegion r, StatisticsCollector tracker, ImmutableBytesPtr cfKey,
+            List<Mutation> mutations) throws IOException {
         if (tracker == null) { return; }
         boolean useMaxTimeStamp = clientTimeStamp == StatisticsCollector.NO_TIMESTAMP;
         if (!useMaxTimeStamp) {
             mutations.add(getLastStatsUpdatedTimePut(clientTimeStamp));
         }
         long readTimeStamp = useMaxTimeStamp ? HConstants.LATEST_TIMESTAMP : clientTimeStamp;
-        Result result = StatisticsUtil.readRegionStatistics(statsReaderTable, tableName, cfKey, p.getRegionName(), readTimeStamp);
+        Result result = StatisticsUtil.readRegionStatistics(statsReaderTable, tableName, cfKey, p.getRegionName(),
+                readTimeStamp);
         if (result != null && !result.isEmpty()) {
         	Cell cell = result.getColumnLatestCell(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES, PhoenixDatabaseMetaData.GUIDE_POSTS_BYTES);
-
+        	Cell rowCountCell = result.getColumnLatestCell(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES, PhoenixDatabaseMetaData.GUIDE_POSTS_ROW_COUNT_BYTES);
+            long rowCount = 0;
         	if (cell != null) {
                 long writeTimeStamp = useMaxTimeStamp ? cell.getTimestamp() : clientTimeStamp;
 
-                GuidePostsInfo guidePosts = GuidePostsInfo.fromBytes(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
+                GuidePostsInfo guidePostsRegionInfo = GuidePostsInfo.deserializeGuidePostsInfo(cell.getValueArray(),
+                        cell.getValueOffset(), cell.getValueLength(), rowCount);
                 byte[] pPrefix = StatisticsUtil.getRowKey(tableName, cfKey, p.getRegionName());
                 mutations.add(new Delete(pPrefix, writeTimeStamp));
                 
 	        	long byteSize = 0;
-	        	Cell byteSizeCell = result.getColumnLatestCell(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES, PhoenixDatabaseMetaData.GUIDE_POSTS_WIDTH_BYTES);
-	        	if (byteSizeCell != null) {
-	        		byteSize = PLong.INSTANCE.getCodec().decodeLong(byteSizeCell.getValueArray(), byteSizeCell.getValueOffset(), SortOrder.getDefault()) / 2;
-	        	}
-	        	int midEndIndex, midStartIndex;
-	            int index = Collections.binarySearch(guidePosts.getGuidePosts(), r.getStartKey(), Bytes.BYTES_COMPARATOR);
-	            if (index < 0) {
-	                midEndIndex = midStartIndex = -(index + 1);
-	            } else {
-	                // For an exact match, we want to get rid of the exact match guidepost,
-	                // since it's replaced by the region boundary.
-	                midEndIndex = index;
-	                midStartIndex = index + 1;
-	            }
+                Cell byteSizeCell = result.getColumnLatestCell(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES,
+                        PhoenixDatabaseMetaData.GUIDE_POSTS_WIDTH_BYTES);
+                int index = Collections.binarySearch(guidePostsRegionInfo.getGuidePosts(), r.getStartKey(),
+                        Bytes.BYTES_COMPARATOR);
+                int size = guidePostsRegionInfo.getGuidePosts().size();
+                int midEndIndex, midStartIndex;
+                if (index < 0) {
+                    midEndIndex = midStartIndex = -(index + 1);
+                } else {
+                    // For an exact match, we want to get rid of the exact match guidepost,
+                    // since it's replaced by the region boundary.
+                    midEndIndex = index;
+                    midStartIndex = index + 1;
+                }
+                double per = (double)(midEndIndex + 1) / size;
+                long leftRowCount = 0;
+                long rightRowCount = 0;
+                long leftByteCount = 0;
+                long rightByteCount = 0;
+                if (rowCountCell != null) {
+                    rowCount = PLong.INSTANCE.getCodec().decodeLong(rowCountCell.getValueArray(),
+                            rowCountCell.getValueOffset(), SortOrder.getDefault());
+                    leftRowCount = (long)(per * rowCount);
+                    if (leftRowCount == rowCount) {
+                        leftRowCount = (rightRowCount = rowCount / 2);
+                    } else {
+                        rightRowCount = (long)((1 - per) * rowCount);
+                    }
+                }
+                if (byteSizeCell != null) {
+                    byteSize = PLong.INSTANCE.getCodec().decodeLong(byteSizeCell.getValueArray(),
+                            byteSizeCell.getValueOffset(), SortOrder.getDefault());
+                    leftByteCount = (long)(per * byteSize);
+                    if (leftByteCount == byteSize) {
+                        leftByteCount = (rightByteCount = byteSize / 2);
+                    } else {
+                        rightByteCount = (long)((1 - per) * byteSize);
+                    }
+                }
 	            if (midEndIndex > 0) {
-	                GuidePostsInfo lguidePosts = new GuidePostsInfo(byteSize, guidePosts.getGuidePosts().subList(0, midEndIndex));
-	                tracker.clear();
-	                tracker.addGuidePost(cfKey, lguidePosts, byteSize, cell.getTimestamp());
+	                GuidePostsInfo lguidePosts = new GuidePostsInfo(leftByteCount, guidePostsRegionInfo
+                            .getGuidePosts().subList(0, midEndIndex), leftRowCount);
+                    tracker.clear();
+	                tracker.addGuidePost(cfKey, lguidePosts, leftByteCount, cell.getTimestamp());
 	                addStats(l.getRegionName(), tracker, cfKey, mutations);
 	            }
-	            if (midStartIndex < guidePosts.getGuidePosts().size()) {
-	                GuidePostsInfo rguidePosts = new GuidePostsInfo(byteSize, guidePosts.getGuidePosts().subList(midStartIndex, guidePosts.getGuidePosts().size()));
+	            if (midStartIndex < size) {
+	                GuidePostsInfo rguidePosts = new GuidePostsInfo(rightByteCount, guidePostsRegionInfo
+                            .getGuidePosts().subList(midStartIndex, size),
+                            rightRowCount);
 	                tracker.clear();
-	                tracker.addGuidePost(cfKey, rguidePosts, byteSize, cell.getTimestamp());
+	                tracker.addGuidePost(cfKey, rguidePosts, rightByteCount, cell.getTimestamp());
 	                addStats(r.getRegionName(), tracker, cfKey, mutations);
 	            }
         	}
@@ -150,7 +182,7 @@ public class StatisticsWriter implements Closeable {
     }
     
     /**
-     * Update a list of statistics for a given region.  If the ANALYZE <tablename> query is issued
+     * Update a list of statistics for a given region.  If the UPDATE STATISTICS <tablename> query is issued
      * then we use Upsert queries to update the table
      * If the region gets splitted or the major compaction happens we update using HTable.put()
      * @param tracker - the statistics tracker
@@ -160,7 +192,8 @@ public class StatisticsWriter implements Closeable {
      *             if we fail to do any of the puts. Any single failure will prevent any future attempts for the remaining list of stats to
      *             update
      */
-    public void addStats(byte[] regionName, StatisticsCollector tracker, ImmutableBytesPtr cfKey, List<Mutation> mutations) throws IOException {
+    public void addStats(byte[] regionName, StatisticsCollector tracker, ImmutableBytesPtr cfKey,
+            List<Mutation> mutations) throws IOException {
         if (tracker == null) { return; }
         boolean useMaxTimeStamp = clientTimeStamp == StatisticsCollector.NO_TIMESTAMP;
         long timeStamp = clientTimeStamp;
@@ -174,14 +207,17 @@ public class StatisticsWriter implements Closeable {
         if (gp != null) {
             put.add(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES, PhoenixDatabaseMetaData.GUIDE_POSTS_COUNT_BYTES,
                     timeStamp, PLong.INSTANCE.toBytes((gp.getGuidePosts().size())));
-            put.add(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES, PhoenixDatabaseMetaData.GUIDE_POSTS_BYTES,
-                    timeStamp, PVarbinary.INSTANCE.toBytes(gp.toBytes()));
+            put.add(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES, PhoenixDatabaseMetaData.GUIDE_POSTS_BYTES, timeStamp,
+                    PVarbinary.INSTANCE.toBytes(gp.serializeGuidePostsInfo()));
             put.add(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES, PhoenixDatabaseMetaData.GUIDE_POSTS_WIDTH_BYTES,
                     timeStamp, PLong.INSTANCE.toBytes(gp.getByteCount()));
+            // Write as long_array
+            put.add(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES, PhoenixDatabaseMetaData.GUIDE_POSTS_ROW_COUNT_BYTES,
+                    timeStamp, PLong.INSTANCE.toBytes(gp.getRowCount()));
         }
         // Add our empty column value so queries behave correctly
-        put.add(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES, QueryConstants.EMPTY_COLUMN_BYTES,
-                timeStamp, ByteUtil.EMPTY_BYTE_ARRAY);
+        put.add(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES, QueryConstants.EMPTY_COLUMN_BYTES, timeStamp,
+                ByteUtil.EMPTY_BYTE_ARRAY);
         mutations.add(put);
     }
 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/223a1484/phoenix-core/src/test/java/org/apache/phoenix/util/TestUtil.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/util/TestUtil.java b/phoenix-core/src/test/java/org/apache/phoenix/util/TestUtil.java
index 8c55f1b..b0d55e7 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/util/TestUtil.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/util/TestUtil.java
@@ -37,6 +37,7 @@ import java.sql.SQLException;
 import java.sql.Statement;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.List;
 import java.util.Properties;
 
@@ -79,6 +80,9 @@ import org.apache.phoenix.query.QueryConstants;
 import org.apache.phoenix.schema.PColumn;
 import org.apache.phoenix.schema.types.PDataType;
 import org.apache.phoenix.schema.RowKeyValueAccessor;
+import org.apache.phoenix.schema.TableRef;
+import org.apache.phoenix.schema.stats.GuidePostsInfo;
+import org.apache.phoenix.schema.stats.PTableStats;
 import org.apache.phoenix.schema.tuple.Tuple;
 
 import com.google.common.collect.Lists;
@@ -489,7 +493,38 @@ public class TestUtil {
         List<KeyRange> keyRanges = pstmt.getQueryPlan().getSplits();
         return keyRanges;
     }
-    
+
+    public static Collection<GuidePostsInfo> getGuidePostsList(Connection conn, String tableName) throws SQLException {
+        return getGuidePostsList(conn, tableName, null, null, null, null);
+    }
+
+    public static Collection<GuidePostsInfo> getGuidePostsList(Connection conn, String tableName, String where)
+            throws SQLException {
+        return getGuidePostsList(conn, tableName, null, null, null, where);
+    }
+
+    public static Collection<GuidePostsInfo> getGuidePostsList(Connection conn, String tableName, String pkCol,
+            byte[] lowerRange, byte[] upperRange, String whereClauseSuffix) throws SQLException {
+        String whereClauseStart = (lowerRange == null && upperRange == null ? ""
+                : " WHERE "
+                        + ((lowerRange != null ? (pkCol + " >= ? " + (upperRange != null ? " AND " : "")) : "") + (upperRange != null ? (pkCol + " < ?")
+                                : "")));
+        String whereClause = whereClauseSuffix == null ? whereClauseStart
+                : whereClauseStart.length() == 0 ? (" WHERE " + whereClauseSuffix) : (" AND " + whereClauseSuffix);
+        String query = "SELECT /*+ NO_INDEX */ COUNT(*) FROM " + tableName + whereClause;
+        PhoenixPreparedStatement pstmt = conn.prepareStatement(query).unwrap(PhoenixPreparedStatement.class);
+        if (lowerRange != null) {
+            pstmt.setBytes(1, lowerRange);
+        }
+        if (upperRange != null) {
+            pstmt.setBytes(lowerRange != null ? 2 : 1, upperRange);
+        }
+        pstmt.execute();
+        TableRef tableRef = pstmt.getQueryPlan().getTableRef();
+        PTableStats tableStats = tableRef.getTable().getTableStats();
+        return tableStats.getGuidePosts().values();
+    }
+
     public static List<KeyRange> getSplits(Connection conn, byte[] lowerRange, byte[] upperRange) throws SQLException {
         return getSplits(conn, STABLE_NAME, STABLE_PK_NAME, lowerRange, upperRange, null);
     }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/223a1484/phoenix-protocol/src/main/PGuidePosts.proto
----------------------------------------------------------------------
diff --git a/phoenix-protocol/src/main/PGuidePosts.proto b/phoenix-protocol/src/main/PGuidePosts.proto
new file mode 100644
index 0000000..1550391
--- /dev/null
+++ b/phoenix-protocol/src/main/PGuidePosts.proto
@@ -0,0 +1,10 @@
+option java_package = "org.apache.phoenix.coprocessor.generated";
+option java_outer_classname = "PGuidePostsProtos";
+option java_generic_services = true;
+option java_generate_equals_and_hash = true;
+option optimize_for = SPEED;
+message PGuidePosts {
+  repeated bytes guidePosts = 1;
+  optional int64 byteCount = 2;
+  optional int64 rowCount = 3;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/phoenix/blob/223a1484/phoenix-protocol/src/main/PTable.proto
----------------------------------------------------------------------
diff --git a/phoenix-protocol/src/main/PTable.proto b/phoenix-protocol/src/main/PTable.proto
index 772d51d..03f63d5 100644
--- a/phoenix-protocol/src/main/PTable.proto
+++ b/phoenix-protocol/src/main/PTable.proto
@@ -24,6 +24,8 @@ option java_generic_services = true;
 option java_generate_equals_and_hash = true;
 option optimize_for = SPEED;
 
+import "PGuidePosts.proto";
+
 enum PTableType {
   SYSTEM = 0;
   USER = 1;
@@ -52,6 +54,7 @@ message PTableStats {
   optional int64 guidePostsByteCount = 3;
   optional int64 keyBytesCount = 4;
   optional int32 guidePostsCount = 5;
+  optional PGuidePosts pGuidePosts = 6;
 }
 
 message PTable {


[2/2] phoenix git commit: Phoenix-1453 - Collect row counts per region in stats table (Ramkrishna)

Posted by ra...@apache.org.
Phoenix-1453 - Collect row counts per region in stats table (Ramkrishna)


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

Branch: refs/heads/master
Commit: 223a1484097f645014ca0fd41ce29b2a191ef174
Parents: ddcb591
Author: Ramkrishna <ra...@intel.com>
Authored: Wed Jan 14 09:37:42 2015 +0530
Committer: Ramkrishna <ra...@intel.com>
Committed: Wed Jan 14 09:37:42 2015 +0530

----------------------------------------------------------------------
 .../end2end/StatsCollectorAbstractIT.java       |  77 ++
 .../phoenix/end2end/StatsCollectorIT.java       | 108 +--
 .../StatsCollectorWithSplitsAndMultiCFIT.java   | 254 +++++++
 .../phoenix/coprocessor/MetaDataProtocol.java   |   2 +-
 .../generated/PGuidePostsProtos.java            | 732 +++++++++++++++++++
 .../coprocessor/generated/PTableProtos.java     | 259 ++++++-
 .../org/apache/phoenix/execute/ScanPlan.java    |   3 +-
 .../phoenix/jdbc/PhoenixDatabaseMetaData.java   |   2 +
 .../query/ConnectionQueryServicesImpl.java      |   9 +-
 .../query/ConnectionlessQueryServicesImpl.java  |   1 -
 .../apache/phoenix/query/QueryConstants.java    |   2 +
 .../org/apache/phoenix/schema/PTableImpl.java   |  24 +-
 .../phoenix/schema/stats/GuidePostsInfo.java    | 174 +++--
 .../schema/stats/StatisticsCollector.java       |  97 +--
 .../phoenix/schema/stats/StatisticsScanner.java |  29 +-
 .../phoenix/schema/stats/StatisticsUtil.java    |  51 +-
 .../phoenix/schema/stats/StatisticsWriter.java  |  98 ++-
 .../java/org/apache/phoenix/util/TestUtil.java  |  37 +-
 phoenix-protocol/src/main/PGuidePosts.proto     |  10 +
 phoenix-protocol/src/main/PTable.proto          |   3 +
 20 files changed, 1679 insertions(+), 293 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/223a1484/phoenix-core/src/it/java/org/apache/phoenix/end2end/StatsCollectorAbstractIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/StatsCollectorAbstractIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/StatsCollectorAbstractIT.java
new file mode 100644
index 0000000..ab337d6
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/StatsCollectorAbstractIT.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end;
+
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Map;
+
+import org.apache.hadoop.hbase.client.HBaseAdmin;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.phoenix.jdbc.PhoenixConnection;
+import org.apache.phoenix.query.ConnectionQueryServices;
+import org.apache.phoenix.query.QueryServices;
+import org.apache.phoenix.util.ReadOnlyProps;
+import org.junit.BeforeClass;
+import org.junit.experimental.categories.Category;
+
+import com.google.common.collect.Maps;
+@Category(NeedsOwnMiniClusterTest.class)
+public abstract class StatsCollectorAbstractIT extends BaseOwnClusterHBaseManagedTimeIT {
+    protected static final String STATS_TEST_TABLE_NAME = "S";
+    protected static final String STATS_TEST_TABLE_NAME_NEW = "S_NEW";
+    protected static final byte[] STATS_TEST_TABLE_BYTES = Bytes.toBytes(STATS_TEST_TABLE_NAME);
+    protected static final byte[] STATS_TEST_TABLE_BYTES_NEW = Bytes.toBytes(STATS_TEST_TABLE_NAME_NEW);
+
+    @BeforeClass
+    public static void doSetup() throws Exception {
+        Map<String,String> props = Maps.newHashMapWithExpectedSize(3);
+        // Must update config before starting server
+        props.put(QueryServices.STATS_GUIDEPOST_WIDTH_BYTES_ATTRIB, Long.toString(20));
+        props.put(QueryServices.EXPLAIN_CHUNK_COUNT_ATTRIB, Boolean.TRUE.toString());
+        setUpTestDriver(new ReadOnlyProps(props.entrySet().iterator()));
+    }
+    
+    protected void splitTable(Connection conn, byte[] splitPoint, byte[] tabName) throws IOException, InterruptedException, SQLException {
+        ConnectionQueryServices services = conn.unwrap(PhoenixConnection.class).getQueryServices();
+        int nRegionsNow = services.getAllTableRegions(tabName).size();
+        HBaseAdmin admin = services.getAdmin();
+        try {
+            admin.split(tabName, splitPoint);
+            int nTries = 0;
+            int nRegions;
+            do {
+                Thread.sleep(2000);
+                services.clearTableRegionCache(tabName);
+                nRegions = services.getAllTableRegions(tabName).size();
+                nTries++;
+            } while (nRegions == nRegionsNow && nTries < 10);
+            if (nRegions == nRegionsNow) {
+                fail();
+            }
+            // FIXME: I see the commit of the stats finishing before this with a lower timestamp that the scan timestamp,
+            // yet without this sleep, the query finds the old data. Seems like an HBase bug and a potentially serious one.
+            Thread.sleep(8000);
+        } finally {
+            admin.close();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/223a1484/phoenix-core/src/it/java/org/apache/phoenix/end2end/StatsCollectorIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/StatsCollectorIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/StatsCollectorIT.java
index 8b3bdee..6d3eca5 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/StatsCollectorIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/StatsCollectorIT.java
@@ -20,10 +20,8 @@ package org.apache.phoenix.end2end;
 import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
 import static org.apache.phoenix.util.TestUtil.getAllSplits;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 
 import java.io.IOException;
 import java.sql.Array;
@@ -37,27 +35,22 @@ import java.util.Map;
 import java.util.Properties;
 
 import org.apache.hadoop.hbase.HColumnDescriptor;
-import org.apache.hadoop.hbase.HRegionLocation;
 import org.apache.hadoop.hbase.client.HBaseAdmin;
-import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.phoenix.jdbc.PhoenixConnection;
 import org.apache.phoenix.query.ConnectionQueryServices;
 import org.apache.phoenix.query.KeyRange;
 import org.apache.phoenix.query.QueryServices;
 import org.apache.phoenix.query.QueryServicesOptions;
 import org.apache.phoenix.util.PropertiesUtil;
-import org.apache.phoenix.util.QueryUtil;
 import org.apache.phoenix.util.ReadOnlyProps;
-import org.apache.phoenix.util.TestUtil;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
 import com.google.common.collect.Maps;
 
 
-public class StatsCollectorIT extends BaseOwnClusterHBaseManagedTimeIT {
+public class StatsCollectorIT extends StatsCollectorAbstractIT {
     private static final String STATS_TEST_TABLE_NAME = "S";
-    private static final byte[] STATS_TEST_TABLE_BYTES = Bytes.toBytes(STATS_TEST_TABLE_NAME);
         
     @BeforeClass
     public static void doSetup() throws Exception {
@@ -308,103 +301,4 @@ public class StatsCollectorIT extends BaseOwnClusterHBaseManagedTimeIT {
         assertEquals(nRows/2+1, keyRanges.size());
         
     }
-
-
-    private void splitTable(Connection conn, byte[] splitPoint) throws IOException, InterruptedException, SQLException {
-        ConnectionQueryServices services = conn.unwrap(PhoenixConnection.class).getQueryServices();
-        int nRegionsNow = services.getAllTableRegions(STATS_TEST_TABLE_BYTES).size();
-        HBaseAdmin admin = services.getAdmin();
-        try {
-            admin.split(STATS_TEST_TABLE_BYTES, splitPoint);
-            int nTries = 0;
-            int nRegions;
-            do {
-                Thread.sleep(2000);
-                services.clearTableRegionCache(STATS_TEST_TABLE_BYTES);
-                nRegions = services.getAllTableRegions(STATS_TEST_TABLE_BYTES).size();
-                nTries++;
-            } while (nRegions == nRegionsNow && nTries < 10);
-            if (nRegions == nRegionsNow) {
-                fail();
-            }
-            // FIXME: I see the commit of the stats finishing before this with a lower timestamp that the scan timestamp,
-            // yet without this sleep, the query finds the old data. Seems like an HBase bug and a potentially serious one.
-            Thread.sleep(8000);
-        } finally {
-            admin.close();
-        }
-    }
-    
-    @Test
-    public void testSplitUpdatesStats() throws Exception {
-        int nRows = 10;
-        Connection conn;
-        PreparedStatement stmt;
-        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        conn = DriverManager.getConnection(getUrl(), props);
-        conn.createStatement().execute("CREATE TABLE " + STATS_TEST_TABLE_NAME + "(k VARCHAR PRIMARY KEY, v INTEGER) " + HColumnDescriptor.KEEP_DELETED_CELLS + "=" + Boolean.FALSE);
-        stmt = conn.prepareStatement("UPSERT INTO " + STATS_TEST_TABLE_NAME + " VALUES(?,?)");
-        for (int i = 0; i < nRows; i++) {
-            stmt.setString(1, Character.toString((char) ('a' + i)));
-            stmt.setInt(2, i);
-            stmt.executeUpdate();
-        }
-        conn.commit();
-        
-        ResultSet rs;
-        TestUtil.analyzeTable(conn, STATS_TEST_TABLE_NAME);
-        List<KeyRange>keyRanges = getAllSplits(conn, STATS_TEST_TABLE_NAME);
-        assertEquals(nRows+1, keyRanges.size());
-        rs = conn.createStatement().executeQuery("EXPLAIN SELECT * FROM " + STATS_TEST_TABLE_NAME);
-        assertEquals("CLIENT " + (nRows+1) + "-CHUNK " + "PARALLEL 1-WAY FULL SCAN OVER " + STATS_TEST_TABLE_NAME, QueryUtil.getExplainPlan(rs));
-
-        ConnectionQueryServices services = conn.unwrap(PhoenixConnection.class).getQueryServices();
-        List<HRegionLocation> regions = services.getAllTableRegions(STATS_TEST_TABLE_BYTES);
-        assertEquals(1, regions.size());
- 
-        rs = conn.createStatement().executeQuery("SELECT GUIDE_POSTS_COUNT, REGION_NAME FROM SYSTEM.STATS WHERE PHYSICAL_NAME='"+STATS_TEST_TABLE_NAME+"' AND REGION_NAME IS NOT NULL");
-        assertTrue(rs.next());
-        assertEquals(nRows, rs.getLong(1));
-        assertEquals(regions.get(0).getRegionInfo().getRegionNameAsString(), rs.getString(2));
-        assertFalse(rs.next());
-
-        byte[] midPoint = Bytes.toBytes(Character.toString((char) ('a' + (nRows/2))));
-        splitTable(conn, midPoint);
-        
-        keyRanges = getAllSplits(conn, STATS_TEST_TABLE_NAME);
-        assertEquals(nRows+1, keyRanges.size()); // Same number as before because split was at guidepost
-        
-        regions = services.getAllTableRegions(STATS_TEST_TABLE_BYTES);
-        assertEquals(2, regions.size());
-        rs = conn.createStatement().executeQuery("SELECT GUIDE_POSTS_COUNT, REGION_NAME FROM SYSTEM.STATS WHERE PHYSICAL_NAME='"+STATS_TEST_TABLE_NAME+"' AND REGION_NAME IS NOT NULL");
-        assertTrue(rs.next());
-        assertEquals(nRows/2, rs.getLong(1));
-        assertEquals(regions.get(0).getRegionInfo().getRegionNameAsString(), rs.getString(2));
-        assertTrue(rs.next());
-        assertEquals(nRows/2 - 1, rs.getLong(1));
-        assertEquals(regions.get(1).getRegionInfo().getRegionNameAsString(), rs.getString(2));
-        assertFalse(rs.next());
-
-        byte[] midPoint2 = Bytes.toBytes("cj");
-        splitTable(conn, midPoint2);
-        
-        keyRanges = getAllSplits(conn, STATS_TEST_TABLE_NAME);
-        assertEquals(nRows+2, keyRanges.size()); // One extra due to split between guideposts
-        
-        regions = services.getAllTableRegions(STATS_TEST_TABLE_BYTES);
-        assertEquals(3, regions.size());
-        rs = conn.createStatement().executeQuery("SELECT GUIDE_POSTS_COUNT, REGION_NAME FROM SYSTEM.STATS WHERE PHYSICAL_NAME='"+STATS_TEST_TABLE_NAME+"' AND REGION_NAME IS NOT NULL");
-        assertTrue(rs.next());
-        assertEquals(3, rs.getLong(1));
-        assertEquals(regions.get(0).getRegionInfo().getRegionNameAsString(), rs.getString(2));
-        assertTrue(rs.next());
-        assertEquals(2, rs.getLong(1));
-        assertEquals(regions.get(1).getRegionInfo().getRegionNameAsString(), rs.getString(2));
-        assertTrue(rs.next());
-        assertEquals(nRows/2 - 1, rs.getLong(1));
-        assertEquals(regions.get(2).getRegionInfo().getRegionNameAsString(), rs.getString(2));
-        assertFalse(rs.next());
-        
-        conn.close();
-    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/223a1484/phoenix-core/src/it/java/org/apache/phoenix/end2end/StatsCollectorWithSplitsAndMultiCFIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/StatsCollectorWithSplitsAndMultiCFIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/StatsCollectorWithSplitsAndMultiCFIT.java
new file mode 100644
index 0000000..e1497c1
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/StatsCollectorWithSplitsAndMultiCFIT.java
@@ -0,0 +1,254 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end;
+
+import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
+import static org.apache.phoenix.util.TestUtil.getAllSplits;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.hadoop.hbase.HColumnDescriptor;
+import org.apache.hadoop.hbase.HRegionLocation;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.phoenix.jdbc.PhoenixConnection;
+import org.apache.phoenix.query.ConnectionQueryServices;
+import org.apache.phoenix.query.KeyRange;
+import org.apache.phoenix.query.QueryServices;
+import org.apache.phoenix.schema.stats.GuidePostsInfo;
+import org.apache.phoenix.util.PropertiesUtil;
+import org.apache.phoenix.util.QueryUtil;
+import org.apache.phoenix.util.ReadOnlyProps;
+import org.apache.phoenix.util.TestUtil;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.google.common.collect.Maps;
+
+public class StatsCollectorWithSplitsAndMultiCFIT extends StatsCollectorAbstractIT {
+    private static final String STATS_TEST_TABLE_NAME_NEW = "S_NEW";
+    private static final byte[] STATS_TEST_TABLE_NEW_BYTES = Bytes.toBytes(STATS_TEST_TABLE_NAME_NEW);
+
+    @BeforeClass
+    public static void doSetup() throws Exception {
+        Map<String, String> props = Maps.newHashMapWithExpectedSize(3);
+        // Must update config before starting server
+        props.put(QueryServices.STATS_GUIDEPOST_WIDTH_BYTES_ATTRIB, Long.toString(1000));
+        props.put(QueryServices.EXPLAIN_CHUNK_COUNT_ATTRIB, Boolean.TRUE.toString());
+        setUpTestDriver(new ReadOnlyProps(props.entrySet().iterator()));
+    }
+
+    @Test
+    public void testWithMultiCF() throws Exception {
+        int nRows = 20;
+        Connection conn;
+        PreparedStatement stmt;
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        conn = DriverManager.getConnection(getUrl(), props);
+        conn.createStatement().execute(
+                "CREATE TABLE " + STATS_TEST_TABLE_NAME_NEW
+                        + "(k VARCHAR PRIMARY KEY, a.v INTEGER, b.v INTEGER, c.v INTEGER NULL, d.v INTEGER NULL) ");
+        stmt = conn.prepareStatement("UPSERT INTO " + STATS_TEST_TABLE_NAME_NEW + " VALUES(?,?, ?, ?, ?)");
+        byte[] val = new byte[250];
+        for (int i = 0; i < nRows; i++) {
+            stmt.setString(1, Character.toString((char)('a' + i)) + Bytes.toString(val));
+            stmt.setInt(2, i);
+            stmt.setInt(3, i);
+            stmt.setInt(4, i);
+            stmt.setInt(5, i);
+            stmt.executeUpdate();
+        }
+        conn.commit();
+        stmt = conn.prepareStatement("UPSERT INTO " + STATS_TEST_TABLE_NAME_NEW + "(k, c.v, d.v) VALUES(?,?,?)");
+        for (int i = 0; i < 5; i++) {
+            stmt.setString(1, Character.toString((char)('a' + 'z' + i)) + Bytes.toString(val));
+            stmt.setInt(2, i);
+            stmt.setInt(3, i);
+            stmt.executeUpdate();
+        }
+        conn.commit();
+
+        ResultSet rs;
+        TestUtil.analyzeTable(conn, STATS_TEST_TABLE_NAME_NEW);
+        List<KeyRange> keyRanges = getAllSplits(conn, STATS_TEST_TABLE_NAME_NEW);
+        assertEquals(12, keyRanges.size());
+        rs = conn.createStatement().executeQuery("EXPLAIN SELECT * FROM " + STATS_TEST_TABLE_NAME_NEW);
+        assertEquals("CLIENT " + (12) + "-CHUNK " + "PARALLEL 1-WAY FULL SCAN OVER " + STATS_TEST_TABLE_NAME_NEW,
+                QueryUtil.getExplainPlan(rs));
+
+        ConnectionQueryServices services = conn.unwrap(PhoenixConnection.class).getQueryServices();
+        List<HRegionLocation> regions = services.getAllTableRegions(STATS_TEST_TABLE_NEW_BYTES);
+        assertEquals(1, regions.size());
+
+        rs = conn.createStatement().executeQuery(
+                "SELECT GUIDE_POSTS_COUNT, REGION_NAME, GUIDE_POSTS_ROW_COUNT FROM SYSTEM.STATS WHERE PHYSICAL_NAME='"
+                        + STATS_TEST_TABLE_NAME_NEW + "' AND REGION_NAME IS NOT NULL");
+        assertTrue(rs.next());
+        assertEquals(11, (rs.getLong(1)));
+        assertEquals(regions.get(0).getRegionInfo().getRegionNameAsString(), rs.getString(2));
+        // I get 25 here because for A there is an entry added with the default col
+        assertEquals(25, rs.getLong(3));
+        assertTrue(rs.next());
+        assertEquals(5, (rs.getLong(1)));
+        assertEquals(regions.get(0).getRegionInfo().getRegionNameAsString(), rs.getString(2));
+        // for b the count is lesser by 5
+        assertEquals(20, rs.getLong(3));
+        assertTrue(rs.next());
+        assertEquals(6, (rs.getLong(1)));
+        assertEquals(regions.get(0).getRegionInfo().getRegionNameAsString(), rs.getString(2));
+        assertEquals(25, rs.getLong(3));
+        assertTrue(rs.next());
+        assertEquals(6, (rs.getLong(1)));
+        assertEquals(regions.get(0).getRegionInfo().getRegionNameAsString(), rs.getString(2));
+        assertEquals(25, rs.getLong(3));
+        assertFalse(rs.next());
+        Collection<GuidePostsInfo> infos = TestUtil.getGuidePostsList(conn, STATS_TEST_TABLE_NAME_NEW);
+        long[] rowCountArr = new long[]{25, 20, 25, 25};
+        // CF A alone has double the bytecount because it has column qualifier A and column qualifier _0
+        long[] byteCountArr = new long[]{12120, 5540, 6652, 6652};
+        int i = 0;
+        for(GuidePostsInfo info : infos) {
+            assertRowCountAndByteCount(info, rowCountArr[i], byteCountArr[i]);
+            i++;
+        }
+    }
+
+    protected void assertRowCountAndByteCount(GuidePostsInfo info, long rowCount, long byteCount) {
+        assertEquals(info.getRowCount(), rowCount);
+        assertEquals(info.getByteCount(), byteCount);
+    }
+
+    @Test
+    public void testSplitUpdatesStats() throws Exception {
+        int nRows = 20;
+        Connection conn;
+        PreparedStatement stmt;
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        conn = DriverManager.getConnection(getUrl(), props);
+        conn.createStatement().execute(
+                "CREATE TABLE " + STATS_TEST_TABLE_NAME + "(k VARCHAR PRIMARY KEY, v INTEGER) "
+                        + HColumnDescriptor.KEEP_DELETED_CELLS + "=" + Boolean.FALSE);
+        stmt = conn.prepareStatement("UPSERT INTO " + STATS_TEST_TABLE_NAME + " VALUES(?,?)");
+        byte[] val = new byte[250];
+        for (int i = 0; i < nRows; i++) {
+            stmt.setString(1, Character.toString((char)('a' + i)) + Bytes.toString(val));
+            stmt.setInt(2, i);
+            stmt.executeUpdate();
+        }
+        conn.commit();
+
+        ResultSet rs;
+        TestUtil.analyzeTable(conn, STATS_TEST_TABLE_NAME);
+        Collection<GuidePostsInfo> infos = TestUtil.getGuidePostsList(conn, STATS_TEST_TABLE_NAME);
+        for (GuidePostsInfo info : infos) {
+            assertEquals(20, info.getRowCount());
+            assertEquals(11020, info.getByteCount());
+            break;
+        }
+        List<KeyRange> keyRanges = getAllSplits(conn, STATS_TEST_TABLE_NAME);
+        assertEquals((nRows / 2) + 1, keyRanges.size());
+        rs = conn.createStatement().executeQuery("EXPLAIN SELECT * FROM " + STATS_TEST_TABLE_NAME);
+        assertEquals("CLIENT " + ((nRows / 2) + 1) + "-CHUNK " + "PARALLEL 1-WAY FULL SCAN OVER "
+                + STATS_TEST_TABLE_NAME, QueryUtil.getExplainPlan(rs));
+
+        ConnectionQueryServices services = conn.unwrap(PhoenixConnection.class).getQueryServices();
+        List<HRegionLocation> regions = services.getAllTableRegions(STATS_TEST_TABLE_BYTES);
+        assertEquals(1, regions.size());
+
+        rs = conn.createStatement().executeQuery(
+                "SELECT GUIDE_POSTS_COUNT, REGION_NAME, GUIDE_POSTS_ROW_COUNT FROM SYSTEM.STATS WHERE PHYSICAL_NAME='"
+                        + STATS_TEST_TABLE_NAME + "' AND REGION_NAME IS NOT NULL");
+        assertTrue(rs.next());
+        assertEquals(nRows / 2, (rs.getLong(1)));
+        assertEquals(regions.get(0).getRegionInfo().getRegionNameAsString(), rs.getString(2));
+        //PhoenixArray arr = (PhoenixArray)rs.getArray(3);
+        assertEquals(20, rs.getLong(3));
+        assertFalse(rs.next());
+
+        byte[] midPoint = Bytes.toBytes(Character.toString((char)('a' + (5))));
+        splitTable(conn, midPoint, STATS_TEST_TABLE_BYTES);
+
+        keyRanges = getAllSplits(conn, STATS_TEST_TABLE_NAME);
+        assertEquals(12, keyRanges.size()); // Same number as before because split was at guidepost
+
+        regions = services.getAllTableRegions(STATS_TEST_TABLE_BYTES);
+        assertEquals(2, regions.size());
+        rs = conn.createStatement().executeQuery(
+                "SELECT GUIDE_POSTS_COUNT, REGION_NAME, "
+                        + "GUIDE_POSTS_ROW_COUNT FROM SYSTEM.STATS WHERE PHYSICAL_NAME='" + STATS_TEST_TABLE_NAME
+                        + "' AND REGION_NAME IS NOT NULL");
+        assertTrue(rs.next());
+        assertEquals(2, rs.getLong(1));
+        assertEquals(regions.get(0).getRegionInfo().getRegionNameAsString(), rs.getString(2));
+        assertEquals(6, rs.getLong(3));
+        assertTrue(rs.next());
+        assertEquals(8, rs.getLong(1));
+        assertEquals(regions.get(1).getRegionInfo().getRegionNameAsString(), rs.getString(2));
+        assertEquals(14, rs.getLong(3));
+        assertFalse(rs.next());
+
+        byte[] midPoint2 = Bytes.toBytes("cj");
+        splitTable(conn, midPoint2, STATS_TEST_TABLE_BYTES);
+
+        keyRanges = getAllSplits(conn, STATS_TEST_TABLE_NAME);
+        assertEquals((nRows / 2) + 3, keyRanges.size()); // One extra due to split between guideposts
+
+        regions = services.getAllTableRegions(STATS_TEST_TABLE_BYTES);
+        assertEquals(3, regions.size());
+        rs = conn.createStatement().executeQuery(
+                "SELECT GUIDE_POSTS_COUNT, REGION_NAME, GUIDE_POSTS_ROW_COUNT FROM SYSTEM.STATS WHERE PHYSICAL_NAME='"
+                        + STATS_TEST_TABLE_NAME + "' AND REGION_NAME IS NOT NULL");
+        assertTrue(rs.next());
+        assertEquals(1, rs.getLong(1));
+        assertEquals(regions.get(0).getRegionInfo().getRegionNameAsString(), rs.getString(2));
+        assertEquals(4, rs.getLong(3));
+        assertTrue(rs.next());
+        assertEquals(1, rs.getLong(1));
+        assertEquals(regions.get(1).getRegionInfo().getRegionNameAsString(), rs.getString(2));
+        assertEquals(3, rs.getLong(3));
+        assertTrue(rs.next());
+        assertEquals(8, rs.getLong(1));
+        assertEquals(regions.get(2).getRegionInfo().getRegionNameAsString(), rs.getString(2));
+        assertEquals(14, rs.getLong(3));
+        assertFalse(rs.next());
+        rs = conn.createStatement().executeQuery("EXPLAIN SELECT * FROM " + STATS_TEST_TABLE_NAME);
+        assertEquals("CLIENT " + ((nRows / 2) + 3) + "-CHUNK " + "PARALLEL 1-WAY FULL SCAN OVER "
+                + STATS_TEST_TABLE_NAME, QueryUtil.getExplainPlan(rs));
+
+        TestUtil.analyzeTable(conn, STATS_TEST_TABLE_NAME);
+        rs = conn.createStatement().executeQuery("EXPLAIN SELECT * FROM " + STATS_TEST_TABLE_NAME);
+        assertEquals("CLIENT " + ((nRows / 2) + 2) + "-CHUNK " + "PARALLEL 1-WAY FULL SCAN OVER "
+                + STATS_TEST_TABLE_NAME, QueryUtil.getExplainPlan(rs));
+        infos = TestUtil.getGuidePostsList(conn, STATS_TEST_TABLE_NAME);
+        for (GuidePostsInfo info : infos) {
+            assertEquals(20, info.getRowCount());
+            assertEquals(9918, info.getByteCount());
+            break;
+        }
+        conn.close();
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/223a1484/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataProtocol.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataProtocol.java b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataProtocol.java
index 0429635..d04b5c8 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataProtocol.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataProtocol.java
@@ -61,7 +61,7 @@ public abstract class MetaDataProtocol extends MetaDataService {
     public static final long MIN_TABLE_TIMESTAMP = 0;
 
     // Incremented from 3 to 4 to salt the sequence table in 3.2/4.2
-    public static final long MIN_SYSTEM_TABLE_TIMESTAMP = MIN_TABLE_TIMESTAMP + 5;
+    public static final long MIN_SYSTEM_TABLE_TIMESTAMP = MIN_TABLE_TIMESTAMP + 6;
     public static final int DEFAULT_MAX_META_DATA_VERSIONS = 1000;
     public static final int DEFAULT_MAX_STAT_DATA_VERSIONS = 3;
     public static final boolean DEFAULT_META_DATA_KEEP_DELETED_CELLS = true;

http://git-wip-us.apache.org/repos/asf/phoenix/blob/223a1484/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PGuidePostsProtos.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PGuidePostsProtos.java b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PGuidePostsProtos.java
new file mode 100644
index 0000000..9f96530
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PGuidePostsProtos.java
@@ -0,0 +1,732 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: PGuidePosts.proto
+
+package org.apache.phoenix.coprocessor.generated;
+
+public final class PGuidePostsProtos {
+  private PGuidePostsProtos() {}
+  public static void registerAllExtensions(
+      com.google.protobuf.ExtensionRegistry registry) {
+  }
+  public interface PGuidePostsOrBuilder
+      extends com.google.protobuf.MessageOrBuilder {
+
+    // repeated bytes guidePosts = 1;
+    /**
+     * <code>repeated bytes guidePosts = 1;</code>
+     */
+    java.util.List<com.google.protobuf.ByteString> getGuidePostsList();
+    /**
+     * <code>repeated bytes guidePosts = 1;</code>
+     */
+    int getGuidePostsCount();
+    /**
+     * <code>repeated bytes guidePosts = 1;</code>
+     */
+    com.google.protobuf.ByteString getGuidePosts(int index);
+
+    // optional int64 byteCount = 2;
+    /**
+     * <code>optional int64 byteCount = 2;</code>
+     */
+    boolean hasByteCount();
+    /**
+     * <code>optional int64 byteCount = 2;</code>
+     */
+    long getByteCount();
+
+    // optional int64 rowCount = 3;
+    /**
+     * <code>optional int64 rowCount = 3;</code>
+     */
+    boolean hasRowCount();
+    /**
+     * <code>optional int64 rowCount = 3;</code>
+     */
+    long getRowCount();
+  }
+  /**
+   * Protobuf type {@code PGuidePosts}
+   */
+  public static final class PGuidePosts extends
+      com.google.protobuf.GeneratedMessage
+      implements PGuidePostsOrBuilder {
+    // Use PGuidePosts.newBuilder() to construct.
+    private PGuidePosts(com.google.protobuf.GeneratedMessage.Builder<?> builder) {
+      super(builder);
+      this.unknownFields = builder.getUnknownFields();
+    }
+    private PGuidePosts(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); }
+
+    private static final PGuidePosts defaultInstance;
+    public static PGuidePosts getDefaultInstance() {
+      return defaultInstance;
+    }
+
+    public PGuidePosts getDefaultInstanceForType() {
+      return defaultInstance;
+    }
+
+    private final com.google.protobuf.UnknownFieldSet unknownFields;
+    @java.lang.Override
+    public final com.google.protobuf.UnknownFieldSet
+        getUnknownFields() {
+      return this.unknownFields;
+    }
+    private PGuidePosts(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      initFields();
+      int mutable_bitField0_ = 0;
+      com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+          com.google.protobuf.UnknownFieldSet.newBuilder();
+      try {
+        boolean done = false;
+        while (!done) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              done = true;
+              break;
+            default: {
+              if (!parseUnknownField(input, unknownFields,
+                                     extensionRegistry, tag)) {
+                done = true;
+              }
+              break;
+            }
+            case 10: {
+              if (!((mutable_bitField0_ & 0x00000001) == 0x00000001)) {
+                guidePosts_ = new java.util.ArrayList<com.google.protobuf.ByteString>();
+                mutable_bitField0_ |= 0x00000001;
+              }
+              guidePosts_.add(input.readBytes());
+              break;
+            }
+            case 16: {
+              bitField0_ |= 0x00000001;
+              byteCount_ = input.readInt64();
+              break;
+            }
+            case 24: {
+              bitField0_ |= 0x00000002;
+              rowCount_ = input.readInt64();
+              break;
+            }
+          }
+        }
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.setUnfinishedMessage(this);
+      } catch (java.io.IOException e) {
+        throw new com.google.protobuf.InvalidProtocolBufferException(
+            e.getMessage()).setUnfinishedMessage(this);
+      } finally {
+        if (((mutable_bitField0_ & 0x00000001) == 0x00000001)) {
+          guidePosts_ = java.util.Collections.unmodifiableList(guidePosts_);
+        }
+        this.unknownFields = unknownFields.build();
+        makeExtensionsImmutable();
+      }
+    }
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.internal_static_PGuidePosts_descriptor;
+    }
+
+    protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.internal_static_PGuidePosts_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts.class, org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts.Builder.class);
+    }
+
+    public static com.google.protobuf.Parser<PGuidePosts> PARSER =
+        new com.google.protobuf.AbstractParser<PGuidePosts>() {
+      public PGuidePosts parsePartialFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        return new PGuidePosts(input, extensionRegistry);
+      }
+    };
+
+    @java.lang.Override
+    public com.google.protobuf.Parser<PGuidePosts> getParserForType() {
+      return PARSER;
+    }
+
+    private int bitField0_;
+    // repeated bytes guidePosts = 1;
+    public static final int GUIDEPOSTS_FIELD_NUMBER = 1;
+    private java.util.List<com.google.protobuf.ByteString> guidePosts_;
+    /**
+     * <code>repeated bytes guidePosts = 1;</code>
+     */
+    public java.util.List<com.google.protobuf.ByteString>
+        getGuidePostsList() {
+      return guidePosts_;
+    }
+    /**
+     * <code>repeated bytes guidePosts = 1;</code>
+     */
+    public int getGuidePostsCount() {
+      return guidePosts_.size();
+    }
+    /**
+     * <code>repeated bytes guidePosts = 1;</code>
+     */
+    public com.google.protobuf.ByteString getGuidePosts(int index) {
+      return guidePosts_.get(index);
+    }
+
+    // optional int64 byteCount = 2;
+    public static final int BYTECOUNT_FIELD_NUMBER = 2;
+    private long byteCount_;
+    /**
+     * <code>optional int64 byteCount = 2;</code>
+     */
+    public boolean hasByteCount() {
+      return ((bitField0_ & 0x00000001) == 0x00000001);
+    }
+    /**
+     * <code>optional int64 byteCount = 2;</code>
+     */
+    public long getByteCount() {
+      return byteCount_;
+    }
+
+    // optional int64 rowCount = 3;
+    public static final int ROWCOUNT_FIELD_NUMBER = 3;
+    private long rowCount_;
+    /**
+     * <code>optional int64 rowCount = 3;</code>
+     */
+    public boolean hasRowCount() {
+      return ((bitField0_ & 0x00000002) == 0x00000002);
+    }
+    /**
+     * <code>optional int64 rowCount = 3;</code>
+     */
+    public long getRowCount() {
+      return rowCount_;
+    }
+
+    private void initFields() {
+      guidePosts_ = java.util.Collections.emptyList();
+      byteCount_ = 0L;
+      rowCount_ = 0L;
+    }
+    private byte memoizedIsInitialized = -1;
+    public final boolean isInitialized() {
+      byte isInitialized = memoizedIsInitialized;
+      if (isInitialized != -1) return isInitialized == 1;
+
+      memoizedIsInitialized = 1;
+      return true;
+    }
+
+    public void writeTo(com.google.protobuf.CodedOutputStream output)
+                        throws java.io.IOException {
+      getSerializedSize();
+      for (int i = 0; i < guidePosts_.size(); i++) {
+        output.writeBytes(1, guidePosts_.get(i));
+      }
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        output.writeInt64(2, byteCount_);
+      }
+      if (((bitField0_ & 0x00000002) == 0x00000002)) {
+        output.writeInt64(3, rowCount_);
+      }
+      getUnknownFields().writeTo(output);
+    }
+
+    private int memoizedSerializedSize = -1;
+    public int getSerializedSize() {
+      int size = memoizedSerializedSize;
+      if (size != -1) return size;
+
+      size = 0;
+      {
+        int dataSize = 0;
+        for (int i = 0; i < guidePosts_.size(); i++) {
+          dataSize += com.google.protobuf.CodedOutputStream
+            .computeBytesSizeNoTag(guidePosts_.get(i));
+        }
+        size += dataSize;
+        size += 1 * getGuidePostsList().size();
+      }
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt64Size(2, byteCount_);
+      }
+      if (((bitField0_ & 0x00000002) == 0x00000002)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt64Size(3, rowCount_);
+      }
+      size += getUnknownFields().getSerializedSize();
+      memoizedSerializedSize = size;
+      return size;
+    }
+
+    private static final long serialVersionUID = 0L;
+    @java.lang.Override
+    protected java.lang.Object writeReplace()
+        throws java.io.ObjectStreamException {
+      return super.writeReplace();
+    }
+
+    @java.lang.Override
+    public boolean equals(final java.lang.Object obj) {
+      if (obj == this) {
+       return true;
+      }
+      if (!(obj instanceof org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts)) {
+        return super.equals(obj);
+      }
+      org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts other = (org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts) obj;
+
+      boolean result = true;
+      result = result && getGuidePostsList()
+          .equals(other.getGuidePostsList());
+      result = result && (hasByteCount() == other.hasByteCount());
+      if (hasByteCount()) {
+        result = result && (getByteCount()
+            == other.getByteCount());
+      }
+      result = result && (hasRowCount() == other.hasRowCount());
+      if (hasRowCount()) {
+        result = result && (getRowCount()
+            == other.getRowCount());
+      }
+      result = result &&
+          getUnknownFields().equals(other.getUnknownFields());
+      return result;
+    }
+
+    private int memoizedHashCode = 0;
+    @java.lang.Override
+    public int hashCode() {
+      if (memoizedHashCode != 0) {
+        return memoizedHashCode;
+      }
+      int hash = 41;
+      hash = (19 * hash) + getDescriptorForType().hashCode();
+      if (getGuidePostsCount() > 0) {
+        hash = (37 * hash) + GUIDEPOSTS_FIELD_NUMBER;
+        hash = (53 * hash) + getGuidePostsList().hashCode();
+      }
+      if (hasByteCount()) {
+        hash = (37 * hash) + BYTECOUNT_FIELD_NUMBER;
+        hash = (53 * hash) + hashLong(getByteCount());
+      }
+      if (hasRowCount()) {
+        hash = (37 * hash) + ROWCOUNT_FIELD_NUMBER;
+        hash = (53 * hash) + hashLong(getRowCount());
+      }
+      hash = (29 * hash) + getUnknownFields().hashCode();
+      memoizedHashCode = hash;
+      return hash;
+    }
+
+    public static org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts parseFrom(
+        com.google.protobuf.ByteString data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts parseFrom(
+        com.google.protobuf.ByteString data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts parseFrom(byte[] data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts parseFrom(
+        byte[] data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts parseFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input);
+    }
+    public static org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts parseFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input, extensionRegistry);
+    }
+    public static org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts parseDelimitedFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return PARSER.parseDelimitedFrom(input);
+    }
+    public static org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts parseDelimitedFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return PARSER.parseDelimitedFrom(input, extensionRegistry);
+    }
+    public static org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts parseFrom(
+        com.google.protobuf.CodedInputStream input)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input);
+    }
+    public static org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts parseFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input, extensionRegistry);
+    }
+
+    public static Builder newBuilder() { return Builder.create(); }
+    public Builder newBuilderForType() { return newBuilder(); }
+    public static Builder newBuilder(org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts prototype) {
+      return newBuilder().mergeFrom(prototype);
+    }
+    public Builder toBuilder() { return newBuilder(this); }
+
+    @java.lang.Override
+    protected Builder newBuilderForType(
+        com.google.protobuf.GeneratedMessage.BuilderParent parent) {
+      Builder builder = new Builder(parent);
+      return builder;
+    }
+    /**
+     * Protobuf type {@code PGuidePosts}
+     */
+    public static final class Builder extends
+        com.google.protobuf.GeneratedMessage.Builder<Builder>
+       implements org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePostsOrBuilder {
+      public static final com.google.protobuf.Descriptors.Descriptor
+          getDescriptor() {
+        return org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.internal_static_PGuidePosts_descriptor;
+      }
+
+      protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+          internalGetFieldAccessorTable() {
+        return org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.internal_static_PGuidePosts_fieldAccessorTable
+            .ensureFieldAccessorsInitialized(
+                org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts.class, org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts.Builder.class);
+      }
+
+      // Construct using org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts.newBuilder()
+      private Builder() {
+        maybeForceBuilderInitialization();
+      }
+
+      private Builder(
+          com.google.protobuf.GeneratedMessage.BuilderParent parent) {
+        super(parent);
+        maybeForceBuilderInitialization();
+      }
+      private void maybeForceBuilderInitialization() {
+        if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {
+        }
+      }
+      private static Builder create() {
+        return new Builder();
+      }
+
+      public Builder clear() {
+        super.clear();
+        guidePosts_ = java.util.Collections.emptyList();
+        bitField0_ = (bitField0_ & ~0x00000001);
+        byteCount_ = 0L;
+        bitField0_ = (bitField0_ & ~0x00000002);
+        rowCount_ = 0L;
+        bitField0_ = (bitField0_ & ~0x00000004);
+        return this;
+      }
+
+      public Builder clone() {
+        return create().mergeFrom(buildPartial());
+      }
+
+      public com.google.protobuf.Descriptors.Descriptor
+          getDescriptorForType() {
+        return org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.internal_static_PGuidePosts_descriptor;
+      }
+
+      public org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts getDefaultInstanceForType() {
+        return org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts.getDefaultInstance();
+      }
+
+      public org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts build() {
+        org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(result);
+        }
+        return result;
+      }
+
+      public org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts buildPartial() {
+        org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts result = new org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts(this);
+        int from_bitField0_ = bitField0_;
+        int to_bitField0_ = 0;
+        if (((bitField0_ & 0x00000001) == 0x00000001)) {
+          guidePosts_ = java.util.Collections.unmodifiableList(guidePosts_);
+          bitField0_ = (bitField0_ & ~0x00000001);
+        }
+        result.guidePosts_ = guidePosts_;
+        if (((from_bitField0_ & 0x00000002) == 0x00000002)) {
+          to_bitField0_ |= 0x00000001;
+        }
+        result.byteCount_ = byteCount_;
+        if (((from_bitField0_ & 0x00000004) == 0x00000004)) {
+          to_bitField0_ |= 0x00000002;
+        }
+        result.rowCount_ = rowCount_;
+        result.bitField0_ = to_bitField0_;
+        onBuilt();
+        return result;
+      }
+
+      public Builder mergeFrom(com.google.protobuf.Message other) {
+        if (other instanceof org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts) {
+          return mergeFrom((org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts)other);
+        } else {
+          super.mergeFrom(other);
+          return this;
+        }
+      }
+
+      public Builder mergeFrom(org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts other) {
+        if (other == org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts.getDefaultInstance()) return this;
+        if (!other.guidePosts_.isEmpty()) {
+          if (guidePosts_.isEmpty()) {
+            guidePosts_ = other.guidePosts_;
+            bitField0_ = (bitField0_ & ~0x00000001);
+          } else {
+            ensureGuidePostsIsMutable();
+            guidePosts_.addAll(other.guidePosts_);
+          }
+          onChanged();
+        }
+        if (other.hasByteCount()) {
+          setByteCount(other.getByteCount());
+        }
+        if (other.hasRowCount()) {
+          setRowCount(other.getRowCount());
+        }
+        this.mergeUnknownFields(other.getUnknownFields());
+        return this;
+      }
+
+      public final boolean isInitialized() {
+        return true;
+      }
+
+      public Builder mergeFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts parsedMessage = null;
+        try {
+          parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+        } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+          parsedMessage = (org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts) e.getUnfinishedMessage();
+          throw e;
+        } finally {
+          if (parsedMessage != null) {
+            mergeFrom(parsedMessage);
+          }
+        }
+        return this;
+      }
+      private int bitField0_;
+
+      // repeated bytes guidePosts = 1;
+      private java.util.List<com.google.protobuf.ByteString> guidePosts_ = java.util.Collections.emptyList();
+      private void ensureGuidePostsIsMutable() {
+        if (!((bitField0_ & 0x00000001) == 0x00000001)) {
+          guidePosts_ = new java.util.ArrayList<com.google.protobuf.ByteString>(guidePosts_);
+          bitField0_ |= 0x00000001;
+         }
+      }
+      /**
+       * <code>repeated bytes guidePosts = 1;</code>
+       */
+      public java.util.List<com.google.protobuf.ByteString>
+          getGuidePostsList() {
+        return java.util.Collections.unmodifiableList(guidePosts_);
+      }
+      /**
+       * <code>repeated bytes guidePosts = 1;</code>
+       */
+      public int getGuidePostsCount() {
+        return guidePosts_.size();
+      }
+      /**
+       * <code>repeated bytes guidePosts = 1;</code>
+       */
+      public com.google.protobuf.ByteString getGuidePosts(int index) {
+        return guidePosts_.get(index);
+      }
+      /**
+       * <code>repeated bytes guidePosts = 1;</code>
+       */
+      public Builder setGuidePosts(
+          int index, com.google.protobuf.ByteString value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  ensureGuidePostsIsMutable();
+        guidePosts_.set(index, value);
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>repeated bytes guidePosts = 1;</code>
+       */
+      public Builder addGuidePosts(com.google.protobuf.ByteString value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  ensureGuidePostsIsMutable();
+        guidePosts_.add(value);
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>repeated bytes guidePosts = 1;</code>
+       */
+      public Builder addAllGuidePosts(
+          java.lang.Iterable<? extends com.google.protobuf.ByteString> values) {
+        ensureGuidePostsIsMutable();
+        super.addAll(values, guidePosts_);
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>repeated bytes guidePosts = 1;</code>
+       */
+      public Builder clearGuidePosts() {
+        guidePosts_ = java.util.Collections.emptyList();
+        bitField0_ = (bitField0_ & ~0x00000001);
+        onChanged();
+        return this;
+      }
+
+      // optional int64 byteCount = 2;
+      private long byteCount_ ;
+      /**
+       * <code>optional int64 byteCount = 2;</code>
+       */
+      public boolean hasByteCount() {
+        return ((bitField0_ & 0x00000002) == 0x00000002);
+      }
+      /**
+       * <code>optional int64 byteCount = 2;</code>
+       */
+      public long getByteCount() {
+        return byteCount_;
+      }
+      /**
+       * <code>optional int64 byteCount = 2;</code>
+       */
+      public Builder setByteCount(long value) {
+        bitField0_ |= 0x00000002;
+        byteCount_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>optional int64 byteCount = 2;</code>
+       */
+      public Builder clearByteCount() {
+        bitField0_ = (bitField0_ & ~0x00000002);
+        byteCount_ = 0L;
+        onChanged();
+        return this;
+      }
+
+      // optional int64 rowCount = 3;
+      private long rowCount_ ;
+      /**
+       * <code>optional int64 rowCount = 3;</code>
+       */
+      public boolean hasRowCount() {
+        return ((bitField0_ & 0x00000004) == 0x00000004);
+      }
+      /**
+       * <code>optional int64 rowCount = 3;</code>
+       */
+      public long getRowCount() {
+        return rowCount_;
+      }
+      /**
+       * <code>optional int64 rowCount = 3;</code>
+       */
+      public Builder setRowCount(long value) {
+        bitField0_ |= 0x00000004;
+        rowCount_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>optional int64 rowCount = 3;</code>
+       */
+      public Builder clearRowCount() {
+        bitField0_ = (bitField0_ & ~0x00000004);
+        rowCount_ = 0L;
+        onChanged();
+        return this;
+      }
+
+      // @@protoc_insertion_point(builder_scope:PGuidePosts)
+    }
+
+    static {
+      defaultInstance = new PGuidePosts(true);
+      defaultInstance.initFields();
+    }
+
+    // @@protoc_insertion_point(class_scope:PGuidePosts)
+  }
+
+  private static com.google.protobuf.Descriptors.Descriptor
+    internal_static_PGuidePosts_descriptor;
+  private static
+    com.google.protobuf.GeneratedMessage.FieldAccessorTable
+      internal_static_PGuidePosts_fieldAccessorTable;
+
+  public static com.google.protobuf.Descriptors.FileDescriptor
+      getDescriptor() {
+    return descriptor;
+  }
+  private static com.google.protobuf.Descriptors.FileDescriptor
+      descriptor;
+  static {
+    java.lang.String[] descriptorData = {
+      "\n\021PGuidePosts.proto\"F\n\013PGuidePosts\022\022\n\ngu" +
+      "idePosts\030\001 \003(\014\022\021\n\tbyteCount\030\002 \001(\003\022\020\n\010row" +
+      "Count\030\003 \001(\003BE\n(org.apache.phoenix.coproc" +
+      "essor.generatedB\021PGuidePostsProtosH\001\210\001\001\240" +
+      "\001\001"
+    };
+    com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
+      new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
+        public com.google.protobuf.ExtensionRegistry assignDescriptors(
+            com.google.protobuf.Descriptors.FileDescriptor root) {
+          descriptor = root;
+          internal_static_PGuidePosts_descriptor =
+            getDescriptor().getMessageTypes().get(0);
+          internal_static_PGuidePosts_fieldAccessorTable = new
+            com.google.protobuf.GeneratedMessage.FieldAccessorTable(
+              internal_static_PGuidePosts_descriptor,
+              new java.lang.String[] { "GuidePosts", "ByteCount", "RowCount", });
+          return null;
+        }
+      };
+    com.google.protobuf.Descriptors.FileDescriptor
+      .internalBuildGeneratedFileFrom(descriptorData,
+        new com.google.protobuf.Descriptors.FileDescriptor[] {
+        }, assigner);
+  }
+
+  // @@protoc_insertion_point(outer_class_scope)
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/223a1484/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PTableProtos.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PTableProtos.java b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PTableProtos.java
index ff2cfe4..1e34bc9 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PTableProtos.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PTableProtos.java
@@ -1631,6 +1631,20 @@ public final class PTableProtos {
      * <code>optional int32 guidePostsCount = 5;</code>
      */
     int getGuidePostsCount();
+
+    // optional .PGuidePosts pGuidePosts = 6;
+    /**
+     * <code>optional .PGuidePosts pGuidePosts = 6;</code>
+     */
+    boolean hasPGuidePosts();
+    /**
+     * <code>optional .PGuidePosts pGuidePosts = 6;</code>
+     */
+    org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts getPGuidePosts();
+    /**
+     * <code>optional .PGuidePosts pGuidePosts = 6;</code>
+     */
+    org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePostsOrBuilder getPGuidePostsOrBuilder();
   }
   /**
    * Protobuf type {@code PTableStats}
@@ -1711,6 +1725,19 @@ public final class PTableProtos {
               guidePostsCount_ = input.readInt32();
               break;
             }
+            case 50: {
+              org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts.Builder subBuilder = null;
+              if (((bitField0_ & 0x00000010) == 0x00000010)) {
+                subBuilder = pGuidePosts_.toBuilder();
+              }
+              pGuidePosts_ = input.readMessage(org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts.PARSER, extensionRegistry);
+              if (subBuilder != null) {
+                subBuilder.mergeFrom(pGuidePosts_);
+                pGuidePosts_ = subBuilder.buildPartial();
+              }
+              bitField0_ |= 0x00000010;
+              break;
+            }
           }
         }
       } catch (com.google.protobuf.InvalidProtocolBufferException e) {
@@ -1841,12 +1868,35 @@ public final class PTableProtos {
       return guidePostsCount_;
     }
 
+    // optional .PGuidePosts pGuidePosts = 6;
+    public static final int PGUIDEPOSTS_FIELD_NUMBER = 6;
+    private org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts pGuidePosts_;
+    /**
+     * <code>optional .PGuidePosts pGuidePosts = 6;</code>
+     */
+    public boolean hasPGuidePosts() {
+      return ((bitField0_ & 0x00000010) == 0x00000010);
+    }
+    /**
+     * <code>optional .PGuidePosts pGuidePosts = 6;</code>
+     */
+    public org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts getPGuidePosts() {
+      return pGuidePosts_;
+    }
+    /**
+     * <code>optional .PGuidePosts pGuidePosts = 6;</code>
+     */
+    public org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePostsOrBuilder getPGuidePostsOrBuilder() {
+      return pGuidePosts_;
+    }
+
     private void initFields() {
       key_ = com.google.protobuf.ByteString.EMPTY;
       values_ = java.util.Collections.emptyList();
       guidePostsByteCount_ = 0L;
       keyBytesCount_ = 0L;
       guidePostsCount_ = 0;
+      pGuidePosts_ = org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts.getDefaultInstance();
     }
     private byte memoizedIsInitialized = -1;
     public final boolean isInitialized() {
@@ -1879,6 +1929,9 @@ public final class PTableProtos {
       if (((bitField0_ & 0x00000008) == 0x00000008)) {
         output.writeInt32(5, guidePostsCount_);
       }
+      if (((bitField0_ & 0x00000010) == 0x00000010)) {
+        output.writeMessage(6, pGuidePosts_);
+      }
       getUnknownFields().writeTo(output);
     }
 
@@ -1913,6 +1966,10 @@ public final class PTableProtos {
         size += com.google.protobuf.CodedOutputStream
           .computeInt32Size(5, guidePostsCount_);
       }
+      if (((bitField0_ & 0x00000010) == 0x00000010)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(6, pGuidePosts_);
+      }
       size += getUnknownFields().getSerializedSize();
       memoizedSerializedSize = size;
       return size;
@@ -1958,6 +2015,11 @@ public final class PTableProtos {
         result = result && (getGuidePostsCount()
             == other.getGuidePostsCount());
       }
+      result = result && (hasPGuidePosts() == other.hasPGuidePosts());
+      if (hasPGuidePosts()) {
+        result = result && getPGuidePosts()
+            .equals(other.getPGuidePosts());
+      }
       result = result &&
           getUnknownFields().equals(other.getUnknownFields());
       return result;
@@ -1991,6 +2053,10 @@ public final class PTableProtos {
         hash = (37 * hash) + GUIDEPOSTSCOUNT_FIELD_NUMBER;
         hash = (53 * hash) + getGuidePostsCount();
       }
+      if (hasPGuidePosts()) {
+        hash = (37 * hash) + PGUIDEPOSTS_FIELD_NUMBER;
+        hash = (53 * hash) + getPGuidePosts().hashCode();
+      }
       hash = (29 * hash) + getUnknownFields().hashCode();
       memoizedHashCode = hash;
       return hash;
@@ -2092,6 +2158,7 @@ public final class PTableProtos {
       }
       private void maybeForceBuilderInitialization() {
         if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {
+          getPGuidePostsFieldBuilder();
         }
       }
       private static Builder create() {
@@ -2110,6 +2177,12 @@ public final class PTableProtos {
         bitField0_ = (bitField0_ & ~0x00000008);
         guidePostsCount_ = 0;
         bitField0_ = (bitField0_ & ~0x00000010);
+        if (pGuidePostsBuilder_ == null) {
+          pGuidePosts_ = org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts.getDefaultInstance();
+        } else {
+          pGuidePostsBuilder_.clear();
+        }
+        bitField0_ = (bitField0_ & ~0x00000020);
         return this;
       }
 
@@ -2159,6 +2232,14 @@ public final class PTableProtos {
           to_bitField0_ |= 0x00000008;
         }
         result.guidePostsCount_ = guidePostsCount_;
+        if (((from_bitField0_ & 0x00000020) == 0x00000020)) {
+          to_bitField0_ |= 0x00000010;
+        }
+        if (pGuidePostsBuilder_ == null) {
+          result.pGuidePosts_ = pGuidePosts_;
+        } else {
+          result.pGuidePosts_ = pGuidePostsBuilder_.build();
+        }
         result.bitField0_ = to_bitField0_;
         onBuilt();
         return result;
@@ -2197,6 +2278,9 @@ public final class PTableProtos {
         if (other.hasGuidePostsCount()) {
           setGuidePostsCount(other.getGuidePostsCount());
         }
+        if (other.hasPGuidePosts()) {
+          mergePGuidePosts(other.getPGuidePosts());
+        }
         this.mergeUnknownFields(other.getUnknownFields());
         return this;
       }
@@ -2435,6 +2519,123 @@ public final class PTableProtos {
         return this;
       }
 
+      // optional .PGuidePosts pGuidePosts = 6;
+      private org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts pGuidePosts_ = org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts.getDefaultInstance();
+      private com.google.protobuf.SingleFieldBuilder<
+          org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts, org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts.Builder, org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePostsOrBuilder> pGuidePostsBuilder_;
+      /**
+       * <code>optional .PGuidePosts pGuidePosts = 6;</code>
+       */
+      public boolean hasPGuidePosts() {
+        return ((bitField0_ & 0x00000020) == 0x00000020);
+      }
+      /**
+       * <code>optional .PGuidePosts pGuidePosts = 6;</code>
+       */
+      public org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts getPGuidePosts() {
+        if (pGuidePostsBuilder_ == null) {
+          return pGuidePosts_;
+        } else {
+          return pGuidePostsBuilder_.getMessage();
+        }
+      }
+      /**
+       * <code>optional .PGuidePosts pGuidePosts = 6;</code>
+       */
+      public Builder setPGuidePosts(org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts value) {
+        if (pGuidePostsBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          pGuidePosts_ = value;
+          onChanged();
+        } else {
+          pGuidePostsBuilder_.setMessage(value);
+        }
+        bitField0_ |= 0x00000020;
+        return this;
+      }
+      /**
+       * <code>optional .PGuidePosts pGuidePosts = 6;</code>
+       */
+      public Builder setPGuidePosts(
+          org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts.Builder builderForValue) {
+        if (pGuidePostsBuilder_ == null) {
+          pGuidePosts_ = builderForValue.build();
+          onChanged();
+        } else {
+          pGuidePostsBuilder_.setMessage(builderForValue.build());
+        }
+        bitField0_ |= 0x00000020;
+        return this;
+      }
+      /**
+       * <code>optional .PGuidePosts pGuidePosts = 6;</code>
+       */
+      public Builder mergePGuidePosts(org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts value) {
+        if (pGuidePostsBuilder_ == null) {
+          if (((bitField0_ & 0x00000020) == 0x00000020) &&
+              pGuidePosts_ != org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts.getDefaultInstance()) {
+            pGuidePosts_ =
+              org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts.newBuilder(pGuidePosts_).mergeFrom(value).buildPartial();
+          } else {
+            pGuidePosts_ = value;
+          }
+          onChanged();
+        } else {
+          pGuidePostsBuilder_.mergeFrom(value);
+        }
+        bitField0_ |= 0x00000020;
+        return this;
+      }
+      /**
+       * <code>optional .PGuidePosts pGuidePosts = 6;</code>
+       */
+      public Builder clearPGuidePosts() {
+        if (pGuidePostsBuilder_ == null) {
+          pGuidePosts_ = org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts.getDefaultInstance();
+          onChanged();
+        } else {
+          pGuidePostsBuilder_.clear();
+        }
+        bitField0_ = (bitField0_ & ~0x00000020);
+        return this;
+      }
+      /**
+       * <code>optional .PGuidePosts pGuidePosts = 6;</code>
+       */
+      public org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts.Builder getPGuidePostsBuilder() {
+        bitField0_ |= 0x00000020;
+        onChanged();
+        return getPGuidePostsFieldBuilder().getBuilder();
+      }
+      /**
+       * <code>optional .PGuidePosts pGuidePosts = 6;</code>
+       */
+      public org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePostsOrBuilder getPGuidePostsOrBuilder() {
+        if (pGuidePostsBuilder_ != null) {
+          return pGuidePostsBuilder_.getMessageOrBuilder();
+        } else {
+          return pGuidePosts_;
+        }
+      }
+      /**
+       * <code>optional .PGuidePosts pGuidePosts = 6;</code>
+       */
+      private com.google.protobuf.SingleFieldBuilder<
+          org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts, org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts.Builder, org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePostsOrBuilder> 
+          getPGuidePostsFieldBuilder() {
+        if (pGuidePostsBuilder_ == null) {
+          pGuidePostsBuilder_ = new com.google.protobuf.SingleFieldBuilder<
+              org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts, org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts.Builder, org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePostsOrBuilder>(
+                  pGuidePosts_,
+                  getParentForChildren(),
+                  isClean());
+          pGuidePosts_ = null;
+        }
+        return pGuidePostsBuilder_;
+      }
+
       // @@protoc_insertion_point(builder_scope:PTableStats)
     }
 
@@ -6003,33 +6204,34 @@ public final class PTableProtos {
       descriptor;
   static {
     java.lang.String[] descriptorData = {
-      "\n\014PTable.proto\"\347\001\n\007PColumn\022\027\n\017columnName" +
-      "Bytes\030\001 \002(\014\022\027\n\017familyNameBytes\030\002 \001(\014\022\020\n\010" +
-      "dataType\030\003 \002(\t\022\021\n\tmaxLength\030\004 \001(\005\022\r\n\005sca" +
-      "le\030\005 \001(\005\022\020\n\010nullable\030\006 \002(\010\022\020\n\010position\030\007" +
-      " \002(\005\022\021\n\tsortOrder\030\010 \002(\005\022\021\n\tarraySize\030\t \001" +
-      "(\005\022\024\n\014viewConstant\030\n \001(\014\022\026\n\016viewReferenc" +
-      "ed\030\013 \001(\010\"w\n\013PTableStats\022\013\n\003key\030\001 \002(\014\022\016\n\006" +
-      "values\030\002 \003(\014\022\033\n\023guidePostsByteCount\030\003 \001(" +
-      "\003\022\025\n\rkeyBytesCount\030\004 \001(\003\022\027\n\017guidePostsCo" +
-      "unt\030\005 \001(\005\"\242\004\n\006PTable\022\027\n\017schemaNameBytes\030",
-      "\001 \002(\014\022\026\n\016tableNameBytes\030\002 \002(\014\022\036\n\ttableTy" +
-      "pe\030\003 \002(\0162\013.PTableType\022\022\n\nindexState\030\004 \001(" +
-      "\t\022\026\n\016sequenceNumber\030\005 \002(\003\022\021\n\ttimeStamp\030\006" +
-      " \002(\003\022\023\n\013pkNameBytes\030\007 \001(\014\022\021\n\tbucketNum\030\010" +
-      " \002(\005\022\031\n\007columns\030\t \003(\0132\010.PColumn\022\030\n\007index" +
-      "es\030\n \003(\0132\007.PTable\022\027\n\017isImmutableRows\030\013 \002" +
-      "(\010\022 \n\nguidePosts\030\014 \003(\0132\014.PTableStats\022\032\n\022" +
-      "dataTableNameBytes\030\r \001(\014\022\031\n\021defaultFamil" +
-      "yName\030\016 \001(\014\022\022\n\ndisableWAL\030\017 \002(\010\022\023\n\013multi" +
-      "Tenant\030\020 \002(\010\022\020\n\010viewType\030\021 \001(\014\022\025\n\rviewSt",
-      "atement\030\022 \001(\014\022\025\n\rphysicalNames\030\023 \003(\014\022\020\n\010" +
-      "tenantId\030\024 \001(\014\022\023\n\013viewIndexId\030\025 \001(\005\022\021\n\ti" +
-      "ndexType\030\026 \001(\014\022\026\n\016statsTimeStamp\030\027 \001(\003*A" +
-      "\n\nPTableType\022\n\n\006SYSTEM\020\000\022\010\n\004USER\020\001\022\010\n\004VI" +
-      "EW\020\002\022\t\n\005INDEX\020\003\022\010\n\004JOIN\020\004B@\n(org.apache." +
-      "phoenix.coprocessor.generatedB\014PTablePro" +
-      "tosH\001\210\001\001\240\001\001"
+      "\n\014PTable.proto\032\021PGuidePosts.proto\"\347\001\n\007PC" +
+      "olumn\022\027\n\017columnNameBytes\030\001 \002(\014\022\027\n\017family" +
+      "NameBytes\030\002 \001(\014\022\020\n\010dataType\030\003 \002(\t\022\021\n\tmax" +
+      "Length\030\004 \001(\005\022\r\n\005scale\030\005 \001(\005\022\020\n\010nullable\030" +
+      "\006 \002(\010\022\020\n\010position\030\007 \002(\005\022\021\n\tsortOrder\030\010 \002" +
+      "(\005\022\021\n\tarraySize\030\t \001(\005\022\024\n\014viewConstant\030\n " +
+      "\001(\014\022\026\n\016viewReferenced\030\013 \001(\010\"\232\001\n\013PTableSt" +
+      "ats\022\013\n\003key\030\001 \002(\014\022\016\n\006values\030\002 \003(\014\022\033\n\023guid" +
+      "ePostsByteCount\030\003 \001(\003\022\025\n\rkeyBytesCount\030\004" +
+      " \001(\003\022\027\n\017guidePostsCount\030\005 \001(\005\022!\n\013pGuideP",
+      "osts\030\006 \001(\0132\014.PGuidePosts\"\242\004\n\006PTable\022\027\n\017s" +
+      "chemaNameBytes\030\001 \002(\014\022\026\n\016tableNameBytes\030\002" +
+      " \002(\014\022\036\n\ttableType\030\003 \002(\0162\013.PTableType\022\022\n\n" +
+      "indexState\030\004 \001(\t\022\026\n\016sequenceNumber\030\005 \002(\003" +
+      "\022\021\n\ttimeStamp\030\006 \002(\003\022\023\n\013pkNameBytes\030\007 \001(\014" +
+      "\022\021\n\tbucketNum\030\010 \002(\005\022\031\n\007columns\030\t \003(\0132\010.P" +
+      "Column\022\030\n\007indexes\030\n \003(\0132\007.PTable\022\027\n\017isIm" +
+      "mutableRows\030\013 \002(\010\022 \n\nguidePosts\030\014 \003(\0132\014." +
+      "PTableStats\022\032\n\022dataTableNameBytes\030\r \001(\014\022" +
+      "\031\n\021defaultFamilyName\030\016 \001(\014\022\022\n\ndisableWAL",
+      "\030\017 \002(\010\022\023\n\013multiTenant\030\020 \002(\010\022\020\n\010viewType\030" +
+      "\021 \001(\014\022\025\n\rviewStatement\030\022 \001(\014\022\025\n\rphysical" +
+      "Names\030\023 \003(\014\022\020\n\010tenantId\030\024 \001(\014\022\023\n\013viewInd" +
+      "exId\030\025 \001(\005\022\021\n\tindexType\030\026 \001(\014\022\026\n\016statsTi" +
+      "meStamp\030\027 \001(\003*A\n\nPTableType\022\n\n\006SYSTEM\020\000\022" +
+      "\010\n\004USER\020\001\022\010\n\004VIEW\020\002\022\t\n\005INDEX\020\003\022\010\n\004JOIN\020\004" +
+      "B@\n(org.apache.phoenix.coprocessor.gener" +
+      "atedB\014PTableProtosH\001\210\001\001\240\001\001"
     };
     com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
       new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
@@ -6047,7 +6249,7 @@ public final class PTableProtos {
           internal_static_PTableStats_fieldAccessorTable = new
             com.google.protobuf.GeneratedMessage.FieldAccessorTable(
               internal_static_PTableStats_descriptor,
-              new java.lang.String[] { "Key", "Values", "GuidePostsByteCount", "KeyBytesCount", "GuidePostsCount", });
+              new java.lang.String[] { "Key", "Values", "GuidePostsByteCount", "KeyBytesCount", "GuidePostsCount", "PGuidePosts", });
           internal_static_PTable_descriptor =
             getDescriptor().getMessageTypes().get(2);
           internal_static_PTable_fieldAccessorTable = new
@@ -6060,6 +6262,7 @@ public final class PTableProtos {
     com.google.protobuf.Descriptors.FileDescriptor
       .internalBuildGeneratedFileFrom(descriptorData,
         new com.google.protobuf.Descriptors.FileDescriptor[] {
+          org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.getDescriptor(),
         }, assigner);
   }
 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/223a1484/phoenix-core/src/main/java/org/apache/phoenix/execute/ScanPlan.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/execute/ScanPlan.java b/phoenix-core/src/main/java/org/apache/phoenix/execute/ScanPlan.java
index d82e8f4..d0a71f4 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/execute/ScanPlan.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/execute/ScanPlan.java
@@ -112,7 +112,8 @@ public class ScanPlan extends BaseQueryPlan {
             estRegionSize = StatisticsUtil.getGuidePostDepth(guidepostPerRegion, guidepostWidth, desc);
         } else {
             // Region size estimated based on total number of bytes divided by number of regions
-            estRegionSize = gpsInfo.getByteCount() / (gpsInfo.getGuidePosts().size()+1);
+            long totByteSize = gpsInfo.getByteCount();
+            estRegionSize = totByteSize / (gpsInfo.getGuidePosts().size()+1);
         }
         // TODO: configurable number of bytes?
         boolean isSerial = (perScanLimit * estRowSize < estRegionSize);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/223a1484/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java
index ce27dfb..aa6fdf0 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java
@@ -249,6 +249,8 @@ public class PhoenixDatabaseMetaData implements DatabaseMetaData, org.apache.pho
     public static final byte[] GUIDE_POSTS_COUNT_BYTES = Bytes.toBytes(GUIDE_POSTS_COUNT);
     public static final String GUIDE_POSTS_WIDTH = "GUIDE_POSTS_WIDTH";
     public static final byte[] GUIDE_POSTS_WIDTH_BYTES = Bytes.toBytes(GUIDE_POSTS_WIDTH);
+    public static final String GUIDE_POSTS_ROW_COUNT = "GUIDE_POSTS_ROW_COUNT";
+    public static final byte[] GUIDE_POSTS_ROW_COUNT_BYTES = Bytes.toBytes(GUIDE_POSTS_ROW_COUNT);
     public static final String MIN_KEY = "MIN_KEY";
     public static final byte[] MIN_KEY_BYTES = Bytes.toBytes(MIN_KEY);
     public static final String MAX_KEY = "MAX_KEY";

http://git-wip-us.apache.org/repos/asf/phoenix/blob/223a1484/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 6de245e..8050b4a 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
@@ -129,6 +129,7 @@ import org.apache.phoenix.schema.stats.PTableStats;
 import org.apache.phoenix.schema.stats.StatisticsUtil;
 import org.apache.phoenix.schema.types.PBoolean;
 import org.apache.phoenix.schema.types.PDataType;
+import org.apache.phoenix.schema.types.PLong;
 import org.apache.phoenix.util.ByteUtil;
 import org.apache.phoenix.util.Closeables;
 import org.apache.phoenix.util.ConfigUtil;
@@ -1719,9 +1720,13 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
                                 metaConnection.createStatement().executeUpdate(
                                         QueryConstants.CREATE_STATS_TABLE_METADATA);
                             } catch (NewerTableAlreadyExistsException ignore) {
-
                             } catch(TableAlreadyExistsException ignore) {
-                                
+                                metaConnection = addColumnsIfNotExists(
+                                        metaConnection,
+                                        PhoenixDatabaseMetaData.SYSTEM_STATS_NAME,
+                                        MetaDataProtocol.MIN_SYSTEM_TABLE_TIMESTAMP,
+                                        PhoenixDatabaseMetaData.GUIDE_POSTS_ROW_COUNT + " "
+                                                + PLong.INSTANCE.getSqlTypeName());
                             }
                         } catch (Exception e) {
                             if (e instanceof SQLException) {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/223a1484/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionlessQueryServicesImpl.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionlessQueryServicesImpl.java b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionlessQueryServicesImpl.java
index 38c6139..da893e6 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionlessQueryServicesImpl.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionlessQueryServicesImpl.java
@@ -274,7 +274,6 @@ public class ConnectionlessQueryServicesImpl extends DelegateQueryServices imple
                     // A TableAlreadyExistsException is not thrown, since the table only exists *after* this fixed timestamp.
                 }
                 try {
-                    // TODO : Get this from a configuration
                     metaConnection.createStatement().executeUpdate(QueryConstants.CREATE_STATS_TABLE_METADATA);
                 } catch (NewerTableAlreadyExistsException ignore) {
                     // Ignore, as this will happen if the SYSTEM.SEQUENCE already exists at this fixed

http://git-wip-us.apache.org/repos/asf/phoenix/blob/223a1484/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java
index 555628d..985d3cc 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java
@@ -36,6 +36,7 @@ import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.DEFAULT_COLUMN_FAM
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.DISABLE_WAL;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.GUIDE_POSTS;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.GUIDE_POSTS_COUNT;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.GUIDE_POSTS_ROW_COUNT;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.GUIDE_POSTS_WIDTH;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.IMMUTABLE_ROWS;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.INCREMENT_BY;
@@ -245,6 +246,7 @@ public interface QueryConstants {
             MIN_KEY + " VARBINARY," + 
             MAX_KEY + " VARBINARY," +
             LAST_STATS_UPDATE_TIME+ " DATE, "+
+            GUIDE_POSTS_ROW_COUNT+ " BIGINT, "+
             "CONSTRAINT " + SYSTEM_TABLE_PK_NAME + " PRIMARY KEY ("
             + PHYSICAL_NAME + ","
             + COLUMN_FAMILY + ","+ REGION_NAME+"))\n" +

http://git-wip-us.apache.org/repos/asf/phoenix/blob/223a1484/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableImpl.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableImpl.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableImpl.java
index 06f3a9c..ad50f73 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableImpl.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableImpl.java
@@ -40,6 +40,8 @@ import org.apache.hadoop.hbase.client.Mutation;
 import org.apache.hadoop.hbase.client.Put;
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.phoenix.coprocessor.generated.PGuidePostsProtos;
+import org.apache.phoenix.coprocessor.generated.PGuidePostsProtos.PGuidePosts;
 import org.apache.phoenix.coprocessor.generated.PTableProtos;
 import org.apache.phoenix.hbase.index.util.ImmutableBytesPtr;
 import org.apache.phoenix.hbase.index.util.KeyValueBuilder;
@@ -884,12 +886,21 @@ public class PTableImpl implements PTable {
       
       boolean isImmutableRows = table.getIsImmutableRows();
       SortedMap<byte[], GuidePostsInfo> tableGuidePosts = new TreeMap<byte[], GuidePostsInfo>(Bytes.BYTES_COMPARATOR);
-      for (PTableProtos.PTableStats pTableStatsProto : table.getGuidePostsList()) {
-          List<byte[]> value = Lists.newArrayListWithExpectedSize(pTableStatsProto.getValuesCount());
+        for (PTableProtos.PTableStats pTableStatsProto : table.getGuidePostsList()) {
+            List<byte[]> value = Lists.newArrayListWithExpectedSize(pTableStatsProto.getValuesCount());
             for (int j = 0; j < pTableStatsProto.getValuesCount(); j++) {
                 value.add(pTableStatsProto.getValues(j).toByteArray());
             }
-            GuidePostsInfo info = new GuidePostsInfo(pTableStatsProto.getGuidePostsByteCount(), value);
+            // No op
+            pTableStatsProto.getGuidePostsByteCount();
+            value = Lists.newArrayListWithExpectedSize(pTableStatsProto.getValuesCount());
+            PGuidePosts pGuidePosts = pTableStatsProto.getPGuidePosts();
+            for(int j = 0; j < pGuidePosts.getGuidePostsCount(); j++) {
+                value.add(pGuidePosts.getGuidePosts(j).toByteArray());
+            }
+            long guidePostsByteCount = pGuidePosts.getByteCount();
+            long rowCount = pGuidePosts.getRowCount();
+            GuidePostsInfo info = new GuidePostsInfo(guidePostsByteCount, value, rowCount);
             tableGuidePosts.put(pTableStatsProto.getKey().toByteArray(), info);
       }
       PTableStats stats = new PTableStatsImpl(tableGuidePosts, table.getStatsTimeStamp());
@@ -984,6 +995,13 @@ public class PTableImpl implements PTable {
              statsBuilder.addValues(HBaseZeroCopyByteString.wrap(stat));
          }
          statsBuilder.setGuidePostsByteCount(entry.getValue().getByteCount());
+         PGuidePostsProtos.PGuidePosts.Builder guidePstsBuilder = PGuidePostsProtos.PGuidePosts.newBuilder();
+         for (byte[] stat : entry.getValue().getGuidePosts()) {
+             guidePstsBuilder.addGuidePosts(HBaseZeroCopyByteString.wrap(stat));
+         }
+         guidePstsBuilder.setByteCount(entry.getValue().getByteCount());
+         guidePstsBuilder.setRowCount(entry.getValue().getRowCount());
+         statsBuilder.setPGuidePosts(guidePstsBuilder);
          builder.addGuidePosts(statsBuilder.build());
       }
       builder.setStatsTimeStamp(table.getTableStats().getTimestamp());