You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@phoenix.apache.org by nd...@apache.org on 2014/12/18 01:11:51 UTC

[13/22] phoenix git commit: PHOENIX-1514 Break up PDataType Enum

http://git-wip-us.apache.org/repos/asf/phoenix/blob/04ef859b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBoolean.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBoolean.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBoolean.java
new file mode 100644
index 0000000..93f6dc1
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBoolean.java
@@ -0,0 +1,140 @@
+/*
+ * 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.types;
+
+import com.google.common.base.Preconditions;
+import com.google.common.primitives.Booleans;
+import org.apache.phoenix.schema.SortOrder;
+
+import java.sql.Types;
+
+public class PBoolean extends PDataType<Boolean> {
+
+  public static final PBoolean INSTANCE = new PBoolean();
+
+  private PBoolean() {
+    super("BOOLEAN", Types.BOOLEAN, Boolean.class, null, 21);
+  }
+
+  @Override
+  public byte[] toBytes(Object object) {
+    if (object == null) {
+      // TODO: review - return null?
+      throw newIllegalDataException(this + " may not be null");
+    }
+    return ((Boolean) object).booleanValue() ? TRUE_BYTES : FALSE_BYTES;
+  }
+
+  @Override
+  public int toBytes(Object object, byte[] bytes, int offset) {
+    if (object == null) {
+      // TODO: review - return null?
+      throw newIllegalDataException(this + " may not be null");
+    }
+    bytes[offset] = ((Boolean) object).booleanValue() ? TRUE_BYTE : FALSE_BYTE;
+    return BOOLEAN_LENGTH;
+  }
+
+  @Override
+  public byte[] toBytes(Object object, SortOrder sortOrder) {
+    if (object == null) {
+      // TODO: review - return null?
+      throw newIllegalDataException(this + " may not be null");
+    }
+    return ((Boolean) object).booleanValue() ^ sortOrder == SortOrder.ASC ?
+        TRUE_BYTES :
+        FALSE_BYTES;
+  }
+
+  @Override
+  public Boolean toObject(byte[] bytes, int offset, int length, PDataType actualType,
+      SortOrder sortOrder, Integer maxLength, Integer scale) {
+    Preconditions.checkNotNull(sortOrder);
+    if (length == 0) {
+      return null;
+    }
+    if (actualType == this) {
+      if (length > 1) {
+        throw newIllegalDataException("BOOLEAN may only be a single byte");
+      }
+      return ((bytes[offset] == FALSE_BYTE ^ sortOrder == SortOrder.DESC) ?
+          Boolean.FALSE :
+          Boolean.TRUE);
+    } else if (actualType == PDecimal.INSTANCE) {
+      // false translated to the ZERO_BYTE
+      return ((bytes[offset] == ZERO_BYTE ^ sortOrder == SortOrder.DESC) ?
+          Boolean.FALSE :
+          Boolean.TRUE);
+    }
+    throwConstraintViolationException(actualType, this);
+    return null;
+  }
+
+  @Override
+  public boolean isCoercibleTo(PDataType targetType) {
+    return super.isCoercibleTo(targetType) || targetType.equals(PBinary.INSTANCE);
+  }
+
+  @Override
+  public boolean isCastableTo(PDataType targetType) {
+    // Allow cast to BOOLEAN so it can be used in an index or group by
+    return super.isCastableTo(targetType) || targetType.equals(PDecimal.INSTANCE);
+  }
+
+  @Override
+  public boolean isFixedWidth() {
+    return true;
+  }
+
+  @Override
+  public Integer getByteSize() {
+    return BOOLEAN_LENGTH;
+  }
+
+  @Override
+  public int estimateByteSize(Object o) {
+    return BOOLEAN_LENGTH;
+  }
+
+  @Override
+  public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
+    return Booleans.compare((Boolean) lhs, (Boolean) rhs);
+  }
+
+  @Override
+  public Object toObject(String value) {
+    return Boolean.parseBoolean(value);
+  }
+
+  @Override
+  public Object toObject(Object object, PDataType actualType) {
+    if (actualType == this || object == null) {
+      return object;
+    }
+    if (actualType == PVarbinary.INSTANCE || actualType == PBinary.INSTANCE) {
+      byte[] bytes = (byte[]) object;
+      return toObject(bytes, 0, bytes.length);
+    }
+    return throwConstraintViolationException(actualType, this);
+  }
+
+  @Override
+  public Object getSampleValue(Integer maxLength, Integer arrayLength) {
+    return RANDOM.get().nextBoolean();
+  }
+}

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

http://git-wip-us.apache.org/repos/asf/phoenix/blob/04ef859b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PChar.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PChar.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PChar.java
new file mode 100644
index 0000000..3100f89
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PChar.java
@@ -0,0 +1,203 @@
+/*
+ * 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.types;
+
+import com.google.common.base.Strings;
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.phoenix.exception.ValueTypeIncompatibleException;
+import org.apache.phoenix.schema.SortOrder;
+import org.apache.phoenix.util.StringUtil;
+
+import java.sql.Types;
+import java.text.Format;
+import java.util.Arrays;
+
+/**
+ * Fixed length single byte characters
+ */
+public class PChar extends PDataType<String> {
+
+  public static final PChar INSTANCE = new PChar();
+
+  private PChar() {
+    super("CHAR", Types.CHAR, String.class, null, 1);
+  }
+
+    @Override
+    public void pad(ImmutableBytesWritable ptr, Integer maxLength) {
+      if (ptr.getLength() >= maxLength) {
+        return;
+      }
+      byte[] newBytes = new byte[maxLength];
+      System.arraycopy(ptr.get(), ptr.getOffset(), newBytes, 0, ptr.getLength());
+      Arrays.fill(newBytes, ptr.getLength(), maxLength, StringUtil.SPACE_UTF8);
+      ptr.set(newBytes);
+    }
+
+    @Override
+    public Object pad(Object object, Integer maxLength) {
+      String s = (String) object;
+      if (s == null) {
+        return s;
+      }
+      if (s.length() == maxLength) {
+        return object;
+      }
+      if (s.length() > maxLength) {
+        throw new ValueTypeIncompatibleException(this,maxLength,null);
+      }
+      return Strings.padEnd(s, maxLength, ' ');
+    }
+
+    @Override
+    public byte[] toBytes(Object object) {
+      if (object == null) {
+        throw newIllegalDataException(this + " may not be null");
+      }
+      byte[] b = PVarchar.INSTANCE.toBytes(object);
+      if (b.length != ((String) object).length()) {
+        throw newIllegalDataException("CHAR types may only contain single byte characters (" + object + ")");
+      }
+      return b;
+    }
+
+    @Override
+    public int toBytes(Object object, byte[] bytes, int offset) {
+      if (object == null) {
+        throw newIllegalDataException(this + " may not be null");
+      }
+      int len = PVarchar.INSTANCE.toBytes(object, bytes, offset);
+      if (len != ((String) object).length()) {
+        throw newIllegalDataException("CHAR types may only contain single byte characters (" + object + ")");
+      }
+      return len;
+    }
+
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length, PDataType actualType, SortOrder sortOrder, Integer maxLength, Integer scale) {
+      if (!actualType.isCoercibleTo(this)) { // TODO: have isCoercibleTo that takes bytes, offset?
+        throwConstraintViolationException(actualType,this);
+      }
+      if (length == 0) {
+        return null;
+      }
+      length = StringUtil.getUnpaddedCharLength(bytes, offset, length, sortOrder);
+      if (sortOrder == SortOrder.DESC) {
+        bytes = SortOrder.invert(bytes, offset, length);
+        offset = 0;
+      }
+      // TODO: UTF-8 decoder that will invert as it decodes
+      String s = Bytes.toString(bytes, offset, length);
+      if (length != s.length()) {
+        throw newIllegalDataException("CHAR types may only contain single byte characters (" + s + ")");
+      }
+      return s;
+    }
+
+    @Override
+    public Object toObject(Object object, PDataType actualType) {
+      if (equalsAny(actualType, PVarchar.INSTANCE, this)) {
+        String s = (String) object;
+        return s == null || s.length() > 0 ? s : null;
+      }
+      return throwConstraintViolationException(actualType,this);
+    }
+
+    @Override
+    public boolean isCoercibleTo(PDataType targetType) {
+      return equalsAny(targetType, this, PVarchar.INSTANCE, PBinary.INSTANCE, PVarbinary.INSTANCE);
+    }
+
+    @Override
+    public void coerceBytes(ImmutableBytesWritable ptr, Object o, PDataType actualType,
+        Integer actualMaxLength, Integer actualScale, SortOrder actualModifier,
+        Integer desiredMaxLength, Integer desiredScale, SortOrder expectedModifier) {
+      if (o != null && actualType.equals(PVarchar.INSTANCE) && ((String)o).length() != ptr.getLength()) {
+        throw newIllegalDataException("CHAR types may only contain single byte characters (" + o + ")");
+      }
+      super.coerceBytes(ptr, o, actualType, actualMaxLength, actualScale, actualModifier, desiredMaxLength, desiredScale, expectedModifier);
+    }
+
+    @Override
+    public boolean isSizeCompatible(ImmutableBytesWritable ptr, Object value, PDataType srcType,
+        Integer maxLength, Integer scale, Integer desiredMaxLength, Integer desiredScale) {
+      return PVarchar.INSTANCE.isSizeCompatible(ptr, value, srcType, maxLength, scale, desiredMaxLength, desiredScale);
+    }
+
+    @Override
+    public boolean isFixedWidth() {
+      return true;
+    }
+
+    @Override
+    public Integer getByteSize() {
+      return null;
+    }
+
+    @Override
+    public Integer getMaxLength(Object o) {
+      if (o == null) {
+        return null;
+      }
+      String value = (String) o;
+      return value.length();
+    }
+
+    @Override
+    public int estimateByteSize(Object o) {
+      String value = (String) o;
+      return value.length();
+    }
+
+    @Override
+    public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
+      return PVarchar.INSTANCE.compareTo(lhs, rhs, rhsType);
+    }
+
+    @Override
+    public Object toObject(String value) {
+      if (value == null || value.length() == 0) {
+        throw newIllegalDataException(this + " may not be null");
+      }
+      if (StringUtil.hasMultiByteChars(value)) {
+        throw newIllegalDataException("CHAR types may only contain single byte characters (" + value + ")");
+      }
+      return value;
+    }
+
+    @Override
+    public Integer estimateByteSizeFromLength(Integer length) {
+      return length;
+    }
+
+    @Override
+    public boolean isBytesComparableWith(PDataType otherType) {
+      return super.isBytesComparableWith(otherType) || otherType.equals(PVarchar.INSTANCE);
+    }
+
+    @Override
+    public String toStringLiteral(byte[] b, int offset, int length, Format formatter) {
+      return PVarchar.INSTANCE.toStringLiteral(b, offset, length, formatter);
+    }
+
+    @Override
+    public Object getSampleValue(Integer maxLength, Integer arrayLength) {
+      return PVarchar.INSTANCE.getSampleValue(maxLength, arrayLength);
+    }
+}

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

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

