PHOENIX-2313 TypeMismatchException thrown while querying a table that has an index with a Boolean
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/BaseMutableIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/BaseMutableIndexIT.java
index 68998cf..ea12245 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/BaseMutableIndexIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/BaseMutableIndexIT.java
@@ -118,12 +118,17 @@
"create " + (localIndex ? "LOCAL" : "") + " index i on t(b)");
conn1.createStatement().execute("upsert into t values(1,true,'foo')");
conn1.createStatement().execute("upsert into t values(2,false,'foo')");
+ conn1.createStatement().execute("upsert into t values(3)");
conn1.commit();
ResultSet rs = conn1.createStatement().executeQuery("select b from t");
- rs.next();
- assertEquals(true, rs.getBoolean(1));
- rs.next();
+ assertTrue(rs.next());
assertEquals(false, rs.getBoolean(1));
+ assertTrue(rs.wasNull());
+ assertTrue(rs.next());
+ assertEquals(false, rs.getBoolean(1));
+ assertTrue(rs.next());
+ assertEquals(true, rs.getBoolean(1));
+ assertFalse(rs.next());
}
@Test
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
index 9892426..f90d70b 100644
--- 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
@@ -27,122 +27,120 @@
public class PBoolean extends PDataType<Boolean> {
- public static final PBoolean INSTANCE = new PBoolean();
+ 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");
+ private PBoolean() {
+ super("BOOLEAN", Types.BOOLEAN, Boolean.class, null, 21);
}
- 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);
- }
- if (actualType == PDecimal.INSTANCE) {
- return ((BigDecimal) object).equals(BigDecimal.ONE) ? Boolean.TRUE : Boolean.FALSE;
+ @Override
+ public byte[] toBytes(Object object) {
+ if (object == null) {
+ // TODO: review - return null?
+ throw newIllegalDataException(this + " may not be null");
}
- return throwConstraintViolationException(actualType, this);
- }
+ return ((Boolean) object).booleanValue() ? TRUE_BYTES : FALSE_BYTES;
+ }
- @Override
- public Object getSampleValue(Integer maxLength, Integer arrayLength) {
- return RANDOM.get().nextBoolean();
- }
-
+ @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 ?
+ FALSE_BYTES :
+ TRUE_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 sortOrder == SortOrder.DESC ? SortOrder.invert(bytes[offset]) != ZERO_BYTE : bytes[offset] != ZERO_BYTE;
+ }
+ 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);
+ }
+ if (actualType == PDecimal.INSTANCE) {
+ return ((BigDecimal) object).equals(BigDecimal.ZERO) ? Boolean.FALSE : Boolean.TRUE;
+ }
+ return throwConstraintViolationException(actualType, this);
+ }
+
+ @Override
+ public Object getSampleValue(Integer maxLength, Integer arrayLength) {
+ return RANDOM.get().nextBoolean();
+ }
+
@Override
public PhoenixArrayFactory getArrayFactory() {
return new PhoenixArrayFactory() {
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/schema/types/PDataTypeTest.java b/phoenix-core/src/test/java/org/apache/phoenix/schema/types/PDataTypeTest.java
index 5657c22..7a04aeb 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/schema/types/PDataTypeTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/schema/types/PDataTypeTest.java
@@ -1799,4 +1799,37 @@
assertTrue(Bytes.compareTo(array, QueryConstants.DESC_SEPARATOR_BYTE_ARRAY) <= 0);
}
}
+
+ @Test
+ public void testBoolean() {
+ byte[] bytes = PBoolean.INSTANCE.toBytes(Boolean.TRUE);
+ assertEquals(1, bytes[0]);
+ bytes = PBoolean.INSTANCE.toBytes(Boolean.FALSE);
+ assertEquals(0, bytes[0]);
+
+ bytes = PBoolean.INSTANCE.toBytes(Boolean.TRUE, SortOrder.DESC);
+ assertEquals(0, bytes[0]);
+ bytes = PBoolean.INSTANCE.toBytes(Boolean.FALSE, SortOrder.DESC);
+ assertEquals(1, bytes[0]);
+
+ Object dec = PDecimal.INSTANCE.toObject(Boolean.TRUE, PBoolean.INSTANCE);
+ bytes = PDecimal.INSTANCE.toBytes(dec);
+ Object b = PBoolean.INSTANCE.toObject(bytes, 0, bytes.length, PDecimal.INSTANCE, SortOrder.ASC);
+ assertEquals(true, b);
+
+ dec = PDecimal.INSTANCE.toObject(Boolean.FALSE, PBoolean.INSTANCE);
+ bytes = PDecimal.INSTANCE.toBytes(dec);
+ b = PBoolean.INSTANCE.toObject(bytes, 0, bytes.length, PDecimal.INSTANCE, SortOrder.ASC);
+ assertEquals(false, b);
+
+ dec = PDecimal.INSTANCE.toObject(Boolean.TRUE, PBoolean.INSTANCE);
+ bytes = PDecimal.INSTANCE.toBytes(dec, SortOrder.DESC);
+ b = PBoolean.INSTANCE.toObject(bytes, 0, bytes.length, PDecimal.INSTANCE, SortOrder.DESC);
+ assertEquals(true, b);
+
+ dec = PDecimal.INSTANCE.toObject(Boolean.FALSE, PBoolean.INSTANCE);
+ bytes = PDecimal.INSTANCE.toBytes(dec, SortOrder.DESC);
+ b = PBoolean.INSTANCE.toObject(bytes, 0, bytes.length, PDecimal.INSTANCE, SortOrder.DESC);
+ assertEquals(false, b);
+ }
}