/* 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.nio.ByteBuffer; import java.util.Collection; import java.util.HashSet; import java.util.Set; import org.voltdb.messaging.*; import edu.brown.hstore.HStoreConstants; /** * Represents a serializeable bundle of procedure name and parameters. This * is the object that is sent by the client library to call a stored procedure. * */ public class StoredProcedureInvocation implements FastSerializable { protected int procId = -1; protected String procName = null; protected ParameterSet params = null; protected ByteBuffer unserializedParams = null; /** * The number of times the txn for this request has been * restarted */ protected int restartCounter = 0; /** A descriptor provided by the client, opaque to the server, returned to the client in the ClientResponse */ protected long clientHandle = -1; /** Whether this invocation should be specifically executed at a particular partition **/ protected int base_partition = HStoreConstants.NULL_PARTITION_ID; /** What partitions this invocation will touch **/ @Deprecated protected Set<Integer> partitions = null; public StoredProcedureInvocation() { super(); } public StoredProcedureInvocation(long handle, String procName, Object... parameters) { this(handle, -1, procName, parameters); } public StoredProcedureInvocation(long handle, int procId, String procName, Object... parameters) { super(); this.clientHandle = handle; this.procId = procId; this.procName = procName; this.params = new ParameterSet(); this.params.setParameters(parameters); } public StoredProcedureInvocation getShallowCopy() { StoredProcedureInvocation copy = new StoredProcedureInvocation(); copy.clientHandle = this.clientHandle; copy.restartCounter = this.restartCounter; copy.params = this.params; copy.procName = this.procName; if (unserializedParams != null) { copy.unserializedParams = unserializedParams.duplicate(); } else { copy.unserializedParams = null; } return copy; } public void setProcedureId(int procId) { this.procId = procId; } public void setProcName(String name) { this.procName = name; } public void setParams(Object... parameters) { // convert the params to the expected types params = new ParameterSet(); params.setParameters(parameters); unserializedParams = null; } public int getProcId() { return procId; } public String getProcName() { return procName; } public ParameterSet getParams() { return params; } public void setClientHandle(int aHandle) { clientHandle = aHandle; } public long getClientHandle() { return clientHandle; } public boolean hasBasePartition() { return (this.base_partition != HStoreConstants.NULL_PARTITION_ID); } public int getBasePartition() { return (this.base_partition); } public void setBasePartition(int partition) { this.base_partition = partition; } public int getRestartCounter() { return (this.base_partition); } public void setRestartCounter(int count) { this.restartCounter = count; } @Deprecated public Set<Integer> getPartitions() { return (this.partitions); } @Deprecated public void addPartitions(Collection<Integer> partitions) { if (this.partitions == null) this.partitions = new HashSet<Integer>(); this.partitions.addAll(partitions); } // ---------------------------------------------------------------------------- // INTERNAL SERIALIZATION METHODS // ---------------------------------------------------------------------------- /** * Build the ParameterSet embedded in this StoredProcedureInvocation * <B>Note:</B> This is the slow version because it will create a new * FastDeserializer every time. */ public void buildParameterSet() { this.buildParameterSet(new FastDeserializer()); } /** * If created from ClientInterface within a single host, * will still need to deserialize the parameter set. This * optimization is not performed for multi-site transactions * (which require concurrent access to the task). */ public void buildParameterSet(FastDeserializer fds) { if (unserializedParams != null) { assert (params == null); try { fds.setBuffer(unserializedParams); params = fds.readObject(ParameterSet.class); unserializedParams = null; } catch (IOException ex) { throw new RuntimeException("Invalid ParameterSet in Stored Procedure Invocation."); } } } /** Read into an unserialized parameter buffer to extract a single parameter */ Object getParameterAtIndex(int partitionIndex) { try { return ParameterSet.getParameterAtIndex(partitionIndex, unserializedParams); } catch (IOException ex) { throw new RuntimeException("Invalid partitionIndex", ex); } } @Override public void readExternal(FastDeserializer in) throws IOException { this.restartCounter = (int)in.readShort(); this.base_partition = (int)in.readShort(); this.clientHandle = in.readLong(); this.procId = in.readShort(); this.procName = in.readString(); // do not deserialize parameters in ClientInterface context this.unserializedParams = in.remainder(); this.params = null; } @Override public void writeExternal(FastSerializer out) throws IOException { assert(!((params == null) && (unserializedParams == null))); assert((params != null) || (unserializedParams != null)); out.writeShort(this.restartCounter); // (2 bytes) out.writeShort(this.base_partition); // (2 bytes) out.writeLong(this.clientHandle); // (8 bytes) out.writeShort(this.procId); // (2 bytes) out.writeString(this.procName); if (this.params != null) { out.writeObject(this.params); } else if (this.unserializedParams != null) { out.write(this.unserializedParams.array(), this.unserializedParams.position() + this.unserializedParams.arrayOffset(), this.unserializedParams.remaining()); } } @Override public String toString() { String extra = ""; if (this.params != null) { extra += String.format("<%d Params>", this.params.toArray().length); } if (this.base_partition != HStoreConstants.NULL_PARTITION_ID) { if (extra.isEmpty() == false) extra += " / "; extra += String.format("<%d BasePartition>", this.base_partition); } return String.format("%s(%s) / %d", this.procName, extra, this.clientHandle); } public void getDumpContents(StringBuilder sb) { sb.append("Invocation: ").append(procName).append("("); if (params != null) for (Object o : params.toArray()) { sb.append(o.toString()).append(", "); } else sb.append("null"); sb.append(")"); } // ---------------------------------------------------------------------------- // QUICK ACCESS METHODS // ---------------------------------------------------------------------------- /** * Mark a serialized byte array of a StoredProcedureInvocation as being redirected to * the given partition id without having to serialize it first. * @param partition * @param buffer ByteBuffer wrapper around a serialized StoredProcedureInvocation */ public static void setBasePartition(int partition, ByteBuffer buffer) { buffer.putShort(2, (short)partition); } /** * Returns the restart counter embedded in this invocation * @param buffer ByteBuffer wrapper around a serialized StoredProcedureInvocation * @return */ public static int getRestartCounter(ByteBuffer buffer) { return (buffer.getShort(0)); } /** * Returns the base partition of this invocation * If the base partition is not set, the return value will be NULL_PARTITION_ID * @param buffer ByteBuffer wrapper around a serialized StoredProcedureInvocation * @return */ public static int getBasePartition(ByteBuffer buffer) { return (buffer.getShort(2)); } /** * Return the client handle from the serialized StoredProcedureInvocation without having to * deserialize it first * @param buffer ByteBuffer wrapper around a serialized StoredProcedureInvocation * @return */ public static long getClientHandle(ByteBuffer buffer) { buffer.rewind(); return (buffer.getLong(4)); } /** * Returns the procId encoded in this invocation * If the procId is not set, the return value will be -1 * @param buffer * @return */ public static int getProcedureId(ByteBuffer buffer) { return (buffer.getShort(12)); } /** * * @param buffer ByteBuffer wrapper around a serialized StoredProcedureInvocation * @return */ public static String getProcedureName(ByteBuffer buffer) { return getProcedureName(new FastDeserializer(buffer)); } /** * Faster way of getting the Procedure name with an existing FastDeserializer * @param in * @return */ public static String getProcedureName(FastDeserializer in) { in.buffer().position(14); try { return (in.readString()); } catch (IOException ex) { throw new RuntimeException(ex); } } /** * Get a ByteBuffer that contains the serialized ParameterSet * for the ByteBuffer that contains a StoredProcedureInvocation * @param buffer ByteBuffer wrapper around a serialized StoredProcedureInvocation * @return */ public static ByteBuffer getParameterSet(ByteBuffer buffer) { seekToParameterSet(buffer); return buffer.slice(); } public static void seekToParameterSet(ByteBuffer buffer) { // Skip to the procedure name buffer.position(14); int procNameLen = buffer.getInt(); buffer.position(buffer.position() + procNameLen); } }