http://git-wip-us.apache.org/repos/asf/phoenix/blob/04ef859b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDataTypeFactory.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDataTypeFactory.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDataTypeFactory.java
new file mode 100644
index 0000000..45a9657
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDataTypeFactory.java
@@ -0,0 +1,118 @@
+/*
+ * 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.types;
+
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * Factory avoids circular dependency problem in static initializers across types.
+ */
+public class PDataTypeFactory {
+
+  private static PDataTypeFactory INSTANCE;
+  private final PDataType[] orderedTypes;
+  private final SortedSet<PDataType> types;
+  private final Map<Class<? extends PDataType>, PDataType> classToInstance;
+
+  public static PDataTypeFactory getInstance() {
+    if (INSTANCE == null) {
+      INSTANCE = new PDataTypeFactory();
+    }
+    return INSTANCE;
+  }
+
+  private PDataTypeFactory() {
+    types = new TreeSet<>(new Comparator<PDataType>() {
+      @Override
+      public int compare(PDataType o1, PDataType o2) {
+        return Integer.compare(o1.ordinal(), o2.ordinal());
+      }
+    });    // TODO: replace with ServiceLoader or some other plugin system
+    types.add(PBinary.INSTANCE);
+    types.add(PBinaryArray.INSTANCE);
+    types.add(PChar.INSTANCE);
+    types.add(PCharArray.INSTANCE);
+    types.add(PDecimal.INSTANCE);
+    types.add(PDecimalArray.INSTANCE);
+    types.add(PBoolean.INSTANCE);
+    types.add(PBooleanArray.INSTANCE);
+    types.add(PDate.INSTANCE);
+    types.add(PDateArray.INSTANCE);
+    types.add(PDouble.INSTANCE);
+    types.add(PDoubleArray.INSTANCE);
+    types.add(PFloat.INSTANCE);
+    types.add(PFloatArray.INSTANCE);
+    types.add(PInteger.INSTANCE);
+    types.add(PIntegerArray.INSTANCE);
+    types.add(PLong.INSTANCE);
+    types.add(PLongArray.INSTANCE);
+    types.add(PTime.INSTANCE);
+    types.add(PTimeArray.INSTANCE);
+    types.add(PTimestamp.INSTANCE);
+    types.add(PTimestampArray.INSTANCE);
+    types.add(PSmallint.INSTANCE);
+    types.add(PSmallintArray.INSTANCE);
+    types.add(PTinyint.INSTANCE);
+    types.add(PTinyintArray.INSTANCE);
+    types.add(PUnsignedDate.INSTANCE);
+    types.add(PUnsignedDateArray.INSTANCE);
+    types.add(PUnsignedDouble.INSTANCE);
+    types.add(PUnsignedDoubleArray.INSTANCE);
+    types.add(PUnsignedFloat.INSTANCE);
+    types.add(PUnsignedFloatArray.INSTANCE);
+    types.add(PUnsignedInt.INSTANCE);
+    types.add(PUnsignedIntArray.INSTANCE);
+    types.add(PUnsignedLong.INSTANCE);
+    types.add(PUnsignedLongArray.INSTANCE);
+    types.add(PUnsignedSmallint.INSTANCE);
+    types.add(PUnsignedSmallintArray.INSTANCE);
+    types.add(PUnsignedTime.INSTANCE);
+    types.add(PUnsignedTimeArray.INSTANCE);
+    types.add(PUnsignedTimestamp.INSTANCE);
+    types.add(PUnsignedTimestampArray.INSTANCE);
+    types.add(PUnsignedTinyint.INSTANCE);
+    types.add(PUnsignedTinyintArray.INSTANCE);
+    types.add(PVarbinary.INSTANCE);
+    types.add(PVarbinaryArray.INSTANCE);
+    types.add(PVarchar.INSTANCE);
+    types.add(PVarcharArray.INSTANCE);
+
+    classToInstance = new HashMap<>(types.size());
+    for (PDataType t : types) {
+      classToInstance.put(t.getClass(), t);
+    }
+    orderedTypes = types.toArray(new PDataType[types.size()]);
+  }
+
+  public Set<PDataType> getTypes() {
+    return types;
+  }
+
+  public PDataType[] getOrderedTypes() {
+    return orderedTypes;
+  }
+
+  public PDataType instanceFromClass(Class<? extends PDataType> clazz) {
+    return classToInstance.get(clazz);
+  }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/04ef859b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDate.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDate.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDate.java
new file mode 100644
index 0000000..9ab2226
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDate.java
@@ -0,0 +1,192 @@
+/*
+ * 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.types;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.phoenix.schema.SortOrder;
+import org.apache.phoenix.util.DateUtil;
+
+import java.math.BigDecimal;
+import java.sql.Date;
+import java.sql.Types;
+import java.text.Format;
+
+public class PDate extends PDataType<Date> {
+
+  public static final PDate INSTANCE = new PDate();
+
+  private PDate() {
+    super("DATE", Types.DATE, Date.class,
+        new DateCodec(), 11); // After TIMESTAMP and DATE to ensure toLiteral finds those first
+  }
+
+  @Override
+  public byte[] toBytes(Object object) {
+    if (object == null) {
+      throw newIllegalDataException(this + " may not be null");
+    }
+    byte[] bytes = new byte[getByteSize()];
+    toBytes(object, bytes, 0);
+    return bytes;
+  }
+
+  @Override
+  public int toBytes(Object object, byte[] bytes, int offset) {
+    if (object == null) {
+      throw newIllegalDataException(this + " may not be null");
+    }
+    getCodec().encodeLong(((java.util.Date) object).getTime(), bytes, offset);
+    return this.getByteSize();
+  }
+
+  @Override
+  public Object toObject(Object object, PDataType actualType) {
+    if (object == null) {
+      return null;
+    }
+    if (equalsAny(actualType, PTime.INSTANCE, PUnsignedTime.INSTANCE)) {
+      return new Date(((java.sql.Time) object).getTime());
+    } else if (equalsAny(actualType, PTimestamp.INSTANCE, PUnsignedTimestamp.INSTANCE)) {
+      return new Date(((java.sql.Timestamp) object).getTime());
+    } else if (equalsAny(actualType, PDate.INSTANCE, PUnsignedDate.INSTANCE)) {
+      return object;
+    } else if (equalsAny(actualType, PLong.INSTANCE, PUnsignedLong.INSTANCE)) {
+      return new Date((Long) object);
+    } else if (actualType == PDecimal.INSTANCE) {
+      return new Date(((BigDecimal) object).longValueExact());
+    }
+    return throwConstraintViolationException(actualType, this);
+  }
+
+  @Override
+  public Date toObject(byte[] b, int o, int l, PDataType actualType, SortOrder sortOrder, Integer maxLength, Integer scale) {
+    if (l == 0) {
+      return null;
+    }
+    if (equalsAny(actualType, PTimestamp.INSTANCE, PUnsignedTimestamp.INSTANCE, PDate.INSTANCE,
+        PUnsignedDate.INSTANCE, PTime.INSTANCE, PUnsignedTime.INSTANCE, PLong.INSTANCE,
+        PUnsignedLong.INSTANCE)) {
+      return new Date(actualType.getCodec().decodeLong(b, o, sortOrder));
+    } else if (actualType == PDecimal.INSTANCE) {
+      BigDecimal bd = (BigDecimal) actualType.toObject(b, o, l, actualType, sortOrder);
+      return new Date(bd.longValueExact());
+    }
+    throwConstraintViolationException(actualType, this);
+    return null;
+  }
+
+  @Override
+  public boolean isCastableTo(PDataType targetType) {
+    return super.isCastableTo(targetType) || equalsAny(targetType, PDecimal.INSTANCE, PLong.INSTANCE, PUnsignedLong.INSTANCE);
+  }
+
+  @Override
+  public boolean isCoercibleTo(PDataType targetType) {
+    return equalsAny(targetType, PDate.INSTANCE, PTime.INSTANCE, PTimestamp.INSTANCE, PVarbinary.INSTANCE, PBinary.INSTANCE);
+  }
+
+  @Override
+  public boolean isCoercibleTo(PDataType targetType, Object value) {
+    if (value != null) {
+      if (equalsAny(targetType, PUnsignedTimestamp.INSTANCE, PUnsignedDate.INSTANCE,
+          PUnsignedTime.INSTANCE)) {
+        return ((java.util.Date) value).getTime() >= 0;
+      }
+    }
+    return super.isCoercibleTo(targetType, value);
+  }
+
+  @Override
+  public boolean isFixedWidth() {
+    return true;
+  }
+
+  @Override
+  public Integer getByteSize() {
+    return Bytes.SIZEOF_LONG;
+  }
+
+  @Override
+  public int compareTo(Object lhs, Object rhs, PDataType rhsType) {
+    if (rhsType == PTimestamp.INSTANCE || rhsType == PUnsignedTimestamp.INSTANCE) {
+      return -rhsType.compareTo(rhs, lhs, PTime.INSTANCE);
+    }
+    return ((java.util.Date) rhs).compareTo((java.util.Date) lhs);
+  }
+
+  @Override
+  public Object toObject(String value) {
+    if (value == null || value.length() == 0) {
+      return null;
+    }
+    return DateUtil.parseDate(value);
+  }
+
+  @Override
+  public boolean isBytesComparableWith(PDataType otherType) {
+    return super.isBytesComparableWith(otherType) || otherType == PTime.INSTANCE;
+  }
+
+  @Override
+  public String toStringLiteral(byte[] b, int offset, int length, Format formatter) {
+    if (formatter == null || formatter == DateUtil.DEFAULT_DATE_FORMATTER) {
+      // If default formatter has not been overridden,
+      // use one that displays milliseconds.
+      formatter = DateUtil.DEFAULT_MS_DATE_FORMATTER;
+    }
+    return "'" + super.toStringLiteral(b, offset, length, formatter) + "'";
+  }
+
+  @Override
+  public void coerceBytes(ImmutableBytesWritable ptr, Object object, PDataType actualType,
+      Integer maxLength, Integer scale, SortOrder actualModifier, Integer desiredMaxLength, Integer desiredScale,
+      SortOrder expectedModifier) {
+    if (ptr.getLength() > 0 && actualType == PTimestamp.INSTANCE
+        && actualModifier == expectedModifier) {
+      ptr.set(ptr.get(), ptr.getOffset(), getByteSize());
+      return;
+    }
+    super.coerceBytes(ptr, object, actualType, maxLength, scale, actualModifier, desiredMaxLength,
+        desiredScale, expectedModifier);
+  }
+
+  @Override
+  public Object getSampleValue(Integer maxLength, Integer arrayLength) {
+    return new Date((Long) PLong.INSTANCE.getSampleValue(maxLength, arrayLength));
+  }
+
+  static class DateCodec extends PLong.LongCodec {
+
+    @Override
+    public int decodeInt(byte[] b, int o, SortOrder sortOrder) {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public PhoenixArrayFactory getPhoenixArrayFactory() {
+      return new PhoenixArrayFactory() {
+
+        @Override
+        public PhoenixArray newArray(PDataType type, Object[] elements) {
+          return new PhoenixArray(type, elements);
+        }
+      };
+    }
+  }
+}

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