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);
+ }
+}