001: //jTDS JDBC Driver for Microsoft SQL Server and Sybase
002: //Copyright (C) 2004 The jTDS Project
003: //
004: //This library is free software; you can redistribute it and/or
005: //modify it under the terms of the GNU Lesser General Public
006: //License as published by the Free Software Foundation; either
007: //version 2.1 of the License, or (at your option) any later version.
008: //
009: //This library is distributed in the hope that it will be useful,
010: //but WITHOUT ANY WARRANTY; without even the implied warranty of
011: //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: //Lesser General Public License for more details.
013: //
014: //You should have received a copy of the GNU Lesser General Public
015: //License along with this library; if not, write to the Free Software
016: //Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: //
018: package net.sourceforge.jtds.jdbc;
019:
020: import java.sql.ResultSet;
021: import java.sql.SQLException;
022: import java.sql.Types;
023:
024: /**
025: * Represents an SQL data type as required by <code>getTypeInfo()</code>.
026: * Provides a suitable natural ordering.
027: * <p/>
028: * This class probably shouldn't be public, but is required to be so by the
029: * tests.
030: *
031: * @author David Eaves
032: * @version $Id: TypeInfo.java,v 1.5 2005/07/27 11:02:34 alin_sinpalean Exp $
033: */
034: public class TypeInfo implements Comparable {
035: static final int NUM_COLS = 18;
036:
037: private final String typeName;
038: private final int dataType;
039: private final int precision;
040: private final String literalPrefix;
041: private final String literalSuffix;
042: private final String createParams;
043: private final short nullable;
044: private final boolean caseSensitive;
045: private final short searchable;
046: private final boolean unsigned;
047: private final boolean fixedPrecScale;
048: private final boolean autoIncrement;
049: private final String localTypeName;
050: private final short minimumScale;
051: private final short maximumScale;
052: private final int sqlDataType;
053: private final int sqlDatetimeSub;
054: private final int numPrecRadix;
055:
056: private final int normalizedType;
057: private final int distanceFromJdbcType;
058:
059: public TypeInfo(ResultSet rs, boolean useLOBs) throws SQLException {
060: typeName = rs.getString(1);
061: dataType = rs.getInt(2);
062: precision = rs.getInt(3);
063: literalPrefix = rs.getString(4);
064: literalSuffix = rs.getString(5);
065: createParams = rs.getString(6);
066: nullable = rs.getShort(7);
067: caseSensitive = rs.getBoolean(8);
068: searchable = rs.getShort(9);
069: unsigned = rs.getBoolean(10);
070: fixedPrecScale = rs.getBoolean(11);
071: autoIncrement = rs.getBoolean(12);
072: localTypeName = rs.getString(13);
073: if (rs.getMetaData().getColumnCount() >= 18) {
074: // Some servers provide more information
075: minimumScale = rs.getShort(14);
076: maximumScale = rs.getShort(15);
077: sqlDataType = rs.getInt(16);
078: sqlDatetimeSub = rs.getInt(17);
079: numPrecRadix = rs.getInt(18);
080: } else {
081: // Must initialize final fields
082: minimumScale = 0;
083: maximumScale = 0;
084: sqlDataType = 0;
085: sqlDatetimeSub = 0;
086: numPrecRadix = 0;
087: }
088: normalizedType = normalizeDataType(dataType, useLOBs);
089: distanceFromJdbcType = determineDistanceFromJdbcType();
090: }
091:
092: /**
093: * For testing only. Create an instance with just the properties utilised
094: * in the <code>compareTo()</code> method (set name, type, and auto
095: * increment).
096: */
097: public TypeInfo(String typeName, int dataType, boolean autoIncrement) {
098: this .typeName = typeName;
099: this .dataType = dataType;
100: this .autoIncrement = autoIncrement;
101: this .precision = 0;
102: this .literalPrefix = null;
103: this .literalSuffix = null;
104: this .createParams = null;
105: this .nullable = 0;
106: this .caseSensitive = false;
107: this .searchable = 0;
108: this .unsigned = false;
109: this .fixedPrecScale = false;
110: this .localTypeName = null;
111: this .minimumScale = 0;
112: this .maximumScale = 0;
113: this .sqlDataType = 0;
114: this .sqlDatetimeSub = 0;
115: this .numPrecRadix = 0;
116:
117: normalizedType = normalizeDataType(dataType, true);
118: distanceFromJdbcType = determineDistanceFromJdbcType();
119: }
120:
121: public boolean equals(Object o) {
122: if (o instanceof TypeInfo) {
123: return compareTo(o) == 0;
124: }
125:
126: return false;
127: }
128:
129: public int hashCode() {
130: return normalizedType * dataType * (autoIncrement ? 7 : 11);
131: }
132:
133: public String toString() {
134: return typeName + " ("
135: + (dataType != normalizedType ? dataType + "->" : "")
136: + normalizedType + ')';
137: }
138:
139: public void update(ResultSet rs) throws SQLException {
140: rs.updateString(1, typeName);
141: rs.updateInt(2, normalizedType);
142: rs.updateInt(3, precision);
143: rs.updateString(4, literalPrefix);
144: rs.updateString(5, literalSuffix);
145: rs.updateString(6, createParams);
146: rs.updateShort(7, nullable);
147: rs.updateBoolean(8, caseSensitive);
148: rs.updateShort(9, searchable);
149: rs.updateBoolean(10, unsigned);
150: rs.updateBoolean(11, fixedPrecScale);
151: rs.updateBoolean(12, autoIncrement);
152: rs.updateString(13, localTypeName);
153: if (rs.getMetaData().getColumnCount() >= 18) {
154: // Some servers provide more information
155: rs.updateShort(14, minimumScale);
156: rs.updateShort(15, maximumScale);
157: rs.updateInt(16, sqlDataType);
158: rs.updateInt(17, sqlDatetimeSub);
159: rs.updateInt(18, numPrecRadix);
160: }
161: }
162:
163: /**
164: * Comparable implementation that orders by dataType, then by how closely
165: * the data type maps to the corresponding JDBC SQL type.
166: * <p/>
167: * The data type values for the non-standard SQL Server types tend to have
168: * negative numbers while the corresponding standard types have positive
169: * numbers so utilise that in the sorting.
170: */
171: public int compareTo(Object o) {
172: TypeInfo other = ((TypeInfo) o);
173:
174: // Order by normalised type, then proximity to standard JDBC type.
175: return compare(normalizedType, other.normalizedType)
176: * 10
177: + compare(distanceFromJdbcType,
178: other.distanceFromJdbcType);
179: }
180:
181: private int compare(int i1, int i2) {
182: return i1 < i2 ? -1 : (i1 == i2 ? 0 : 1);
183: }
184:
185: /**
186: * Determine how close this type is to the corresponding JDBC type. Used in
187: * sorting to distinguish between types that have the same
188: * <code>normalizedType</code> value.
189: *
190: * @return positive integer indicating how far away the type is from the
191: * corresponding JDBC type, with zero being the nearest possible
192: * match and 9 being the least
193: */
194: private int determineDistanceFromJdbcType() {
195: // TODO: Are these assumptions correct/complete?
196: switch (dataType) {
197: // Cases without an un-normalized alternative, so these are the
198: // best available
199: case 11: // Sybase DATETIME
200: case 10: // Sybase TIME
201: case 9: // Sybase DATE
202: case 6: // FLOAT
203: return 0;
204: case 12: // VARCHAR, SYSNAME and NVARCHAR all together with Sybase
205: if (typeName.equalsIgnoreCase("varchar")) {
206: return 0;
207: }
208: if (typeName.equalsIgnoreCase("nvarchar")) {
209: return 1;
210: }
211: return 2;
212: // Special case as the same data type value is used for SYSNAME and
213: // NVARCHAR (SYSNAME is essentially an alias for NVARCHAR). We
214: // don't want applications preferring SYSNAME.
215: case -9: // SYSNAME / NVARCHAR
216: return typeName.equalsIgnoreCase("sysname") ? 4 : 3;
217:
218: // Particularly non-standard types
219: case -11: // UNIQUEIDENTIFIER
220: return 9;
221: case -150: // SQL_VARIANT
222: return 8;
223:
224: // Default behaviour is to assume that if type has not been
225: // normalised it is the closest available match, unless it is an
226: // auto incrementing type
227: default:
228: return (dataType == normalizedType && !autoIncrement) ? 0
229: : 5;
230: }
231: }
232:
233: /**
234: * Return a {@link java.sql.Types}-defined type for an SQL Server specific data type.
235: *
236: * @param serverDataType the data type, as returned by the server
237: * @param useLOBs whether LOB data types are used for large types
238: * @return the equivalent data type defined by <code>java.sql.Types</code>
239: */
240: public static int normalizeDataType(int serverDataType,
241: boolean useLOBs) {
242: switch (serverDataType) {
243: case 35: // Sybase UNIVARCHAR
244: return Types.VARCHAR;
245: case 11: // Sybase DATETIME
246: return Types.TIMESTAMP;
247: case 10: // Sybase TIME
248: return Types.TIME;
249: case 9: // Sybase DATE
250: return Types.DATE;
251: case 6: // FLOAT
252: return Types.DOUBLE;
253: case -1: // LONGVARCHAR
254: return useLOBs ? Types.CLOB : Types.LONGVARCHAR;
255: case -4: // LONGVARBINARY
256: return useLOBs ? Types.BLOB : Types.LONGVARBINARY;
257: case -8: // NCHAR
258: return Types.CHAR;
259: case -9: // SYSNAME / NVARCHAR
260: return Types.VARCHAR;
261: case -10: // NTEXT
262: return useLOBs ? Types.CLOB : Types.LONGVARCHAR;
263: case -11: // UNIQUEIDENTIFIER
264: return Types.CHAR;
265: case -150: // SQL_VARIANT
266: return Types.VARCHAR;
267: default:
268: return serverDataType;
269: }
270: }
271: }
|