//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.ResultSet; import java.sql.SQLException; import java.sql.Types; /** * Represents an SQL data type as required by <code>getTypeInfo()</code>. * Provides a suitable natural ordering. * <p/> * This class probably shouldn't be public, but is required to be so by the * tests. * * @author David Eaves * @version $Id: TypeInfo.java,v 1.5 2005-07-27 11:02:34 alin_sinpalean Exp $ */ public class TypeInfo implements Comparable { static final int NUM_COLS = 18; private final String typeName; private final int dataType; private final int precision; private final String literalPrefix; private final String literalSuffix; private final String createParams; private final short nullable; private final boolean caseSensitive; private final short searchable; private final boolean unsigned; private final boolean fixedPrecScale; private final boolean autoIncrement; private final String localTypeName; private final short minimumScale; private final short maximumScale; private final int sqlDataType; private final int sqlDatetimeSub; private final int numPrecRadix; private final int normalizedType; private final int distanceFromJdbcType; public TypeInfo(ResultSet rs, boolean useLOBs) throws SQLException { typeName = rs.getString(1); dataType = rs.getInt(2); precision = rs.getInt(3); literalPrefix = rs.getString(4); literalSuffix = rs.getString(5); createParams = rs.getString(6); nullable = rs.getShort(7); caseSensitive = rs.getBoolean(8); searchable = rs.getShort(9); unsigned = rs.getBoolean(10); fixedPrecScale = rs.getBoolean(11); autoIncrement = rs.getBoolean(12); localTypeName = rs.getString(13); if (rs.getMetaData().getColumnCount() >= 18) { // Some servers provide more information minimumScale = rs.getShort(14); maximumScale = rs.getShort(15); sqlDataType = rs.getInt(16); sqlDatetimeSub = rs.getInt(17); numPrecRadix = rs.getInt(18); } else { // Must initialize final fields minimumScale = 0; maximumScale = 0; sqlDataType = 0; sqlDatetimeSub = 0; numPrecRadix = 0; } normalizedType = normalizeDataType(dataType, useLOBs); distanceFromJdbcType = determineDistanceFromJdbcType(); } /** * For testing only. Create an instance with just the properties utilised * in the <code>compareTo()</code> method (set name, type, and auto * increment). */ public TypeInfo(String typeName, int dataType, boolean autoIncrement) { this.typeName = typeName; this.dataType = dataType; this.autoIncrement = autoIncrement; this.precision = 0; this.literalPrefix = null; this.literalSuffix = null; this.createParams = null; this.nullable = 0; this.caseSensitive = false; this.searchable = 0; this.unsigned = false; this.fixedPrecScale = false; this.localTypeName = null; this.minimumScale = 0; this.maximumScale = 0; this.sqlDataType = 0; this.sqlDatetimeSub = 0; this.numPrecRadix = 0; normalizedType = normalizeDataType(dataType, true); distanceFromJdbcType = determineDistanceFromJdbcType(); } public boolean equals(Object o) { if (o instanceof TypeInfo) { return compareTo(o) == 0; } return false; } public int hashCode() { return normalizedType * dataType * (autoIncrement ? 7 : 11); } public String toString() { return typeName + " (" + (dataType != normalizedType ? dataType + "->" : "") + normalizedType + ')'; } public void update(ResultSet rs) throws SQLException { rs.updateString(1, typeName); rs.updateInt(2, normalizedType); rs.updateInt(3, precision); rs.updateString(4, literalPrefix); rs.updateString(5, literalSuffix); rs.updateString(6, createParams); rs.updateShort(7, nullable); rs.updateBoolean(8, caseSensitive); rs.updateShort(9, searchable); rs.updateBoolean(10, unsigned); rs.updateBoolean(11, fixedPrecScale); rs.updateBoolean(12, autoIncrement); rs.updateString(13, localTypeName); if (rs.getMetaData().getColumnCount() >= 18) { // Some servers provide more information rs.updateShort(14, minimumScale); rs.updateShort(15, maximumScale); rs.updateInt(16, sqlDataType); rs.updateInt(17, sqlDatetimeSub); rs.updateInt(18, numPrecRadix); } } /** * Comparable implementation that orders by dataType, then by how closely * the data type maps to the corresponding JDBC SQL type. * <p/> * The data type values for the non-standard SQL Server types tend to have * negative numbers while the corresponding standard types have positive * numbers so utilise that in the sorting. */ public int compareTo(Object o) { TypeInfo other = ((TypeInfo) o); // Order by normalised type, then proximity to standard JDBC type. return compare(normalizedType, other.normalizedType) * 10 + compare(distanceFromJdbcType, other.distanceFromJdbcType); } private int compare(int i1, int i2) { return i1 < i2 ? -1 : (i1 == i2 ? 0 : 1); } /** * Determine how close this type is to the corresponding JDBC type. Used in * sorting to distinguish between types that have the same * <code>normalizedType</code> value. * * @return positive integer indicating how far away the type is from the * corresponding JDBC type, with zero being the nearest possible * match and 9 being the least */ private int determineDistanceFromJdbcType() { // TODO: Are these assumptions correct/complete? switch (dataType) { // Cases without an un-normalized alternative, so these are the // best available case 11: // Sybase DATETIME case 10: // Sybase TIME case 9: // Sybase DATE case 6: // FLOAT return 0; case 12: // VARCHAR, SYSNAME and NVARCHAR all together with Sybase if (typeName.equalsIgnoreCase("varchar")) { return 0; } if (typeName.equalsIgnoreCase("nvarchar")) { return 1; } return 2; // Special case as the same data type value is used for SYSNAME and // NVARCHAR (SYSNAME is essentially an alias for NVARCHAR). We // don't want applications preferring SYSNAME. case -9: // SYSNAME / NVARCHAR return typeName.equalsIgnoreCase("sysname") ? 4 : 3; // Particularly non-standard types case -11: // UNIQUEIDENTIFIER return 9; case -150: // SQL_VARIANT return 8; // Default behaviour is to assume that if type has not been // normalised it is the closest available match, unless it is an // auto incrementing type default: return (dataType == normalizedType && !autoIncrement) ? 0 : 5; } } /** * Return a {@link java.sql.Types}-defined type for an SQL Server specific data type. * * @param serverDataType the data type, as returned by the server * @param useLOBs whether LOB data types are used for large types * @return the equivalent data type defined by <code>java.sql.Types</code> */ public static int normalizeDataType(int serverDataType, boolean useLOBs) { switch (serverDataType) { case 35: // Sybase UNIVARCHAR return Types.VARCHAR; case 11: // Sybase DATETIME return Types.TIMESTAMP; case 10: // Sybase TIME return Types.TIME; case 9: // Sybase DATE return Types.DATE; case 6: // FLOAT return Types.DOUBLE; case -1: // LONGVARCHAR return useLOBs ? Types.CLOB : Types.LONGVARCHAR; case -4: // LONGVARBINARY return useLOBs ? Types.BLOB : Types.LONGVARBINARY; case -8: // NCHAR return Types.CHAR; case -9: // SYSNAME / NVARCHAR return Types.VARCHAR; case -10: // NTEXT return useLOBs ? Types.CLOB : Types.LONGVARCHAR; case -11: // UNIQUEIDENTIFIER return Types.CHAR; case -150: // SQL_VARIANT return Types.VARCHAR; default: return serverDataType; } } }