All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.snappydata.thrift.common.OptimizedElementArray Maven / Gradle / Ivy

There is a newer version: 1.6.7
Show newest version
/*
 * Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
 *
 * Licensed 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. See accompanying
 * LICENSE file.
 */
/*
 * Changes for SnappyData data platform.
 *
 * Portions Copyright (c) 2017-2019 TIBCO Software Inc. All rights reserved.
 *
 * Licensed 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. See accompanying
 * LICENSE file.
 */

package io.snappydata.thrift.common;

import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.sql.Date;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.gemstone.gemfire.internal.shared.ClientSharedUtils;
import com.pivotal.gemfirexd.internal.shared.common.ResolverUtils;
import io.snappydata.thrift.BlobChunk;
import io.snappydata.thrift.ClobChunk;
import io.snappydata.thrift.ColumnDescriptor;
import io.snappydata.thrift.ColumnValue;
import io.snappydata.thrift.Decimal;
import io.snappydata.thrift.SnappyType;
import org.apache.spark.unsafe.Platform;
import org.apache.thrift.TBaseHelper;
import org.apache.thrift.TException;
import org.apache.thrift.protocol.TField;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.protocol.TProtocolException;

/**
 * A compact way to represent a set of primitive and non-primitive values. The
 * optimization is only useful when there a multiple primitive values
 * (int/byte/long/short/float/double/boolean) in the list.
 * 

* This packs the primitives in a byte[] while non-primitives are packed in an * Object[] to minimize the memory overhead. Consequently it is not efficient to * directly iterate over this as a List. Users of this class should normally * find the type of the element at a position, and then invoke the appropriate * getter instead, e.g. if the type of element is {@link SnappyType#INTEGER} * then use the getInt() method. *

* Note that the getters and setters of this class do not do any type checks or * type conversions by design and will happily get/set garbage or throw * {@link IndexOutOfBoundsException} or {@link NullPointerException} assuming * that caller has done the requisite type checks. *

* Layout of the primitives array: *

 *   .----------------------- 1 byte type for each field (-ve of type for null)
 *   |
 *   |       .--------------- Long for each field. Value as long for primitives.
 *   |       |                Index into Object array for non-primitives.
 *   |       |
 *   |       |
 *   V       V
 *   +-------+---------------+
 *   |       |               |
 *   +-------+---------------+
 *    \-----/ \-------------/
 *     header      body
 * 
*/ public class OptimizedElementArray { /** * Holds the primitive values with types at start (-ve of type for nulls). * Also has the offset into {@link #nonPrimitives} for non-primitives. */ protected long[] primitives; protected Object[] nonPrimitives; protected int nonPrimSize; protected int headerSize; protected transient int hash; protected boolean hasLobs; protected static final long[] EMPTY = new long[0]; protected OptimizedElementArray() { this.primitives = EMPTY; } protected OptimizedElementArray(OptimizedElementArray other) { this(other, false, true); } protected OptimizedElementArray(OptimizedElementArray other, boolean otherIsEmpty, boolean copyValues) { final long[] prims = other.primitives; final Object[] nonPrims = other.nonPrimitives; this.headerSize = other.headerSize; if (prims.length > 0) { // make a copy of primitives array in any case regardless of // "copyValues" since the types and non-primitive indexes are required this.primitives = prims.clone(); } else { this.primitives = EMPTY; } if (nonPrims != null) { // copy values only if other row has some non-primitive values if (copyValues && !otherIsEmpty) { this.nonPrimitives = nonPrims.clone(); } else { this.nonPrimitives = new Object[nonPrims.length]; } } this.nonPrimSize = other.nonPrimSize; this.hasLobs = other.hasLobs; if (copyValues) { this.hash = other.hash; } else { // mark primitives as non-null since setters do not mark values // as non-null, and mark non-primitives as null setDefaultNullability(); } } /** * Initialize the array for given {@link ColumnDescriptor}s. * * @param metadata the list of {@link ColumnDescriptor}s ordered by their position */ public OptimizedElementArray(final List metadata, boolean checkOutputParameters) { int nonPrimitiveIndex = 0; int numFields = metadata.size(); // remove any output parameters at the end if (checkOutputParameters) { for (int rIndex = numFields - 1; rIndex >= 0; rIndex--) { if (!metadata.get(rIndex).isParameterOut()) { numFields = rIndex + 1; break; } } } // a byte for type of each field at the start final int headerSize = (numFields + 7) >>> 3; this.primitives = new long[headerSize + numFields]; for (int index = 0; index < numFields; index++) { final ColumnDescriptor cd = metadata.get(index); final SnappyType type = cd.type; switch (type) { case INTEGER: case BIGINT: case DOUBLE: case FLOAT: case DATE: case TIMESTAMP: case TIME: case SMALLINT: case BOOLEAN: case TINYINT: case NULLTYPE: setType(index, type.getValue()); // skip primitives that will be stored directly in "primitives" break; case BLOB: case CLOB: // set the position for non-primitives this.primitives[headerSize + index] = nonPrimitiveIndex++; // mark null setType(index, -type.getValue()); this.hasLobs = true; break; default: // set the position for non-primitives this.primitives[headerSize + index] = nonPrimitiveIndex++; // mark null setType(index, -type.getValue()); break; } } if (nonPrimitiveIndex > 0) { this.nonPrimitives = new Object[nonPrimitiveIndex]; this.nonPrimSize = nonPrimitiveIndex; } this.headerSize = headerSize; } private void setDefaultNullability() { final long[] primitives = this.primitives; final int end = Platform.LONG_ARRAY_OFFSET + size(); for (int offset = Platform.LONG_ARRAY_OFFSET; offset < end; offset++) { final byte snappyType = Platform.getByte(primitives, offset); switch (Math.abs(snappyType)) { case 4: // INTEGER case 5: // BIGINT case 7: // DOUBLE case 6: // FLOAT case 12: // DATE case 13: // TIME case 14: // TIMESTAMP case 3: // SMALLINT case 1: // BOOLEAN case 2: // TINYINT case 24: // NULLTYPE // primitives must be marked non-null if (snappyType < 0) { Platform.putByte(primitives, offset, (byte)-snappyType); } break; default: // non-primitives must be marked null if (snappyType > 0) { Platform.putByte(primitives, offset, (byte)-snappyType); } } } } public final void setType(int index, int snappyType) { Platform.putByte(this.primitives, Platform.LONG_ARRAY_OFFSET + index, (byte)snappyType); } /** * Returns the {@link SnappyType#getValue()} type for the column at given * index. The returned type will be negative if the column value is null. * * @param index 0-based index of the column */ public final int getType(int index) { return Platform.getByte(primitives, Platform.LONG_ARRAY_OFFSET + index); } public final boolean isNull(int index) { return getType(index) < 0; } public final boolean getBoolean(int index) { return getByte(index) != 0; } public final byte getByte(int index) { return (byte)this.primitives[headerSize + index]; } public final short getShort(int index) { return (short)this.primitives[headerSize + index]; } public final int getInt(int index) { return (int)this.primitives[headerSize + index]; } public final long getLong(int index) { return this.primitives[headerSize + index]; } public final float getFloat(int index) { return Float.intBitsToFloat(getInt(index)); } public final double getDouble(int index) { return Double.longBitsToDouble(getLong(index)); } public final Date getDate(int index) { return Converters.getDate(getLong(index)); } public final Time getTime(int index) { return Converters.getTime(getLong(index)); } public final long getDateTimeMillis(int index) { return getLong(index) * 1000L; } public final Timestamp getTimestamp(int index) { return Converters.getTimestamp(getLong(index)); } public final Object getObject(int index) { return this.nonPrimitives[(int)this.primitives[headerSize + index]]; } public final void initializeLobs(LobService lobService) throws SQLException { if (!hasLobs || nonPrimitives == null) return; final Object[] nonPrimitives = this.nonPrimitives; final long[] primitives = this.primitives; final int headerSize = this.headerSize; final int size = primitives.length - headerSize; for (int index = 0; index < size; index++) { final int type = getType(index); if (type == SnappyType.BLOB.getValue()) { final int lobIndex = (int)primitives[headerSize + index]; if (nonPrimitives[lobIndex] instanceof BlobChunk) { BlobChunk chunk = (BlobChunk)nonPrimitives[lobIndex]; nonPrimitives[lobIndex] = lobService.createBlob(chunk, false); } } else if (type == SnappyType.CLOB.getValue()) { final int lobIndex = (int)primitives[headerSize + index]; if (nonPrimitives[lobIndex] instanceof ClobChunk) { ClobChunk chunk = (ClobChunk)nonPrimitives[lobIndex]; nonPrimitives[lobIndex] = lobService.createClob(chunk, false); } } } } public final void setNull(int index) { int snappyType = getType(index); if (snappyType > 0) { setType(index, -snappyType); } } public final void setNull(int index, int snappyType) { setType(index, -snappyType); } public final int setNotNull(int index) { int snappyType = getType(index); if (snappyType < 0) { snappyType = -snappyType; setType(index, snappyType); } return snappyType; } protected final void setPrimLong(int primIndex, long value) { this.primitives[primIndex] = value; } public final void setBoolean(int index, boolean value) { setPrimLong(headerSize + index, value ? 1L : 0L); } public final void setByte(int index, byte value) { setPrimLong(headerSize + index, value); } public final void setShort(int index, short value) { setPrimLong(headerSize + index, value); } public final void setInt(int index, int value) { setPrimLong(headerSize + index, value); } public final void setLong(int index, long value) { setPrimLong(headerSize + index, value); } public final void setFloat(int index, float value) { setPrimLong(headerSize + index, Float.floatToIntBits(value)); } public final void setDouble(int index, double value) { setPrimLong(headerSize + index, Double.doubleToLongBits(value)); } public final void setDateTime(int index, java.util.Date date) { setPrimLong(headerSize + index, Converters.getDateTime(date)); } public final void setTimestamp(int index, Timestamp ts) { setPrimLong(headerSize + index, Converters.getTimestampNanos(ts)); } public final void setTimestamp(int index, java.util.Date ts) { setPrimLong(headerSize + index, Converters.getTimestampNanos(ts)); } public final void setObject(int index, Object value, SnappyType type) { if (value != null) { this.nonPrimitives[(int)this.primitives[headerSize + index]] = value; // we force change the type below for non-primitives which will work // fine since conversions will be changed on server if (getType(index) != type.getValue()) { setType(index, type.getValue()); } } else { setNull(index, type.getValue()); } } static { // check the type mapping which is hard-coded in switch-case matches // to avoid looking up SnappyType for typeId for every column in every row if (SnappyType.BOOLEAN.getValue() != 1) { throw new AssertionError("typeId mismatch"); } if (SnappyType.TINYINT.getValue() != 2) { throw new AssertionError("typeId mismatch"); } if (SnappyType.SMALLINT.getValue() != 3) { throw new AssertionError("typeId mismatch"); } if (SnappyType.INTEGER.getValue() != 4) { throw new AssertionError("typeId mismatch"); } if (SnappyType.BIGINT.getValue() != 5) { throw new AssertionError("typeId mismatch"); } if (SnappyType.FLOAT.getValue() != 6) { throw new AssertionError("typeId mismatch"); } if (SnappyType.DOUBLE.getValue() != 7) { throw new AssertionError("typeId mismatch"); } if (SnappyType.DECIMAL.getValue() != 8) { throw new AssertionError("typeId mismatch"); } if (SnappyType.CHAR.getValue() != 9) { throw new AssertionError("typeId mismatch"); } if (SnappyType.VARCHAR.getValue() != 10) { throw new AssertionError("typeId mismatch"); } if (SnappyType.LONGVARCHAR.getValue() != 11) { throw new AssertionError("typeId mismatch"); } if (SnappyType.DATE.getValue() != 12) { throw new AssertionError("typeId mismatch"); } if (SnappyType.TIME.getValue() != 13) { throw new AssertionError("typeId mismatch"); } if (SnappyType.TIMESTAMP.getValue() != 14) { throw new AssertionError("typeId mismatch"); } if (SnappyType.BINARY.getValue() != 15) { throw new AssertionError("typeId mismatch"); } if (SnappyType.VARBINARY.getValue() != 16) { throw new AssertionError("typeId mismatch"); } if (SnappyType.LONGVARBINARY.getValue() != 17) { throw new AssertionError("typeId mismatch"); } if (SnappyType.BLOB.getValue() != 18) { throw new AssertionError("typeId mismatch"); } if (SnappyType.CLOB.getValue() != 19) { throw new AssertionError("typeId mismatch"); } if (SnappyType.SQLXML.getValue() != 20) { throw new AssertionError("typeId mismatch"); } if (SnappyType.ARRAY.getValue() != 21) { throw new AssertionError("typeId mismatch"); } if (SnappyType.MAP.getValue() != 22) { throw new AssertionError("typeId mismatch"); } if (SnappyType.STRUCT.getValue() != 23) { throw new AssertionError("typeId mismatch"); } if (SnappyType.NULLTYPE.getValue() != 24) { throw new AssertionError("typeId mismatch"); } if (SnappyType.JSON.getValue() != 25) { throw new AssertionError("typeId mismatch"); } if (SnappyType.JAVA_OBJECT.getValue() != 26) { throw new AssertionError("typeId mismatch"); } if (SnappyType.OTHER.getValue() != 27) { throw new AssertionError("typeId mismatch"); } if (SnappyType.findByValue(28) != null) { throw new AssertionError("unhandled typeId 29"); } } /** * this should exactly match ColumnValue.standardSchemeWriteValue */ public final void writeStandardScheme(final BitSet changedColumns, TProtocol oprot) throws TException { final long[] primitives = this.primitives; final Object[] nonPrimitives = this.nonPrimitives; int offset = this.headerSize; int index; final int size; if (changedColumns == null) { index = 0; size = size(); if (size == 0) return; } else { index = changedColumns.nextSetBit(0); size = 0; offset += index; if (index < 0) return; } while (true) { oprot.writeStructBegin(ColumnValue.STRUCT_DESC); // nulls will always have -ve types so no need for further null checks final int snappyType = getType(index); // check more common types first switch (snappyType) { case 9: // CHAR case 10: // VARCHAR case 11: // LONGVARCHAR oprot.writeFieldBegin(ColumnValue.STRING_VAL_FIELD_DESC); oprot.writeString((String)nonPrimitives[(int)primitives[offset]]); break; case 4: // INTEGER oprot.writeFieldBegin(ColumnValue.I32_VAL_FIELD_DESC); oprot.writeI32((int)primitives[offset]); break; case 5: // BIGINT oprot.writeFieldBegin(ColumnValue.I64_VAL_FIELD_DESC); oprot.writeI64(primitives[offset]); break; case 12: // DATE oprot.writeFieldBegin(ColumnValue.DATE_VAL_FIELD_DESC); oprot.writeI64(primitives[offset]); break; case 14: // TIMESTAMP oprot.writeFieldBegin(ColumnValue.TIMESTAMP_VAL_FIELD_DESC); oprot.writeI64(primitives[offset]); break; case 7: // DOUBLE oprot.writeFieldBegin(ColumnValue.DOUBLE_VAL_FIELD_DESC); oprot.writeDouble(Double.longBitsToDouble(primitives[offset])); break; case 8: // DECIMAL oprot.writeFieldBegin(ColumnValue.DECIMAL_VAL_FIELD_DESC); BigDecimal decimal = (BigDecimal)nonPrimitives[(int)primitives[offset]]; Converters.getDecimal(decimal).write(oprot); break; case 6: // FLOAT oprot.writeFieldBegin(ColumnValue.FLOAT_VAL_FIELD_DESC); oprot.writeI32((int)primitives[offset]); break; case 3: // SMALLINT oprot.writeFieldBegin(ColumnValue.I16_VAL_FIELD_DESC); oprot.writeI16((short)primitives[offset]); break; case 1: // BOOLEAN oprot.writeFieldBegin(ColumnValue.BOOL_VAL_FIELD_DESC); oprot.writeBool(primitives[offset] != 0); break; case 2: // TINYINT oprot.writeFieldBegin(ColumnValue.BYTE_VAL_FIELD_DESC); oprot.writeByte((byte)primitives[offset]); break; case 13: // TIME oprot.writeFieldBegin(ColumnValue.TIME_VAL_FIELD_DESC); oprot.writeI64(primitives[offset]); break; case 19: // CLOB case 20: // SQLXML case 25: // JSON oprot.writeFieldBegin(ColumnValue.CLOB_VAL_FIELD_DESC); ClobChunk clob = (ClobChunk)nonPrimitives[(int)primitives[offset]]; clob.write(oprot); break; case 18: // BLOB oprot.writeFieldBegin(ColumnValue.BLOB_VAL_FIELD_DESC); BlobChunk blob = (BlobChunk)nonPrimitives[(int)primitives[offset]]; blob.write(oprot); break; case 15: // BINARY case 16: // VARBINARY case 17: // LONGVARBINARY oprot.writeFieldBegin(ColumnValue.BINARY_VAL_FIELD_DESC); byte[] bytes = (byte[])nonPrimitives[(int)primitives[offset]]; oprot.writeBinary(ByteBuffer.wrap(bytes)); break; case 21: // ARRAY oprot.writeFieldBegin(ColumnValue.ARRAY_VAL_FIELD_DESC); @SuppressWarnings("unchecked") List list = (List)nonPrimitives[(int)primitives[offset]]; oprot.writeListBegin(new org.apache.thrift.protocol.TList( org.apache.thrift.protocol.TType.STRUCT, list.size())); for (ColumnValue cv : list) { cv.write(oprot); } oprot.writeListEnd(); break; case 22: // MAP oprot.writeFieldBegin(ColumnValue.MAP_VAL_FIELD_DESC); @SuppressWarnings("unchecked") Map map = (Map)nonPrimitives[(int)primitives[offset]]; oprot.writeMapBegin(new org.apache.thrift.protocol.TMap( org.apache.thrift.protocol.TType.STRUCT, org.apache.thrift.protocol.TType.STRUCT, map.size())); for (Map.Entry entry : map.entrySet()) { entry.getKey().write(oprot); entry.getValue().write(oprot); } oprot.writeMapEnd(); break; case 23: // STRUCT oprot.writeFieldBegin(ColumnValue.STRUCT_VAL_FIELD_DESC); @SuppressWarnings("unchecked") List struct = (List)nonPrimitives[(int)primitives[offset]]; oprot.writeListBegin(new org.apache.thrift.protocol.TList( org.apache.thrift.protocol.TType.STRUCT, struct.size())); for (ColumnValue cv : struct) { cv.write(oprot); } oprot.writeListEnd(); break; case 24: // NULLTYPE oprot.writeFieldBegin(ColumnValue.NULL_VAL_FIELD_DESC); // not-null case since for null type will be -ve oprot.writeBool(false); break; case 26: // JAVA_OBJECT oprot.writeFieldBegin(ColumnValue.JAVA_VAL_FIELD_DESC); byte[] objBytes = ((Converters.JavaObjectWrapper)nonPrimitives[ (int)primitives[offset]]).getSerialized(); oprot.writeBinary(ByteBuffer.wrap(objBytes)); break; default: // check for null if (snappyType < 0) { oprot.writeFieldBegin(ColumnValue.NULL_VAL_FIELD_DESC); oprot.writeBool(true); } else { throw new TProtocolException("write: unhandled typeId=" + snappyType + " at index=" + index + " with size=" + size + "(changedCols=" + changedColumns + ")"); } } oprot.writeFieldEnd(); oprot.writeFieldStop(); oprot.writeStructEnd(); if (changedColumns == null) { index++; offset++; if (index >= size) return; } else { index = changedColumns.nextSetBit(index); offset = headerSize + index; if (index < 0) return; } } } public final void readStandardScheme(final int numFields, TProtocol iprot) throws TException { initialize(numFields); final long[] primitives = this.primitives; int nonPrimSize = 0; int offset = this.headerSize; for (int index = 0; index < numFields; index++, offset++) { iprot.readStructBegin(); TField field = iprot.readFieldBegin(); final ColumnValue._Fields setField = ColumnValue._Fields.findByThriftId(field.id); SnappyType nullType = null; if (setField != null) { // check more common types first switch (setField) { case STRING_VAL: if (field.type == ColumnValue.STRING_VAL_FIELD_DESC.type) { ensureNonPrimCapacity(nonPrimSize); String str = iprot.readString(); primitives[offset] = nonPrimSize; nonPrimitives[nonPrimSize++] = str; setType(index, SnappyType.VARCHAR.getValue()); } else { nullType = SnappyType.VARCHAR; } break; case I32_VAL: if (field.type == ColumnValue.I32_VAL_FIELD_DESC.type) { setPrimLong(offset, iprot.readI32()); setType(index, SnappyType.INTEGER.getValue()); } else { nullType = SnappyType.INTEGER; } break; case I64_VAL: if (field.type == ColumnValue.I64_VAL_FIELD_DESC.type) { setPrimLong(offset, iprot.readI64()); setType(index, SnappyType.BIGINT.getValue()); } else { nullType = SnappyType.BIGINT; } break; case DATE_VAL: if (field.type == ColumnValue.DATE_VAL_FIELD_DESC.type) { setPrimLong(offset, iprot.readI64()); setType(index, SnappyType.DATE.getValue()); } else { nullType = SnappyType.DATE; } break; case TIMESTAMP_VAL: if (field.type == ColumnValue.TIMESTAMP_VAL_FIELD_DESC.type) { setPrimLong(offset, iprot.readI64()); setType(index, SnappyType.TIMESTAMP.getValue()); } else { nullType = SnappyType.TIMESTAMP; } break; case DOUBLE_VAL: if (field.type == ColumnValue.DOUBLE_VAL_FIELD_DESC.type) { setPrimLong(offset, Double.doubleToLongBits(iprot.readDouble())); setType(index, SnappyType.DOUBLE.getValue()); } else { nullType = SnappyType.DOUBLE; } break; case DECIMAL_VAL: if (field.type == ColumnValue.DECIMAL_VAL_FIELD_DESC.type) { ensureNonPrimCapacity(nonPrimSize); Decimal decimal = new Decimal(); decimal.read(iprot); primitives[offset] = nonPrimSize; nonPrimitives[nonPrimSize++] = Converters .getBigDecimal(decimal); setType(index, SnappyType.DECIMAL.getValue()); } else { nullType = SnappyType.DECIMAL; } break; case FLOAT_VAL: if (field.type == ColumnValue.FLOAT_VAL_FIELD_DESC.type) { setPrimLong(offset, iprot.readI32()); setType(index, SnappyType.FLOAT.getValue()); } else { nullType = SnappyType.FLOAT; } break; case I16_VAL: if (field.type == ColumnValue.I16_VAL_FIELD_DESC.type) { setPrimLong(offset, iprot.readI16()); setType(index, SnappyType.SMALLINT.getValue()); } else { nullType = SnappyType.SMALLINT; } break; case BOOL_VAL: if (field.type == ColumnValue.BOOL_VAL_FIELD_DESC.type) { setPrimLong(offset, iprot.readBool() ? 1L : 0L); setType(index, SnappyType.BOOLEAN.getValue()); } else { nullType = SnappyType.BOOLEAN; } break; case BYTE_VAL: if (field.type == ColumnValue.BYTE_VAL_FIELD_DESC.type) { setPrimLong(offset, iprot.readByte()); setType(index, SnappyType.TINYINT.getValue()); } else { nullType = SnappyType.TINYINT; } break; case TIME_VAL: if (field.type == ColumnValue.TIME_VAL_FIELD_DESC.type) { setPrimLong(offset, iprot.readI64()); setType(index, SnappyType.TIME.getValue()); } else { nullType = SnappyType.TIME; } break; case CLOB_VAL: if (field.type == ColumnValue.CLOB_VAL_FIELD_DESC.type) { ensureNonPrimCapacity(nonPrimSize); ClobChunk clob = new ClobChunk(); clob.read(iprot); primitives[offset] = nonPrimSize; nonPrimitives[nonPrimSize++] = clob; setType(index, SnappyType.CLOB.getValue()); hasLobs = true; } else { nullType = SnappyType.CLOB; } break; case BLOB_VAL: if (field.type == ColumnValue.BLOB_VAL_FIELD_DESC.type) { ensureNonPrimCapacity(nonPrimSize); BlobChunk blob = new BlobChunk(); blob.read(iprot); primitives[offset] = nonPrimSize; nonPrimitives[nonPrimSize++] = blob; setType(index, SnappyType.BLOB.getValue()); hasLobs = true; } else { nullType = SnappyType.BLOB; } break; case BINARY_VAL: if (field.type == ColumnValue.BINARY_VAL_FIELD_DESC.type) { ensureNonPrimCapacity(nonPrimSize); byte[] bytes = ThriftUtils.toBytes(iprot.readBinary()); primitives[offset] = nonPrimSize; nonPrimitives[nonPrimSize++] = bytes; setType(index, SnappyType.VARBINARY.getValue()); } else { nullType = SnappyType.VARBINARY; } break; case NULL_VAL: setType(index, iprot.readBool() ? -SnappyType.NULLTYPE.getValue() : SnappyType.NULLTYPE.getValue()); break; case ARRAY_VAL: case STRUCT_VAL: SnappyType fieldSQLType; byte fieldType; if (setField == ColumnValue._Fields.ARRAY_VAL) { fieldSQLType = SnappyType.ARRAY; fieldType = ColumnValue.ARRAY_VAL_FIELD_DESC.type; } else { fieldSQLType = SnappyType.STRUCT; fieldType = ColumnValue.STRUCT_VAL_FIELD_DESC.type; } if (field.type == fieldType) { ensureNonPrimCapacity(nonPrimSize); org.apache.thrift.protocol.TList alist = iprot.readListBegin(); final int listSize = alist.size; final ArrayList values = new ArrayList<>(listSize); for (int i = 0; i < listSize; i++) { ColumnValue cv = new ColumnValue(); cv.read(iprot); values.add(cv); } iprot.readListEnd(); primitives[offset] = nonPrimSize; nonPrimitives[nonPrimSize++] = values; setType(index, fieldSQLType.getValue()); } else { nullType = fieldSQLType; } break; case MAP_VAL: if (field.type == ColumnValue.MAP_VAL_FIELD_DESC.type) { ensureNonPrimCapacity(nonPrimSize); org.apache.thrift.protocol.TMap m = iprot.readMapBegin(); final int mapSize = m.size; Map map = new HashMap<>(mapSize << 1); for (int i = 0; i < mapSize; i++) { ColumnValue k = new ColumnValue(); ColumnValue v = new ColumnValue(); k.read(iprot); v.read(iprot); map.put(k, v); } iprot.readMapEnd(); primitives[offset] = nonPrimSize; nonPrimitives[nonPrimSize++] = map; setType(index, SnappyType.MAP.getValue()); } else { nullType = SnappyType.MAP; } break; case JAVA_VAL: if (field.type == ColumnValue.JAVA_VAL_FIELD_DESC.type) { ensureNonPrimCapacity(nonPrimSize); byte[] serializedBytes = ThriftUtils.toBytes( iprot.readBinary()); primitives[offset] = nonPrimSize; nonPrimitives[nonPrimSize++] = new Converters.JavaObjectWrapper( serializedBytes); setType(index, SnappyType.JAVA_OBJECT.getValue()); } else { nullType = SnappyType.JAVA_OBJECT; } break; default: throw ClientSharedUtils.newRuntimeException( "unknown column type = " + setField, null); } if (nullType != null) { // treat like null value org.apache.thrift.protocol.TProtocolUtil.skip(iprot, field.type); setType(index, -nullType.getValue()); } } else { // treat like null value org.apache.thrift.protocol.TProtocolUtil.skip(iprot, field.type); setType(index, -SnappyType.NULLTYPE.getValue()); } iprot.readFieldEnd(); // this is so that we will eat the stop byte. we could put a check here to // make sure that it actually *is* the stop byte, but it's faster to do it // this way. iprot.readFieldBegin(); iprot.readStructEnd(); } this.nonPrimSize = nonPrimSize; } public final void initialize(int numFields) { // a byte for type of each field at the start final int headerSize = (numFields + 7) >>> 3; this.primitives = new long[headerSize + numFields]; this.nonPrimitives = null; this.nonPrimSize = 0; this.headerSize = headerSize; this.hash = 0; this.hasLobs = false; } public final void setColumnValue(int index, ColumnValue cv) throws SQLException { final ColumnValue._Fields setField = cv.getSetField(); if (setField != null) { int sqlTypeId; Object fieldVal = Boolean.FALSE; // indicator for primitives // check more common types first switch (setField) { case STRING_VAL: fieldVal = cv.getFieldValue(); sqlTypeId = SnappyType.VARCHAR.getValue(); break; case I32_VAL: sqlTypeId = SnappyType.INTEGER.getValue(); break; case I64_VAL: sqlTypeId = SnappyType.BIGINT.getValue(); break; case DATE_VAL: sqlTypeId = SnappyType.DATE.getValue(); break; case TIMESTAMP_VAL: sqlTypeId = SnappyType.TIMESTAMP.getValue(); break; case DOUBLE_VAL: sqlTypeId = SnappyType.DOUBLE.getValue(); break; case DECIMAL_VAL: Decimal decimal = (Decimal)cv.getFieldValue(); fieldVal = decimal != null ? Converters.getBigDecimal(decimal) : null; sqlTypeId = SnappyType.DECIMAL.getValue(); break; case FLOAT_VAL: sqlTypeId = SnappyType.FLOAT.getValue(); break; case I16_VAL: sqlTypeId = SnappyType.SMALLINT.getValue(); break; case BOOL_VAL: sqlTypeId = SnappyType.BOOLEAN.getValue(); break; case BYTE_VAL: sqlTypeId = SnappyType.TINYINT.getValue(); break; case TIME_VAL: sqlTypeId = SnappyType.TIME.getValue(); break; case CLOB_VAL: fieldVal = cv.getFieldValue(); sqlTypeId = SnappyType.CLOB.getValue(); hasLobs = true; break; case BLOB_VAL: fieldVal = cv.getFieldValue(); sqlTypeId = SnappyType.BLOB.getValue(); hasLobs = true; break; case BINARY_VAL: fieldVal = cv.getBinary_val(); sqlTypeId = SnappyType.VARBINARY.getValue(); break; case NULL_VAL: setType(index, cv.getNull_val() ? -SnappyType.NULLTYPE.getValue() : SnappyType.NULLTYPE.getValue()); return; case ARRAY_VAL: fieldVal = cv.getFieldValue(); sqlTypeId = SnappyType.ARRAY.getValue(); break; case MAP_VAL: fieldVal = cv.getFieldValue(); sqlTypeId = SnappyType.MAP.getValue(); break; case STRUCT_VAL: fieldVal = cv.getFieldValue(); sqlTypeId = SnappyType.STRUCT.getValue(); break; case JAVA_VAL: byte[] serializedBytes = cv.getJava_val(); fieldVal = serializedBytes != null ? new Converters.JavaObjectWrapper( serializedBytes) : null; sqlTypeId = SnappyType.JAVA_OBJECT.getValue(); break; default: throw ClientSharedUtils.newRuntimeException("unknown column value: " + (cv.getFieldValue() != null ? cv : "null"), null); } if (fieldVal == Boolean.FALSE) { setPrimLong(headerSize + index, cv.getPrimitiveLong()); setType(index, sqlTypeId); } else if (fieldVal != null) { ensureNonPrimCapacity(nonPrimSize); primitives[headerSize + index] = nonPrimSize; nonPrimitives[nonPrimSize++] = fieldVal; setType(index, sqlTypeId); } else { // -ve typeId indicator for null values setType(index, -sqlTypeId); } } else { // treat like null value setType(index, -SnappyType.NULLTYPE.getValue()); } } protected final void ensureNonPrimCapacity(final int nonPrimSize) { if (this.nonPrimitives != null) { final int capacity = this.nonPrimitives.length; if (nonPrimSize >= capacity) { final int newCapacity = Math.min(capacity + (capacity >>> 1), size()); Object[] newNonPrims = new Object[newCapacity]; System.arraycopy(this.nonPrimitives, 0, newNonPrims, 0, capacity); this.nonPrimitives = newNonPrims; } } else { this.nonPrimitives = new Object[4]; } } public final int size() { return primitives.length - headerSize; } public void clear() { // mark all non-primitives as null setDefaultNullability(); // free the non-primitives to help GC if possible Arrays.fill(this.nonPrimitives, null); // skip primitive values since it doesn't hurt // and we need the non-primitive indexes this.hash = 0; } @Override public int hashCode() { int h = this.hash; if (h != 0) { return h; } long[] prims = this.primitives; if (prims != null && prims.length > 0) { for (long l : prims) { h = ResolverUtils.addLongToHashOpt(l, h); } } final Object[] nps = this.nonPrimitives; if (nps != null && nps.length > 0) { for (Object o : nps) { h = ResolverUtils.addIntToHashOpt(o != null ? o.hashCode() : -1, h); } } return (this.hash = h); } @Override public boolean equals(Object other) { return other instanceof OptimizedElementArray && equals((OptimizedElementArray)other); } public boolean equals(OptimizedElementArray other) { return this.nonPrimSize == other.nonPrimSize && this.hasLobs == other.hasLobs && Arrays.equals(this.primitives, other.primitives) && Arrays.equals(this.nonPrimitives, other.nonPrimitives); } @Override public String toString() { final long[] primitives = this.primitives; final Object[] nonPrimitives = this.nonPrimitives; int offset = this.headerSize; final int size = size(); final StringBuilder sb = new StringBuilder(); for (int index = 0; index < size; index++, offset++) { if (index != 0) sb.append(','); // nulls will always have -ve types so no need for further null checks final int snappyType = getType(index); sb.append("TYPE=").append(SnappyType.findByValue(Math.abs(snappyType))) .append(" VALUE="); // check more common types first switch (snappyType) { case 1: // BOOLEAN sb.append(primitives[offset] != 0); break; case 2: // TINYINT case 3: // SMALLINT case 4: // INTEGER case 5: // BIGINT sb.append(primitives[offset]); break; case 6: // FLOAT sb.append(Float.intBitsToFloat((int)primitives[offset])); break; case 7: // DOUBLE sb.append(Double.longBitsToDouble(primitives[offset])); break; case 8: // DECIMAL case 9: // CHAR case 10: // VARCHAR case 11: // LONGVARCHAR case 18: // BLOB case 19: // CLOB case 20: // SQLXML case 21: // ARRAY case 22: // MAP case 23: // STRUCT case 25: // JSON case 26: // JAVA_OBJECT Object o = nonPrimitives[(int)primitives[offset]]; if (o != null) { sb.append("(type=").append(o.getClass().getName()).append(')'); } sb.append(nonPrimitives[(int)primitives[offset]]); break; case 12: // DATE sb.append(primitives[offset]).append(','); break; case 13: // TIME sb.append(primitives[offset]).append(','); break; case 14: // TIMESTAMP sb.append(primitives[offset]).append(','); break; case 15: // BINARY case 16: // VARBINARY case 17: // LONGVARBINARY byte[] bytes = (byte[])nonPrimitives[(int)primitives[offset]]; TBaseHelper.toString(ByteBuffer.wrap(bytes), sb); break; case 24: // NULLTYPE sb.append("NullType=false"); break; default: // check for null if (snappyType < 0) { sb.append("NULL"); } else { sb.append("UNKNOWN"); } break; } } return sb.toString(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy