/* This file is part of VoltDB.
* Copyright (C) 2008-2010 VoltDB L.L.C.
*
* VoltDB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VoltDB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VoltDB. If not, see <http://www.gnu.org/licenses/>.
*/
package org.voltdb;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.util.Arrays;
import org.voltdb.messaging.FastDeserializer;
import org.voltdb.messaging.FastSerializable;
import org.voltdb.messaging.FastSerializer;
import org.voltdb.types.TimestampType;
import org.voltdb.types.VoltDecimalHelper;
import edu.brown.pools.Poolable;
import edu.brown.utils.StringUtil;
/**
* The ordered set of parameters of the proper types that is passed into
* a stored procedure OR a plan fragment.
*/
public class ParameterSet implements FastSerializable, Poolable {
static final byte ARRAY = -99;
public static final ParameterSet EMPTY = new ParameterSet();
private final boolean m_serializingToEE;
private Object m_params[] = new Object[0];
public ParameterSet() {
this(false);
}
public ParameterSet(Object...params) {
this(false);
m_params = params;
}
public ParameterSet(boolean serializingToEE) {
m_serializingToEE = serializingToEE;
}
@Override
public boolean isInitialized() {
return false;
}
@Override
public void finish() {
this.m_params = null;
}
/**
* Sets the internal array to params. Note: this does *not* copy the argument.
*/
public ParameterSet setParameters(Object... params) {
this.m_params = params;
return (this);
}
/**
* Set the internal array of this ParameterSet to the same as the one given
* @param other
* @return
*/
public ParameterSet setParameters(ParameterSet other) {
this.m_params = other.m_params;
return (this);
}
@Override
public int hashCode() {
return Arrays.hashCode(m_params);
}
public void clear() {
this.m_params = null;
}
public Object[] toArray() {
return m_params;
}
public int size() {
return m_params.length;
}
static Object getParameterAtIndex(int partitionIndex, ByteBuffer unserializedParams) throws IOException {
FastDeserializer in = new FastDeserializer(unserializedParams);
int paramLen = in.readShort();
if (partitionIndex >= paramLen) {
// error if caller desires out of bounds parameter
throw new RuntimeException("Invalid partition parameter requested.");
}
for (int i = 0; i < partitionIndex; ++i) {
readOneParameter(in);
}
Object retval = readOneParameter(in);
unserializedParams.rewind();
return retval;
}
@Override
public void readExternal(FastDeserializer in) throws IOException {
int paramLen = in.readShort();
m_params = new Object[paramLen];
for (int i = 0; i < paramLen; i++) {
m_params[i] = readOneParameter(in);
}
}
@Override
public void writeExternal(FastSerializer out) throws IOException {
out.writeShort(m_params.length);
for (Object obj : m_params) {
if (obj == null) {
VoltType type = VoltType.NULL;
out.writeByte(type.getValue());
continue;
}
Class<?> cls = obj.getClass();
if (cls.isArray()) {
// EE doesn't support array parameters. Arrays of bytes are
// only useful to strings. Special case them here.
if (m_serializingToEE && obj instanceof byte[]) {
final byte[] b = (byte[]) obj;
if (b.length > VoltType.MAX_VALUE_LENGTH) {
throw new VoltOverflowException(
"Value of string byte[] larger than allowed max " + VoltType.MAX_VALUE_LENGTH_STR);
}
out.writeByte(VoltType.STRING.getValue());
out.writeInt(b.length);
out.write(b);
continue;
}
out.writeByte(ARRAY);
VoltType type = VoltType.typeFromClass(cls.getComponentType());
out.writeByte(type.getValue());
switch (type) {
case TINYINT:
out.writeArray((byte[])obj);
break;
case SMALLINT:
out.writeArray((short[]) obj);
break;
case INTEGER:
out.writeArray((int[]) obj);
break;
case BIGINT:
out.writeArray((long[]) obj);
break;
case FLOAT:
out.writeArray((double[]) obj);
break;
case STRING:
out.writeArray((String[]) obj);
break;
case TIMESTAMP:
out.writeArray((TimestampType[]) obj);
break;
case DECIMAL:
// converted long128 in serializer api
out.writeArray((BigDecimal[]) obj);
break;
case BOOLEAN:
out.writeArray((boolean[]) obj);
break;
case VOLTTABLE:
out.writeArray((VoltTable[]) obj);
break;
default:
throw new RuntimeException("FIXME: Unsupported type " + type);
}
continue;
}
// Handle NULL mappings not encoded by type.min_value convention
if (obj == VoltType.NULL_TIMESTAMP) {
out.writeByte(VoltType.TIMESTAMP.getValue());
out.writeLong(VoltType.NULL_BIGINT); // corresponds to EE value.h isNull()
continue;
}
else if (obj == VoltType.NULL_STRING) {
out.writeByte(VoltType.STRING.getValue());
out.writeInt(VoltType.NULL_STRING_LENGTH);
continue;
}
else if (obj == VoltType.NULL_DECIMAL) {
out.writeByte(VoltType.DECIMAL.getValue());
VoltDecimalHelper.serializeNull(out);
continue;
}
VoltType type = VoltType.typeFromClass(cls);
out.writeByte(type.getValue());
switch (type) {
case TINYINT:
out.writeByte((Byte)obj);
break;
case SMALLINT:
out.writeShort((Short)obj);
break;
case INTEGER:
out.writeInt((Integer) obj);
break;
case BIGINT:
out.writeLong((Long) obj);
break;
case FLOAT:
out.writeDouble((Double) obj);
break;
case STRING:
out.writeString((String) obj);
break;
case TIMESTAMP:
out.writeTimestamp((TimestampType) obj);
break;
case DECIMAL:
VoltDecimalHelper.serializeBigDecimal((BigDecimal)obj, out);
break;
case BOOLEAN:
out.writeBoolean((Boolean)obj);
break;
case VOLTTABLE:
out.writeObject((VoltTable) obj);
break;
default:
throw new RuntimeException("FIXME: Unsupported type " + type);
}
}
}
@Override
public String toString() {
return String.format("%s{%s}",
this.getClass().getSimpleName(),
StringUtil.toString(m_params, true, true));
}
static private Object readOneParameter(FastDeserializer in) throws IOException {
byte nextTypeByte = in.readByte();
if (nextTypeByte == ARRAY) {
VoltType nextType = VoltType.get(in.readByte());
if (nextType == null) return null;
return in.readArray(nextType.classFromType());
}
else {
VoltType nextType = VoltType.get(nextTypeByte);
switch (nextType) {
case NULL:
return null;
case TINYINT:
return in.readByte();
case SMALLINT:
return in.readShort();
case INTEGER:
return in.readInt();
case BIGINT:
return in.readLong();
case FLOAT:
return in.readDouble();
case STRING:
String string_val = in.readString();
if (string_val == null)
{
return VoltType.NULL_STRING;
}
return string_val;
case TIMESTAMP:
return in.readTimestamp();
case BOOLEAN:
return in.readBoolean();
case VOLTTABLE:
return in.readObject(VoltTable.class);
case DECIMAL: {
BigDecimal decimal_val = in.readBigDecimal();
if (decimal_val == null)
{
return VoltType.NULL_DECIMAL;
}
return decimal_val;
}
case DECIMAL_STRING: {
BigDecimal decimal_val = in.readBigDecimalFromString();
if (decimal_val == null)
{
return VoltType.NULL_DECIMAL;
}
return decimal_val;
}
default:
throw new RuntimeException("ParameterSet doesn't support type" + nextType);
}
}
}
static Object limitType(Object o) {
Class<?> ctype = o.getClass();
if (ctype == Integer.class) {
return ((Integer) o).longValue();
}
return o;
}
}