blob: 3dc10501eb97f41549fca8a672fa59f686c0bd84 [file] [log] [blame]
/*
* 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.sql.Array;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Map;
import com.google.common.base.Strings;
import org.apache.phoenix.util.SQLCloseable;
/**
* java.sql.Array implementation for Phoenix
*/
public class PhoenixArray implements Array,SQLCloseable {
private static final String TO_STRING_SEPARATOR = ", ";
private static final String TO_STRING_END = "]";
private static final String TO_STRING_BEGIN = "[";
PDataType baseType;
Object array;
int numElements;
Integer maxLength;
protected int hashCode = Integer.MIN_VALUE;
public PhoenixArray() {
// empty constructor
}
public Integer getMaxLength() {
return maxLength;
}
public boolean isPrimitiveType() {
return this.baseType.getCodec() != null;
}
private static Object[] coerceToNewLength(PDataType baseType, Object[] elements, int maxLength) {
Object[] resizedElements = new Object[elements.length];
for (int i = 0; i < elements.length; i++) {
Integer length = baseType.getMaxLength(elements[i]);
if (length != null) {
if (length == maxLength) {
resizedElements[i] = elements[i];
} else {
resizedElements[i] = baseType.pad(elements[i], maxLength);
}
} else {
resizedElements[i] = baseType.pad(elements[i], maxLength);
}
}
return resizedElements;
}
private static Object[] coerceToEqualLength(PDataType baseType, Object[] elements) {
if (elements == null || elements.length == 0) {
return elements;
}
int maxLength = 0;
boolean resizeElements = false;
for (int i = 0; i < elements.length; i++) {
Integer length = baseType.getMaxLength(elements[i]);
if (length != null) {
if (maxLength == 0){
maxLength = length;
continue;
}
if (length > maxLength) {
maxLength = length;
resizeElements = true;
} else if (length < maxLength) {
resizeElements = true;
}
} else {
resizeElements = true;
}
}
if (!resizeElements) {
return elements;
}
return coerceToNewLength(baseType, elements, maxLength);
}
public PhoenixArray(PDataType baseType, Object[] elements) {
// As we are dealing with primitive types and only the Boxed objects
this.baseType = baseType;
if (baseType.isFixedWidth()) {
if (baseType.getByteSize() == null) {
elements = coerceToEqualLength(baseType, elements);
if (elements != null && elements.length > 0) {
for(int i = 0; i < elements.length; i++) {
if(elements[i] != null) {
maxLength = baseType.getMaxLength(elements[i]);
break;
}
}
}
}
}
this.array = convertObjectArrayToPrimitiveArray(elements);
this.numElements = elements.length;
}
public PhoenixArray(PhoenixArray pArr, Integer desiredMaxLength) {
this.baseType = pArr.baseType;
Object[] elements = (Object[])pArr.array;
if (baseType.isFixedWidth()) {
if (baseType.getByteSize() == null) {
elements = coerceToNewLength(baseType, (Object[])pArr.array, desiredMaxLength);
maxLength = desiredMaxLength;
}
}
this.array = convertObjectArrayToPrimitiveArray(elements);
this.numElements = elements.length;
}
public Object convertObjectArrayToPrimitiveArray(Object[] elements) {
return elements;
}
@Override
public void free() throws SQLException {
}
@Override
public Object getArray() throws SQLException {
return array;
}
@Override
public void close() throws SQLException {
this.array = null;
}
@Override
public Object getArray(Map<String, Class<?>> map) throws SQLException {
throw new UnsupportedOperationException("Currently not supported");
}
@Override
public Object getArray(long index, int count) throws SQLException {
if(index < 1) {
throw new IllegalArgumentException("Index cannot be less than 1");
}
// Get the set of elements from the given index to the specified count
Object[] intArr = (Object[]) array;
boundaryCheck(index, count, intArr);
Object[] newArr = new Object[count];
// Add checks() here.
int i = 0;
for (int j = (int) index; j < count; j++) {
newArr[i] = intArr[j];
i++;
}
return newArr;
}
private void boundaryCheck(long index, int count, Object[] arr) {
if ((--index) + count > arr.length) {
throw new IllegalArgumentException("The array index is out of range of the total number of elements in the array " + arr.length);
}
}
@Override
public Object getArray(long index, int count, Map<String, Class<?>> map)
throws SQLException {
if(map != null && !map.isEmpty()) {
throw new UnsupportedOperationException("Currently not supported");
}
return null;
}
@Override
public int getBaseType() throws SQLException {
return baseType.getSqlType();
}
@Override
public String getBaseTypeName() throws SQLException {
return baseType.getSqlTypeName();
}
@Override
public ResultSet getResultSet() throws SQLException {
throw new UnsupportedOperationException("Currently not supported");
}
@Override
public ResultSet getResultSet(Map<String, Class<?>> arg0)
throws SQLException {
throw new UnsupportedOperationException("Currently not supported");
}
@Override
public ResultSet getResultSet(long arg0, int arg1) throws SQLException {
throw new UnsupportedOperationException("Currently not supported");
}
@Override
public ResultSet getResultSet(long arg0, int arg1,
Map<String, Class<?>> arg2) throws SQLException {
throw new UnsupportedOperationException("Currently not supported");
}
/**
* Return the value in position {@code index} from the underlying array. Used to work around
* casting and reflection while enabling primitive arrays.
*/
public Object getElement(int index) {
return ((Object[]) array)[index];
}
public int getDimensions() {
return this.numElements;
}
public int estimateByteSize(int pos) {
if(((Object[])array)[pos] == null) {
return 0;
}
return this.baseType.estimateByteSize(((Object[])array)[pos]);
}
public Integer getMaxLength(int pos) {
return this.baseType.getMaxLength(((Object[])array)[pos]);
}
public byte[] toBytes(int pos) {
return this.baseType.toBytes(((Object[])array)[pos]);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(TO_STRING_BEGIN);
boolean isFirst = true;
for (int i = 0; i < getDimensions(); i++) {
Object o = getElement(i);
if (isFirst) {
isFirst = false;
} else {
sb.append(TO_STRING_SEPARATOR);
}
sb.append(this.baseType.toStringLiteral(o));
}
sb.append(TO_STRING_END);
return sb.toString();
}
public boolean isNull(int pos) {
if(this.baseType.toBytes(((Object[])array)[pos]).length == 0) {
return true;
} else {
return false;
}
}
@Override
public boolean equals(Object obj) {
if (obj == null) return false;
if (this == obj) return true;
if (!(obj instanceof PhoenixArray)) return false;
PhoenixArray oArray = (PhoenixArray) obj;
if (numElements != oArray.numElements) return false;
if (baseType.getSqlType() != oArray.baseType.getSqlType()) return false;
return Arrays.deepEquals((Object[]) array, (Object[]) oArray.array);
}
@Override
public int hashCode() {
// implementation based on commons.lang.HashCodeBuilder, except the hashcode is cached and
// reused for a given instance.
if (hashCode != Integer.MIN_VALUE) return hashCode;
hashCode = 17;
hashCode = hashCode * 37 + numElements;
hashCode = hashCode * 37 + baseType.getSqlType();
hashCode = hashCode * 37 + Arrays.deepHashCode((Object[]) array);
return hashCode;
}
public static class PrimitiveIntPhoenixArray extends PhoenixArray {
private int[] intArr;
public PrimitiveIntPhoenixArray(PDataType dataType, Object[] elements) {
super(dataType, elements);
}
@Override
public Object convertObjectArrayToPrimitiveArray(Object[] elements) {
intArr = new int[elements.length];
int i = 0;
for (Object o : elements) {
if (o != null) {
intArr[i] = (Integer) o;
}
i++;
}
return intArr;
}
@Override
public int estimateByteSize(int pos) {
return this.baseType.estimateByteSize(intArr[pos]);
}
@Override
public byte[] toBytes(int pos) {
return this.baseType.toBytes(intArr[pos]);
}
@Override
public boolean equals(Object obj) {
if (obj == null) return false;
if (this == obj) return true;
if (this.getClass() != obj.getClass()) return false;
PrimitiveIntPhoenixArray oArray = (PrimitiveIntPhoenixArray) obj;
if (numElements != oArray.numElements) return false;
if (baseType.getSqlType() != oArray.baseType.getSqlType()) return false;
return Arrays.equals((int[]) array, (int[]) oArray.array);
}
@Override
public int hashCode() {
if (hashCode != Integer.MIN_VALUE) return hashCode;
hashCode = 17;
hashCode = hashCode * 37 + numElements;
hashCode = hashCode * 37 + baseType.getSqlType();
hashCode = hashCode * 37 + Arrays.hashCode((int[]) array);
return hashCode;
}
@Override
public Object getElement(int index) {
return ((int[]) array)[index];
}
}
public static class PrimitiveShortPhoenixArray extends PhoenixArray {
private short[] shortArr;
public PrimitiveShortPhoenixArray(PDataType dataType, Object[] elements) {
super(dataType, elements);
}
@Override
public Object convertObjectArrayToPrimitiveArray(Object[] elements) {
shortArr = new short[elements.length];
int i = 0;
for(Object o : elements) {
if (o != null) {
shortArr[i] = (Short)o;
}
i++;
}
return shortArr;
}
@Override
public int estimateByteSize(int pos) {
return this.baseType.estimateByteSize(shortArr[pos]);
}
@Override
public byte[] toBytes(int pos) {
return this.baseType.toBytes(shortArr[pos]);
}
@Override
public boolean equals(Object obj) {
if (obj == null) return false;
if (this == obj) return true;
if (this.getClass() != obj.getClass()) return false;
PrimitiveShortPhoenixArray oArray = (PrimitiveShortPhoenixArray) obj;
if (numElements != oArray.numElements) return false;
if (baseType.getSqlType() != oArray.baseType.getSqlType()) return false;
return Arrays.equals((short[]) array, (short[]) oArray.array);
}
@Override
public int hashCode() {
if (hashCode != Integer.MIN_VALUE) return hashCode;
hashCode = 17;
hashCode = hashCode * 37 + numElements;
hashCode = hashCode * 37 + baseType.getSqlType();
hashCode = hashCode * 37 + Arrays.hashCode((short[]) array);
return hashCode;
}
@Override
public Object getElement(int index) {
return ((short[]) array)[index];
}
}
public static class PrimitiveLongPhoenixArray extends PhoenixArray {
private long[] longArr;
public PrimitiveLongPhoenixArray(PDataType dataType, Object[] elements) {
super(dataType, elements);
}
@Override
public Object convertObjectArrayToPrimitiveArray(Object[] elements) {
longArr = new long[elements.length];
int i = 0;
for(Object o : elements) {
if (o != null) {
longArr[i] = (Long)o;
}
i++;
}
return longArr;
}
@Override
public int estimateByteSize(int pos) {
return this.baseType.estimateByteSize(longArr[pos]);
}
@Override
public byte[] toBytes(int pos) {
return this.baseType.toBytes(longArr[pos]);
}
@Override
public boolean equals(Object obj) {
if (obj == null) return false;
if (this == obj) return true;
if (this.getClass() != obj.getClass()) return false;
PrimitiveLongPhoenixArray oArray = (PrimitiveLongPhoenixArray) obj;
if (numElements != oArray.numElements) return false;
if (baseType.getSqlType() != oArray.baseType.getSqlType()) return false;
return Arrays.equals((long[]) array, (long[]) oArray.array);
}
@Override
public int hashCode() {
if (hashCode != Integer.MIN_VALUE) return hashCode;
hashCode = 17;
hashCode = hashCode * 37 + numElements;
hashCode = hashCode * 37 + baseType.getSqlType();
hashCode = hashCode * 37 + Arrays.hashCode((long[]) array);
return hashCode;
}
@Override
public Object getElement(int index) {
return ((long[]) array)[index];
}
}
public static class PrimitiveDoublePhoenixArray extends PhoenixArray {
private double[] doubleArr;
public PrimitiveDoublePhoenixArray(PDataType dataType, Object[] elements) {
super(dataType, elements);
}
@Override
public Object convertObjectArrayToPrimitiveArray(Object[] elements) {
doubleArr = new double[elements.length];
int i = 0;
for (Object o : elements) {
if (o != null) {
doubleArr[i] = (Double) o;
}
i++;
}
return doubleArr;
}
@Override
public int estimateByteSize(int pos) {
return this.baseType.estimateByteSize(doubleArr[pos]);
}
@Override
public byte[] toBytes(int pos) {
return this.baseType.toBytes(doubleArr[pos]);
}
@Override
public boolean equals(Object obj) {
if (obj == null) return false;
if (this == obj) return true;
if (this.getClass() != obj.getClass()) return false;
PrimitiveDoublePhoenixArray oArray = (PrimitiveDoublePhoenixArray) obj;
if (numElements != oArray.numElements) return false;
if (baseType.getSqlType() != oArray.baseType.getSqlType()) return false;
return Arrays.equals((double[]) array, (double[]) oArray.array);
}
@Override
public int hashCode() {
if (hashCode != Integer.MIN_VALUE) return hashCode;
hashCode = 17;
hashCode = hashCode * 37 + numElements;
hashCode = hashCode * 37 + baseType.getSqlType();
hashCode = hashCode * 37 + Arrays.hashCode((double[]) array);
return hashCode;
}
@Override
public Object getElement(int index) {
return ((double[]) array)[index];
}
}
public static class PrimitiveFloatPhoenixArray extends PhoenixArray {
private float[] floatArr;
public PrimitiveFloatPhoenixArray(PDataType dataType, Object[] elements) {
super(dataType, elements);
}
@Override
public Object convertObjectArrayToPrimitiveArray(Object[] elements) {
floatArr = new float[elements.length];
int i = 0;
for(Object o : elements) {
if (o != null) {
floatArr[i] = (Float)o;
}
i++;
}
return floatArr;
}
@Override
public int estimateByteSize(int pos) {
return this.baseType.estimateByteSize(floatArr[pos]);
}
@Override
public byte[] toBytes(int pos) {
return this.baseType.toBytes(floatArr[pos]);
}
@Override
public boolean equals(Object obj) {
if (obj == null) return false;
if (this == obj) return true;
if (this.getClass() != obj.getClass()) return false;
PrimitiveFloatPhoenixArray oArray = (PrimitiveFloatPhoenixArray) obj;
if (numElements != oArray.numElements) return false;
if (baseType.getSqlType() != oArray.baseType.getSqlType()) return false;
return Arrays.equals((float[]) array, (float[]) oArray.array);
}
@Override
public int hashCode() {
if (hashCode != Integer.MIN_VALUE) return hashCode;
hashCode = 17;
hashCode = hashCode * 37 + numElements;
hashCode = hashCode * 37 + baseType.getSqlType();
hashCode = hashCode * 37 + Arrays.hashCode((float[]) array);
return hashCode;
}
@Override
public Object getElement(int index) {
return ((float[]) array)[index];
}
}
public static class PrimitiveBytePhoenixArray extends PhoenixArray {
private byte[] byteArr;
public PrimitiveBytePhoenixArray(PDataType dataType, Object[] elements) {
super(dataType, elements);
}
@Override
public Object convertObjectArrayToPrimitiveArray(Object[] elements) {
byteArr = new byte[elements.length];
int i = 0;
for(Object o : elements) {
if (o != null) {
byteArr[i] = (Byte)o;
}
i++;
}
return byteArr;
}
@Override
public int estimateByteSize(int pos) {
return this.baseType.estimateByteSize(byteArr[pos]);
}
@Override
public byte[] toBytes(int pos) {
return this.baseType.toBytes(byteArr[pos]);
}
@Override
public boolean equals(Object obj) {
if (obj == null) return false;
if (this == obj) return true;
if (this.getClass() != obj.getClass()) return false;
PrimitiveBytePhoenixArray oArray = (PrimitiveBytePhoenixArray) obj;
if (numElements != oArray.numElements) return false;
if (baseType.getSqlType() != oArray.baseType.getSqlType()) return false;
return Arrays.equals((byte[]) array, (byte[]) oArray.array);
}
@Override
public int hashCode() {
if (hashCode != Integer.MIN_VALUE) return hashCode;
hashCode = 17;
hashCode = hashCode * 37 + numElements;
hashCode = hashCode * 37 + baseType.getSqlType();
hashCode = hashCode * 37 + Arrays.hashCode((byte[]) array);
return hashCode;
}
@Override
public Object getElement(int index) {
return ((byte[]) array)[index];
}
}
public static class PrimitiveBooleanPhoenixArray extends PhoenixArray {
private boolean[] booleanArr;
public PrimitiveBooleanPhoenixArray(PDataType dataType, Object[] elements) {
super(dataType, elements);
}
@Override
public boolean isPrimitiveType() {
// boolean is primitive although PBoolean has no codec
return true;
}
@Override
public Object convertObjectArrayToPrimitiveArray(Object[] elements) {
booleanArr = new boolean[elements.length];
int i = 0;
for (Object o : elements) {
if (o != null) {
booleanArr[i] = (Boolean) o;
}
i++;
}
return booleanArr;
}
@Override
public int estimateByteSize(int pos) {
return this.baseType.estimateByteSize(booleanArr[pos]);
}
@Override
public byte[] toBytes(int pos) {
return this.baseType.toBytes(booleanArr[pos]);
}
@Override
public boolean equals(Object obj) {
if (obj == null) return false;
if (this == obj) return true;
if (this.getClass() != obj.getClass()) return false;
PrimitiveBooleanPhoenixArray oArray = (PrimitiveBooleanPhoenixArray) obj;
if (numElements != oArray.numElements) return false;
if (baseType.getSqlType() != oArray.baseType.getSqlType()) return false;
return Arrays.equals((boolean[]) array, (boolean[]) oArray.array);
}
@Override
public int hashCode() {
if (hashCode != Integer.MIN_VALUE) return hashCode;
hashCode = 17;
hashCode = hashCode * 37 + numElements;
hashCode = hashCode * 37 + baseType.getSqlType();
hashCode = hashCode * 37 + Arrays.hashCode((boolean[]) array);
return hashCode;
}
@Override
public Object getElement(int index) {
return ((boolean[]) array)[index];
}
}
}