// jTDS JDBC Driver for Microsoft SQL Server and Sybase // Copyright (C) 2004 The jTDS Project // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library 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 // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // package net.sourceforge.jtds.jdbc; import java.sql.SQLException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.IOException; import java.io.UnsupportedEncodingException; /** * This class is a descriptor for procedure and prepared statement parameters. * * @author Mike Hutchinson * @version $Id: ParamInfo.java,v 1.16 2005-04-25 11:47:01 alin_sinpalean Exp $ */ class ParamInfo implements Cloneable { /** Flag as an input parameter. */ final static int INPUT = 0; /** Flag as an output parameter. */ final static int OUTPUT = 1; /** Flag as an return value parameter. */ final static int RETVAL = 2; /** Flag as a unicode parameter. */ final static int UNICODE = 4; /** Internal TDS data type */ int tdsType; /** JDBC type constant from java.sql.Types */ int jdbcType; /** Formal parameter name eg @P1 */ String name; /** SQL type name eg varchar(10) */ String sqlType; /** Parameter offset in target SQL statement */ int markerPos = -1; /** Current parameter value */ Object value; /** Parameter decimal precision */ int precision = -1; /** Parameter decimal scale */ int scale = -1; /** Length of InputStream */ int length = -1; /** Parameter is an output parameter */ boolean isOutput; /** Parameter is used as SP return value */ boolean isRetVal; /** IN parameter has been set */ boolean isSet; /** Parameter should be sent as unicode */ boolean isUnicode; /** TDS 8 Collation string. */ byte collation[]; /** Character set descriptor (if different from default) */ CharsetInfo charsetInfo; /** OUT parameter value is set.*/ boolean isSetOut; /** OUT Parameter value. */ Object outValue; /** * Construct a parameter with parameter marker offset. * * @param pos the offset of the ? symbol in the target SQL string * @param isUnicode <code>true</code> if the parameter is Unicode encoded */ ParamInfo(int pos, boolean isUnicode) { markerPos = pos; this.isUnicode = isUnicode; } /** * Construct a parameter for statement caching. * * @param name the formal name of the parameter * @param pos the offset of the ? symbol in the parsed SQL string * @param isRetVal <code>true</code> if the parameter is a return value * @param isUnicode <code>true</code> if the parameter is Unicode encoded */ ParamInfo(String name, int pos, boolean isRetVal, boolean isUnicode) { this.name = name; this.markerPos = pos; this.isRetVal = isRetVal; this.isUnicode = isUnicode; } /** * Construct an initialised parameter with extra attributes. * * @param jdbcType the <code>java.sql.Type</code> constant describing this type * @param value the initial parameter value * @param flags the additional attributes eg OUTPUT, RETVAL, UNICODE etc. */ ParamInfo(int jdbcType, Object value, int flags) { this.jdbcType = jdbcType; this.value = value; this.isSet = true; this.isOutput = ((flags & OUTPUT) > 0) || ((flags & RETVAL) > 0); this.isRetVal = ((flags & RETVAL)> 0); this.isUnicode = ((flags & UNICODE) > 0); if (value instanceof String) { this.length = ((String)value).length(); } else if (value instanceof byte[]) { this.length = ((byte[])value).length; } } /** * Construct a parameter based on a result set column. * * @param ci the column descriptor * @param name the name for this parameter or null * @param value the column data value * @param length the column data length */ ParamInfo(ColInfo ci, String name, Object value, int length) { this.name = name; this.tdsType = ci.tdsType; this.scale = ci.scale; this.precision = ci.precision; this.jdbcType = ci.jdbcType; this.sqlType = ci.sqlType; this.collation = ci.collation; this.charsetInfo = ci.charsetInfo; this.isUnicode = TdsData.isUnicode(ci); this.isSet = true; this.value = value; this.length = length; } /** * Get the output parameter value. * * @return the OUT value as an <code>Object</code> * @throws SQLException if the parameter has not been set */ Object getOutValue() throws SQLException { if (!isSetOut) { throw new SQLException( Messages.get("error.callable.outparamnotset"), "HY010"); } return outValue; } /** * Set the OUT parameter value. * @param value The data value. */ void setOutValue(Object value) { outValue= value; isSetOut = true; } /** * Clear the OUT parameter value and status. */ void clearOutValue() { outValue = null; isSetOut = false; } /** * Clear the IN parameter value and status. */ void clearInValue() { value = null; isSet = false; } /** * Get the string value of the parameter. * * @return The data value as a <code>String</code> or null. */ String getString(String charset) throws IOException { if (value == null || value instanceof String) { return (String) value; } if (value instanceof InputStream) { try { value = loadFromReader(new InputStreamReader((InputStream) value, charset), length); length = ((String) value).length(); return (String) value; } catch (UnsupportedEncodingException e) { throw new IOException("I/O Error: UnsupportedEncodingException: "+ e.getMessage()); } } if (value instanceof Reader) { value = loadFromReader((Reader)value, length); return (String)value; } return value.toString(); } /** * Get the byte array value of the parameter. * * @return The data value as a <code>byte[]</code> or null. */ byte[] getBytes(String charset) throws IOException { if (value == null || value instanceof byte[]) { return (byte[])value; } if (value instanceof InputStream) { value = loadFromStream((InputStream) value, length); return (byte[]) value; } if (value instanceof Reader) { String tmp = loadFromReader((Reader) value, length); value = Support.encodeString(charset, tmp); return (byte[]) value; } if (value instanceof String) { return Support.encodeString(charset, (String) value); } return new byte[0]; } /** * Load a byte array from an InputStream * * @param in The InputStream to read from. * @param length The length of the stream. * @return The data as a <code>byte[]</code>. * @throws IOException */ private static byte[] loadFromStream(InputStream in, int length) throws IOException { byte[] buf = new byte[length]; int pos = 0, res; while (pos != length && (res = in.read(buf, pos, length - pos)) != -1) { pos += res; } if (pos != length) { throw new java.io.IOException( "Data in stream less than specified by length"); } if (in.read() >= 0) { throw new java.io.IOException( "More data in stream than specified by length"); } return buf; } /** * Create a String from a Reader stream. * * @param in The Reader object with the data. * @param length Number of characters to read. * @return The data as a <code>String</code>. * @throws IOException */ private static String loadFromReader(Reader in, int length) throws IOException { char[] buf = new char[length]; int pos = 0, res; while (pos != length && (res = in.read(buf, pos, length - pos)) != -1) { pos += res; } if (pos != length) { throw new java.io.IOException( "Data in stream less than specified by length"); } if (in.read() >= 0) { throw new java.io.IOException( "More data in stream than specified by length"); } return new String(buf); } /** * Creates a shallow copy of this <code>ParamInfo</code> instance. Used by * the <code>PreparedStatement</code> batching implementation to duplicate * parameters. */ public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException ex) { // Will not happen return null; } } }