0001: // jTDS JDBC Driver for Microsoft SQL Server and Sybase
0002: // Copyright (C) 2004 The jTDS Project
0003: //
0004: // This library is free software; you can redistribute it and/or
0005: // modify it under the terms of the GNU Lesser General Public
0006: // License as published by the Free Software Foundation; either
0007: // version 2.1 of the License, or (at your option) any later version.
0008: //
0009: // This library is distributed in the hope that it will be useful,
0010: // but WITHOUT ANY WARRANTY; without even the implied warranty of
0011: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0012: // Lesser General Public License for more details.
0013: //
0014: // You should have received a copy of the GNU Lesser General Public
0015: // License along with this library; if not, write to the Free Software
0016: // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0017: //
0018: package net.sourceforge.jtds.jdbc;
0019:
0020: import java.io.*;
0021: import java.math.BigInteger;
0022: import java.math.BigDecimal;
0023: import java.sql.SQLException;
0024:
0025: import net.sourceforge.jtds.util.BlobBuffer;
0026:
0027: /**
0028: * Implement TDS data types and related I/O logic.
0029: * <p>
0030: * Implementation notes:
0031: * <bl>
0032: * <li>This class encapsulates all the knowledge about reading and writing
0033: * TDS data descriptors and related application data.
0034: * <li>There are four key methods supplied here:
0035: * <ol>
0036: * <li>readType() - Reads the column and parameter meta data.
0037: * <li>readData() - Reads actual data values.
0038: * <li>writeParam() - Write parameter descriptors and data.
0039: * <li>getNativeType() - knows how to map JDBC data types to the equivalent TDS type.
0040: * </ol>
0041: * </bl>
0042: *
0043: * @author Mike Hutchinson
0044: * @author Alin Sinpalean
0045: * @author freeTDS project
0046: * @version $Id: TdsData.java,v 1.60 2007/08/19 02:25:28 bheineman Exp $
0047: */
0048: public class TdsData {
0049: /**
0050: * This class implements a descriptor for TDS data types;
0051: *
0052: * @author Mike Hutchinson.
0053: */
0054: private static class TypeInfo {
0055: /** The SQL type name. */
0056: public final String sqlType;
0057: /**
0058: * The size of this type or < 0 for variable sizes.
0059: * <p> Special values as follows:
0060: * <ol>
0061: * <li> -5 sql_variant type.
0062: * <li> -4 text, image or ntext types.
0063: * <li> -2 SQL Server 7+ long char and var binary types.
0064: * <li> -1 varchar, varbinary, null types.
0065: * </ol>
0066: */
0067: public final int size;
0068: /**
0069: * The precision of the type.
0070: * <p>If this is -1 precision must be calculated from buffer size
0071: * eg for varchar fields.
0072: */
0073: public final int precision;
0074: /**
0075: * The display size of the type.
0076: * <p>-1 If the display size must be calculated from the buffer size.
0077: */
0078: public final int displaySize;
0079: /** true if type is a signed numeric. */
0080: public final boolean isSigned;
0081: /** true if type requires TDS80 collation. */
0082: public final boolean isCollation;
0083: /** The java.sql.Types constant for this data type. */
0084: public final int jdbcType;
0085:
0086: /**
0087: * Construct a new TDS data type descriptor.
0088: *
0089: * @param sqlType SQL type name.
0090: * @param size Byte size for this type or < 0 for variable length types.
0091: * @param precision Decimal precision or -1
0092: * @param displaySize Printout size for this type or special values -1,-2.
0093: * @param isSigned True if signed numeric type.
0094: * @param isCollation True if type has TDS 8 collation information.
0095: * @param jdbcType The java.sql.Type constant for this type.
0096: */
0097: TypeInfo(String sqlType, int size, int precision,
0098: int displaySize, boolean isSigned, boolean isCollation,
0099: int jdbcType) {
0100: this .sqlType = sqlType;
0101: this .size = size;
0102: this .precision = precision;
0103: this .displaySize = displaySize;
0104: this .isSigned = isSigned;
0105: this .isCollation = isCollation;
0106: this .jdbcType = jdbcType;
0107: }
0108:
0109: }
0110:
0111: /*
0112: * Constants for TDS data types
0113: */
0114: private static final int SYBCHAR = 47; // 0x2F
0115: private static final int SYBVARCHAR = 39; // 0x27
0116: private static final int SYBINTN = 38; // 0x26
0117: private static final int SYBINT1 = 48; // 0x30
0118: private static final int SYBDATE = 49; // 0x31 Sybase 12
0119: private static final int SYBTIME = 51; // 0x33 Sybase 12
0120: private static final int SYBINT2 = 52; // 0x34
0121: private static final int SYBINT4 = 56; // 0x38
0122: private static final int SYBINT8 = 127;// 0x7F
0123: private static final int SYBFLT8 = 62; // 0x3E
0124: private static final int SYBDATETIME = 61; // 0x3D
0125: private static final int SYBBIT = 50; // 0x32
0126: private static final int SYBTEXT = 35; // 0x23
0127: private static final int SYBNTEXT = 99; // 0x63
0128: private static final int SYBIMAGE = 34; // 0x22
0129: private static final int SYBMONEY4 = 122;// 0x7A
0130: private static final int SYBMONEY = 60; // 0x3C
0131: private static final int SYBDATETIME4 = 58; // 0x3A
0132: private static final int SYBREAL = 59; // 0x3B
0133: private static final int SYBBINARY = 45; // 0x2D
0134: private static final int SYBVOID = 31; // 0x1F
0135: private static final int SYBVARBINARY = 37; // 0x25
0136: private static final int SYBNVARCHAR = 103;// 0x67
0137: private static final int SYBBITN = 104;// 0x68
0138: private static final int SYBNUMERIC = 108;// 0x6C
0139: private static final int SYBDECIMAL = 106;// 0x6A
0140: private static final int SYBFLTN = 109;// 0x6D
0141: private static final int SYBMONEYN = 110;// 0x6E
0142: private static final int SYBDATETIMN = 111;// 0x6F
0143: private static final int SYBDATEN = 123;// 0x7B SYBASE 12
0144: private static final int SYBTIMEN = 147;// 0x93 SYBASE 12
0145: private static final int XSYBCHAR = 175;// 0xAF
0146: private static final int XSYBVARCHAR = 167;// 0xA7
0147: private static final int XSYBNVARCHAR = 231;// 0xE7
0148: private static final int XSYBNCHAR = 239;// 0xEF
0149: private static final int XSYBVARBINARY = 165;// 0xA5
0150: private static final int XSYBBINARY = 173;// 0xAD
0151: private static final int SYBUNITEXT = 174;// 0xAE SYBASE 15
0152: private static final int SYBLONGBINARY = 225;// 0xE1 SYBASE 12
0153: private static final int SYBSINT1 = 64; // 0x40
0154: private static final int SYBUINT2 = 65; // 0x41 SYBASE 15
0155: private static final int SYBUINT4 = 66; // 0x42 SYBASE 15
0156: private static final int SYBUINT8 = 67; // 0x43 SYBASE 15
0157: private static final int SYBUINTN = 68; // 0x44 SYBASE 15
0158: private static final int SYBUNIQUE = 36; // 0x24
0159: private static final int SYBVARIANT = 98; // 0x62
0160: private static final int SYBSINT8 = 191;// 0xBF SYBASE 15
0161:
0162: /*
0163: * Special case for Sybase 12.5+
0164: * This long data type is used to send text and image
0165: * data as statement parameters as a replacement for
0166: * writetext.
0167: * As far as I can tell this data type is only sent not
0168: * received.
0169: */
0170: static final int SYBLONGDATA = 36; // 0x24 SYBASE 12
0171:
0172: /*
0173: * Constants for Sybase User Defined data types used to
0174: * qualify the new longchar and longbinary types.
0175: */
0176: // Common to Sybase and SQL Server
0177: private static final int UDT_CHAR = 1; // 0x01
0178: private static final int UDT_VARCHAR = 2; // 0x02
0179: private static final int UDT_BINARY = 3; // 0x03
0180: private static final int UDT_VARBINARY = 4; // 0x04
0181: private static final int UDT_SYSNAME = 18; // 0x12
0182: // Sybase only
0183: private static final int UDT_NCHAR = 24; // 0x18
0184: private static final int UDT_NVARCHAR = 25; // 0x19
0185: private static final int UDT_UNICHAR = 34; // 0x22
0186: private static final int UDT_UNIVARCHAR = 35; // 0x23
0187: private static final int UDT_UNITEXT = 36; // 0x24
0188: private static final int UDT_LONGSYSNAME = 42; // 0x2A
0189: private static final int UDT_TIMESTAMP = 80; // 0x50
0190: // SQL Server 7+
0191: private static final int UDT_NEWSYSNAME = 256; // 0x100
0192:
0193: /*
0194: * Constants for variable length data types
0195: */
0196: private static final int VAR_MAX = 255;
0197: private static final int SYB_LONGVAR_MAX = 16384;
0198: private static final int MS_LONGVAR_MAX = 8000;
0199: private static final int SYB_CHUNK_SIZE = 8192;
0200:
0201: /**
0202: * Array of TDS data type descriptors.
0203: */
0204: private final static TypeInfo types[] = new TypeInfo[256];
0205:
0206: /**
0207: * Static block to initialise TDS data type descriptors.
0208: */
0209: static {// SQL Type Size Prec DS signed TDS8 Col java Type
0210: types[SYBCHAR] = new TypeInfo("char", -1, -1, 1, false, false,
0211: java.sql.Types.CHAR);
0212: types[SYBVARCHAR] = new TypeInfo("varchar", -1, -1, 1, false,
0213: false, java.sql.Types.VARCHAR);
0214: types[SYBINTN] = new TypeInfo("int", -1, 10, 11, true, false,
0215: java.sql.Types.INTEGER);
0216: types[SYBINT1] = new TypeInfo("tinyint", 1, 3, 4, false, false,
0217: java.sql.Types.TINYINT);
0218: types[SYBINT2] = new TypeInfo("smallint", 2, 5, 6, true, false,
0219: java.sql.Types.SMALLINT);
0220: types[SYBINT4] = new TypeInfo("int", 4, 10, 11, true, false,
0221: java.sql.Types.INTEGER);
0222: types[SYBINT8] = new TypeInfo("bigint", 8, 19, 20, true, false,
0223: java.sql.Types.BIGINT);
0224: types[SYBFLT8] = new TypeInfo("float", 8, 15, 24, true, false,
0225: java.sql.Types.DOUBLE);
0226: types[SYBDATETIME] = new TypeInfo("datetime", 8, 23, 23, false,
0227: false, java.sql.Types.TIMESTAMP);
0228: types[SYBBIT] = new TypeInfo("bit", 1, 1, 1, false, false,
0229: java.sql.Types.BIT);
0230: types[SYBTEXT] = new TypeInfo("text", -4, -1, -1, false, true,
0231: java.sql.Types.CLOB);
0232: types[SYBNTEXT] = new TypeInfo("ntext", -4, -1, -1, false,
0233: true, java.sql.Types.CLOB);
0234: types[SYBUNITEXT] = new TypeInfo("unitext", -4, -1, -1, false,
0235: true, java.sql.Types.CLOB);
0236: types[SYBIMAGE] = new TypeInfo("image", -4, -1, -1, false,
0237: false, java.sql.Types.BLOB);
0238: types[SYBMONEY4] = new TypeInfo("smallmoney", 4, 10, 12, true,
0239: false, java.sql.Types.DECIMAL);
0240: types[SYBMONEY] = new TypeInfo("money", 8, 19, 21, true, false,
0241: java.sql.Types.DECIMAL);
0242: types[SYBDATETIME4] = new TypeInfo("smalldatetime", 4, 16, 19,
0243: false, false, java.sql.Types.TIMESTAMP);
0244: types[SYBREAL] = new TypeInfo("real", 4, 7, 14, true, false,
0245: java.sql.Types.REAL);
0246: types[SYBBINARY] = new TypeInfo("binary", -1, -1, 2, false,
0247: false, java.sql.Types.BINARY);
0248: types[SYBVOID] = new TypeInfo("void", -1, 1, 1, false, false, 0);
0249: types[SYBVARBINARY] = new TypeInfo("varbinary", -1, -1, -1,
0250: false, false, java.sql.Types.VARBINARY);
0251: types[SYBNVARCHAR] = new TypeInfo("nvarchar", -1, -1, -1,
0252: false, false, java.sql.Types.VARCHAR);
0253: types[SYBBITN] = new TypeInfo("bit", -1, 1, 1, false, false,
0254: java.sql.Types.BIT);
0255: types[SYBNUMERIC] = new TypeInfo("numeric", -1, -1, -1, true,
0256: false, java.sql.Types.NUMERIC);
0257: types[SYBDECIMAL] = new TypeInfo("decimal", -1, -1, -1, true,
0258: false, java.sql.Types.DECIMAL);
0259: types[SYBFLTN] = new TypeInfo("float", -1, 15, 24, true, false,
0260: java.sql.Types.DOUBLE);
0261: types[SYBMONEYN] = new TypeInfo("money", -1, 19, 21, true,
0262: false, java.sql.Types.DECIMAL);
0263: types[SYBDATETIMN] = new TypeInfo("datetime", -1, 23, 23,
0264: false, false, java.sql.Types.TIMESTAMP);
0265: types[SYBDATE] = new TypeInfo("date", 4, 10, 10, false, false,
0266: java.sql.Types.DATE);
0267: types[SYBTIME] = new TypeInfo("time", 4, 8, 8, false, false,
0268: java.sql.Types.TIME);
0269: types[SYBDATEN] = new TypeInfo("date", -1, 10, 10, false,
0270: false, java.sql.Types.DATE);
0271: types[SYBTIMEN] = new TypeInfo("time", -1, 8, 8, false, false,
0272: java.sql.Types.TIME);
0273: types[XSYBCHAR] = new TypeInfo("char", -2, -1, -1, false, true,
0274: java.sql.Types.CHAR);
0275: types[XSYBVARCHAR] = new TypeInfo("varchar", -2, -1, -1, false,
0276: true, java.sql.Types.VARCHAR);
0277: types[XSYBNVARCHAR] = new TypeInfo("nvarchar", -2, -1, -1,
0278: false, true, java.sql.Types.VARCHAR);
0279: types[XSYBNCHAR] = new TypeInfo("nchar", -2, -1, -1, false,
0280: true, java.sql.Types.CHAR);
0281: types[XSYBVARBINARY] = new TypeInfo("varbinary", -2, -1, -1,
0282: false, false, java.sql.Types.VARBINARY);
0283: types[XSYBBINARY] = new TypeInfo("binary", -2, -1, -1, false,
0284: false, java.sql.Types.BINARY);
0285: types[SYBLONGBINARY] = new TypeInfo("varbinary", -5, -1, 2,
0286: false, false, java.sql.Types.BINARY);
0287: types[SYBSINT1] = new TypeInfo("tinyint", 1, 2, 3, false,
0288: false, java.sql.Types.TINYINT);
0289: types[SYBUINT2] = new TypeInfo("unsigned smallint", 2, 5, 6,
0290: false, false, java.sql.Types.INTEGER);
0291: types[SYBUINT4] = new TypeInfo("unsigned int", 4, 10, 11,
0292: false, false, java.sql.Types.BIGINT);
0293: types[SYBUINT8] = new TypeInfo("unsigned bigint", 8, 20, 20,
0294: false, false, java.sql.Types.DECIMAL);
0295: types[SYBUINTN] = new TypeInfo("unsigned int", -1, 10, 11,
0296: true, false, java.sql.Types.BIGINT);
0297: types[SYBUNIQUE] = new TypeInfo("uniqueidentifier", -1, 36, 36,
0298: false, false, java.sql.Types.CHAR);
0299: types[SYBVARIANT] = new TypeInfo("sql_variant", -5, 0, 8000,
0300: false, false, java.sql.Types.VARCHAR);
0301: types[SYBSINT8] = new TypeInfo("bigint", 8, 19, 20, true,
0302: false, java.sql.Types.BIGINT);
0303: }
0304:
0305: /** Default Decimal Scale. */
0306: static final int DEFAULT_SCALE = 10;
0307: /** Default precision for SQL Server 6.5 and 7. */
0308: static final int DEFAULT_PRECISION_28 = 28;
0309: /** Default precision for Sybase and SQL Server 2000 and newer. */
0310: static final int DEFAULT_PRECISION_38 = 38;
0311:
0312: /**
0313: * TDS 8 supplies collation information for character data types.
0314: *
0315: * @param in the server response stream
0316: * @param ci the column descriptor
0317: * @return the number of bytes read from the stream as an <code>int</code>
0318: */
0319: static int getCollation(ResponseStream in, ColInfo ci)
0320: throws IOException {
0321: if (TdsData.isCollation(ci)) {
0322: // Read TDS8 collation info
0323: ci.collation = new byte[5];
0324: in.read(ci.collation);
0325:
0326: return 5;
0327: }
0328:
0329: return 0;
0330: }
0331:
0332: /**
0333: * Set the <code>charsetInfo</code> field of <code>ci</code> according to
0334: * the value of its <code>collation</code> field.
0335: * <p>
0336: * The <code>Connection</code> is used to find out whether a specific
0337: * charset was requested. In this case, the column charset will be ignored.
0338: *
0339: * @param ci the <code>ColInfo</code> instance to update
0340: * @param connection a <code>Connection</code> instance to check whether it
0341: * has a fixed charset or not
0342: * @throws SQLException if a <code>CharsetInfo</code> is not found for this
0343: * particular column collation
0344: */
0345: static void setColumnCharset(ColInfo ci, ConnectionJDBC2 connection)
0346: throws SQLException {
0347: if (connection.isCharsetSpecified()) {
0348: // If a charset was requested on connection creation, ignore the
0349: // column collation and use default
0350: ci.charsetInfo = connection.getCharsetInfo();
0351: } else if (ci.collation != null) {
0352: // TDS version will be 8.0 or higher in this case and connection
0353: // collation will be non-null
0354: byte[] collation = ci.collation;
0355: byte[] defaultCollation = connection.getCollation();
0356: int i;
0357:
0358: for (i = 0; i < 5; ++i) {
0359: if (collation[i] != defaultCollation[i]) {
0360: break;
0361: }
0362: }
0363:
0364: if (i == 5) {
0365: ci.charsetInfo = connection.getCharsetInfo();
0366: } else {
0367: ci.charsetInfo = CharsetInfo.getCharset(collation);
0368: }
0369: }
0370: }
0371:
0372: /**
0373: * Read the TDS datastream and populate the ColInfo parameter with
0374: * data type and related information.
0375: * <p>The type infomation conforms to one of the following formats:
0376: * <ol>
0377: * <li> [int1 type] - eg SYBINT4.
0378: * <li> [int1 type] [int1 buffersize] - eg VARCHAR < 256
0379: * <li> [int1 type] [int2 buffersize] - eg VARCHAR > 255.
0380: * <li> [int1 type] [int4 buffersize] [int1 tabnamelen] [int1*n tabname] - eg text.
0381: * <li> [int1 type] [int4 buffersize] - eg sql_variant.
0382: * <li> [int1 type] [int1 buffersize] [int1 precision] [int1 scale] - eg decimal.
0383: * </ol>
0384: * For TDS 8 large character types include a 5 byte collation field after the buffer size.
0385: *
0386: * @param in The server response stream.
0387: * @param ci The ColInfo column descriptor object.
0388: * @return The number of bytes read from the input stream.
0389: * @throws IOException
0390: * @throws ProtocolException
0391: */
0392: static int readType(ResponseStream in, ColInfo ci)
0393: throws IOException, ProtocolException {
0394: int tdsVersion = in.getTdsVersion();
0395: boolean isTds8 = tdsVersion >= Driver.TDS80;
0396: boolean isTds7 = tdsVersion >= Driver.TDS70;
0397: boolean isTds5 = tdsVersion == Driver.TDS50;
0398: boolean isTds42 = tdsVersion == Driver.TDS42;
0399: int bytesRead = 1;
0400: // Get the TDS data type code
0401: int type = in.read();
0402:
0403: if (types[type] == null || (isTds5 && type == SYBLONGDATA)) {
0404: // Trap invalid type or 0x24 received from a Sybase server!
0405: throw new ProtocolException("Invalid TDS data type 0x"
0406: + Integer.toHexString(type & 0xFF));
0407: }
0408:
0409: ci.tdsType = type;
0410: ci.jdbcType = types[type].jdbcType;
0411: ci.bufferSize = types[type].size;
0412:
0413: // Now get the buffersize if required
0414: if (ci.bufferSize == -5) {
0415: // sql_variant
0416: // Sybase long binary
0417: ci.bufferSize = in.readInt();
0418: bytesRead += 4;
0419: } else if (ci.bufferSize == -4) {
0420: // text or image
0421: ci.bufferSize = in.readInt();
0422:
0423: if (isTds8) {
0424: bytesRead += getCollation(in, ci);
0425: }
0426:
0427: int lenName = in.readShort();
0428:
0429: ci.tableName = in.readString(lenName);
0430: bytesRead += 6 + ((in.getTdsVersion() >= Driver.TDS70) ? lenName * 2
0431: : lenName);
0432: } else if (ci.bufferSize == -2) {
0433: // longvarchar longvarbinary
0434: if (isTds5 && ci.tdsType == XSYBCHAR) {
0435: ci.bufferSize = in.readInt();
0436: bytesRead += 4;
0437: } else {
0438: ci.bufferSize = in.readShort();
0439: bytesRead += 2;
0440: }
0441:
0442: if (isTds8) {
0443: bytesRead += getCollation(in, ci);
0444: }
0445:
0446: } else if (ci.bufferSize == -1) {
0447: // varchar varbinary decimal etc
0448: bytesRead += 1;
0449: ci.bufferSize = in.read();
0450: }
0451:
0452: // Set default displaySize and precision
0453: ci.displaySize = types[type].displaySize;
0454: ci.precision = types[type].precision;
0455: ci.sqlType = types[type].sqlType;
0456:
0457: // Now fine tune sizes for specific types
0458: switch (type) {
0459: //
0460: // long datetime has scale of 3 smalldatetime has scale of 0
0461: //
0462: case SYBDATETIME:
0463: ci.scale = 3;
0464: break;
0465: // Establish actual size of nullable datetime
0466: case SYBDATETIMN:
0467: if (ci.bufferSize == 8) {
0468: ci.displaySize = types[SYBDATETIME].displaySize;
0469: ci.precision = types[SYBDATETIME].precision;
0470: ci.scale = 3;
0471: } else {
0472: ci.displaySize = types[SYBDATETIME4].displaySize;
0473: ci.precision = types[SYBDATETIME4].precision;
0474: ci.sqlType = types[SYBDATETIME4].sqlType;
0475: ci.scale = 0;
0476: }
0477: break;
0478: // Establish actual size of nullable float
0479: case SYBFLTN:
0480: if (ci.bufferSize == 8) {
0481: ci.displaySize = types[SYBFLT8].displaySize;
0482: ci.precision = types[SYBFLT8].precision;
0483: } else {
0484: ci.displaySize = types[SYBREAL].displaySize;
0485: ci.precision = types[SYBREAL].precision;
0486: ci.jdbcType = java.sql.Types.REAL;
0487: ci.sqlType = types[SYBREAL].sqlType;
0488: }
0489: break;
0490: // Establish actual size of nullable int
0491: case SYBINTN:
0492: if (ci.bufferSize == 8) {
0493: ci.displaySize = types[SYBINT8].displaySize;
0494: ci.precision = types[SYBINT8].precision;
0495: ci.jdbcType = java.sql.Types.BIGINT;
0496: ci.sqlType = types[SYBINT8].sqlType;
0497: } else if (ci.bufferSize == 4) {
0498: ci.displaySize = types[SYBINT4].displaySize;
0499: ci.precision = types[SYBINT4].precision;
0500: } else if (ci.bufferSize == 2) {
0501: ci.displaySize = types[SYBINT2].displaySize;
0502: ci.precision = types[SYBINT2].precision;
0503: ci.jdbcType = java.sql.Types.SMALLINT;
0504: ci.sqlType = types[SYBINT2].sqlType;
0505: } else {
0506: ci.displaySize = types[SYBINT1].displaySize;
0507: ci.precision = types[SYBINT1].precision;
0508: ci.jdbcType = java.sql.Types.TINYINT;
0509: ci.sqlType = types[SYBINT1].sqlType;
0510: }
0511: break;
0512: // Establish actual size of nullable unsigned int
0513: case SYBUINTN:
0514: if (ci.bufferSize == 8) {
0515: ci.displaySize = types[SYBUINT8].displaySize;
0516: ci.precision = types[SYBUINT8].precision;
0517: ci.jdbcType = types[SYBUINT8].jdbcType;
0518: ci.sqlType = types[SYBUINT8].sqlType;
0519: } else if (ci.bufferSize == 4) {
0520: ci.displaySize = types[SYBUINT4].displaySize;
0521: ci.precision = types[SYBUINT4].precision;
0522: } else if (ci.bufferSize == 2) {
0523: ci.displaySize = types[SYBUINT2].displaySize;
0524: ci.precision = types[SYBUINT2].precision;
0525: ci.jdbcType = types[SYBUINT2].jdbcType;
0526: ci.sqlType = types[SYBUINT2].sqlType;
0527: } else {
0528: throw new ProtocolException(
0529: "unsigned int null (size 1) not supported");
0530: }
0531: break;
0532: //
0533: // Money types have a scale of 4
0534: //
0535: case SYBMONEY:
0536: case SYBMONEY4:
0537: ci.scale = 4;
0538: break;
0539: // Establish actual size of nullable money
0540: case SYBMONEYN:
0541: if (ci.bufferSize == 8) {
0542: ci.displaySize = types[SYBMONEY].displaySize;
0543: ci.precision = types[SYBMONEY].precision;
0544: } else {
0545: ci.displaySize = types[SYBMONEY4].displaySize;
0546: ci.precision = types[SYBMONEY4].precision;
0547: ci.sqlType = types[SYBMONEY4].sqlType;
0548: }
0549: ci.scale = 4;
0550: break;
0551:
0552: // Read in scale and precision for decimal types
0553: case SYBDECIMAL:
0554: case SYBNUMERIC:
0555: ci.precision = in.read();
0556: ci.scale = in.read();
0557: ci.displaySize = ((ci.scale > 0) ? 2 : 1) + ci.precision;
0558: bytesRead += 2;
0559: ci.sqlType = types[type].sqlType;
0560: break;
0561:
0562: // Although a binary type force displaysize to MAXINT
0563: case SYBIMAGE:
0564: ci.precision = Integer.MAX_VALUE;
0565: ci.displaySize = Integer.MAX_VALUE;
0566: break;
0567: // Normal binaries have a display size of 2 * precision 0x0A0B etc
0568: case SYBLONGBINARY:
0569: case SYBVARBINARY:
0570: case SYBBINARY:
0571: case XSYBBINARY:
0572: case XSYBVARBINARY:
0573: ci.precision = ci.bufferSize;
0574: ci.displaySize = ci.precision * 2;
0575: break;
0576:
0577: // SQL Server unicode text can only display half as many chars
0578: case SYBNTEXT:
0579: ci.precision = Integer.MAX_VALUE / 2;
0580: ci.displaySize = Integer.MAX_VALUE / 2;
0581: break;
0582:
0583: // ASE 15+ unicode text can only display half as many chars
0584: case SYBUNITEXT:
0585: ci.precision = Integer.MAX_VALUE / 2;
0586: ci.displaySize = Integer.MAX_VALUE / 2;
0587: break;
0588:
0589: // SQL Server unicode chars can only display half as many chars
0590: case XSYBNCHAR:
0591: case XSYBNVARCHAR:
0592: ci.displaySize = ci.bufferSize / 2;
0593: ci.precision = ci.displaySize;
0594: break;
0595:
0596: // Normal characters display size = precision = buffer size.
0597: case SYBTEXT:
0598: case SYBCHAR:
0599: case XSYBCHAR:
0600: case XSYBVARCHAR:
0601: case SYBVARCHAR:
0602: case SYBNVARCHAR:
0603: ci.precision = ci.bufferSize;
0604: ci.displaySize = ci.precision;
0605: break;
0606: }
0607:
0608: // For numeric types add 'identity' for auto inc data type
0609: if (ci.isIdentity) {
0610: ci.sqlType += " identity";
0611: }
0612:
0613: // Fine tune Sybase or SQL 6.5 data types
0614: if (isTds42 || isTds5) {
0615: switch (ci.userType) {
0616: case UDT_CHAR:
0617: ci.sqlType = "char";
0618: ci.displaySize = ci.bufferSize;
0619: ci.jdbcType = java.sql.Types.CHAR;
0620: break;
0621: case UDT_VARCHAR:
0622: ci.sqlType = "varchar";
0623: ci.displaySize = ci.bufferSize;
0624: ci.jdbcType = java.sql.Types.VARCHAR;
0625: break;
0626: case UDT_BINARY:
0627: ci.sqlType = "binary";
0628: ci.displaySize = ci.bufferSize * 2;
0629: ci.jdbcType = java.sql.Types.BINARY;
0630: break;
0631: case UDT_VARBINARY:
0632: ci.sqlType = "varbinary";
0633: ci.displaySize = ci.bufferSize * 2;
0634: ci.jdbcType = java.sql.Types.VARBINARY;
0635: break;
0636: case UDT_SYSNAME:
0637: ci.sqlType = "sysname";
0638: ci.displaySize = ci.bufferSize;
0639: ci.jdbcType = java.sql.Types.VARCHAR;
0640: break;
0641: case UDT_TIMESTAMP:
0642: ci.sqlType = "timestamp";
0643: ci.displaySize = ci.bufferSize * 2;
0644: ci.jdbcType = java.sql.Types.VARBINARY;
0645: break;
0646: }
0647: }
0648:
0649: // Fine tune Sybase data types
0650: if (isTds5) {
0651: switch (ci.userType) {
0652: case UDT_NCHAR:
0653: ci.sqlType = "nchar";
0654: ci.displaySize = ci.bufferSize;
0655: ci.jdbcType = java.sql.Types.CHAR;
0656: break;
0657: case UDT_NVARCHAR:
0658: ci.sqlType = "nvarchar";
0659: ci.displaySize = ci.bufferSize;
0660: ci.jdbcType = java.sql.Types.VARCHAR;
0661: break;
0662: case UDT_UNICHAR:
0663: ci.sqlType = "unichar";
0664: ci.displaySize = ci.bufferSize / 2;
0665: ci.precision = ci.displaySize;
0666: ci.jdbcType = java.sql.Types.CHAR;
0667: break;
0668: case UDT_UNIVARCHAR:
0669: ci.sqlType = "univarchar";
0670: ci.displaySize = ci.bufferSize / 2;
0671: ci.precision = ci.displaySize;
0672: ci.jdbcType = java.sql.Types.VARCHAR;
0673: break;
0674: case UDT_LONGSYSNAME:
0675: ci.sqlType = "longsysname";
0676: ci.jdbcType = java.sql.Types.VARCHAR;
0677: ci.displaySize = ci.bufferSize;
0678: break;
0679: }
0680: }
0681: // Fine tune SQL Server 7+ datatypes
0682: if (isTds7) {
0683: switch (ci.userType) {
0684: case UDT_TIMESTAMP:
0685: ci.sqlType = "timestamp";
0686: ci.jdbcType = java.sql.Types.BINARY;
0687: break;
0688: case UDT_NEWSYSNAME:
0689: ci.sqlType = "sysname";
0690: ci.jdbcType = java.sql.Types.VARCHAR;
0691: break;
0692: }
0693: }
0694:
0695: return bytesRead;
0696: }
0697:
0698: /**
0699: * Read the TDS data item from the Response Stream.
0700: * <p> The data size is either implicit in the type for example
0701: * fixed size integers, or a count field precedes the actual data.
0702: * The size of the count field varies with the data type.
0703: *
0704: * @param connection an object reference to the caller of this method;
0705: * must be a <code>Connection</code>, <code>Statement</code> or
0706: * <code>ResultSet</code>
0707: * @param in The server ResponseStream.
0708: * @param ci The ColInfo column descriptor object.
0709: * @return The data item Object or null.
0710: * @throws IOException
0711: * @throws ProtocolException
0712: */
0713: static Object readData(ConnectionJDBC2 connection,
0714: ResponseStream in, ColInfo ci) throws IOException,
0715: ProtocolException {
0716: int len;
0717:
0718: switch (ci.tdsType) {
0719: case SYBINTN:
0720: switch (in.read()) {
0721: case 1:
0722: return new Integer(in.read() & 0xFF);
0723: case 2:
0724: return new Integer(in.readShort());
0725: case 4:
0726: return new Integer(in.readInt());
0727: case 8:
0728: return new Long(in.readLong());
0729: }
0730:
0731: break;
0732:
0733: // Sybase ASE 15+ supports unsigned null smallint, int and bigint
0734: case SYBUINTN:
0735: switch (in.read()) {
0736: case 1:
0737: return new Integer(in.read() & 0xFF);
0738: case 2:
0739: return new Integer((int) in.readShort() & 0xFFFF);
0740: case 4:
0741: return new Long((long) in.readInt() & 0xFFFFFFFFL);
0742: case 8:
0743: return in.readUnsignedLong();
0744: }
0745: break;
0746:
0747: case SYBINT1:
0748: return new Integer(in.read() & 0xFF);
0749:
0750: case SYBINT2:
0751: return new Integer(in.readShort());
0752:
0753: case SYBINT4:
0754: return new Integer(in.readInt());
0755:
0756: // SQL Server bigint
0757: case SYBINT8:
0758: return new Long(in.readLong());
0759:
0760: // Sybase ASE 15+ bigint
0761: case SYBSINT8:
0762: return new Long(in.readLong());
0763:
0764: // Sybase ASE 15+ unsigned smallint
0765: case SYBUINT2:
0766: return new Integer((int) in.readShort() & 0xFFFF);
0767:
0768: // Sybase ASE 15+ unsigned int
0769: case SYBUINT4:
0770: return new Long((long) in.readInt() & 0xFFFFFFFFL);
0771:
0772: // Sybase ASE 15+ unsigned bigint
0773: case SYBUINT8:
0774: return in.readUnsignedLong();
0775:
0776: case SYBIMAGE:
0777: len = in.read();
0778:
0779: if (len > 0) {
0780: in.skip(24); // Skip textptr and timestamp
0781: int dataLen = in.readInt();
0782: BlobImpl blob;
0783: if (dataLen == 0 && in.getTdsVersion() <= Driver.TDS50) {
0784: // Length of zero may indicate an initialized image
0785: // column that has been updated to null.
0786: break;
0787: }
0788: if (dataLen <= connection.getLobBuffer()) {
0789: //
0790: // OK Small enough to load into memory
0791: //
0792: byte[] data = new byte[dataLen];
0793: in.read(data);
0794: blob = new BlobImpl(connection, data);
0795: } else {
0796: // Too big, need to write straight to disk
0797: try {
0798: blob = new BlobImpl(connection);
0799: OutputStream out = blob.setBinaryStream(1);
0800: byte[] buffer = new byte[1024];
0801: int result;
0802: while ((result = in.read(buffer, 0, Math.min(
0803: dataLen, buffer.length))) != -1
0804: && dataLen != 0) {
0805: out.write(buffer, 0, result);
0806: dataLen -= result;
0807: }
0808: out.close();
0809: } catch (SQLException e) {
0810: // Transform setBinaryStream SQLException
0811: throw new IOException(e.getMessage());
0812: }
0813: }
0814: return blob;
0815: }
0816:
0817: break;
0818:
0819: case SYBTEXT:
0820: len = in.read();
0821:
0822: if (len > 0) {
0823: String charset;
0824: if (ci.charsetInfo != null) {
0825: charset = ci.charsetInfo.getCharset();
0826: } else {
0827: charset = connection.getCharset();
0828: }
0829: in.skip(24); // Skip textptr and timestamp
0830: int dataLen = in.readInt();
0831: if (dataLen == 0 && in.getTdsVersion() <= Driver.TDS50) {
0832: // Length of zero may indicate an initialized text
0833: // column that has been updated to null.
0834: break;
0835: }
0836: ClobImpl clob = new ClobImpl(connection);
0837: BlobBuffer blobBuffer = clob.getBlobBuffer();
0838: if (dataLen <= connection.getLobBuffer()) {
0839: //
0840: // OK Small enough to load into memory
0841: //
0842: BufferedReader rdr = new BufferedReader(
0843: new InputStreamReader(in
0844: .getInputStream(dataLen), charset),
0845: 1024);
0846: byte[] data = new byte[dataLen * 2];
0847: int p = 0;
0848: int c;
0849: while ((c = rdr.read()) >= 0) {
0850: data[p++] = (byte) c;
0851: data[p++] = (byte) (c >> 8);
0852: }
0853: rdr.close();
0854: blobBuffer.setBuffer(data, false);
0855: if (p == 2 && data[0] == 0x20 && data[1] == 0
0856: && in.getTdsVersion() < Driver.TDS70) {
0857: // Single space with Sybase equates to empty string
0858: p = 0;
0859: }
0860: // Explicitly set length as multi byte character sets
0861: // may not fill array completely.
0862: blobBuffer.setLength(p);
0863: } else {
0864: // Too big, need to write straight to disk
0865: BufferedReader rdr = new BufferedReader(
0866: new InputStreamReader(in
0867: .getInputStream(dataLen), charset),
0868: 1024);
0869: try {
0870: OutputStream out = blobBuffer.setBinaryStream(
0871: 1, false);
0872: int c;
0873: while ((c = rdr.read()) >= 0) {
0874: out.write(c);
0875: out.write(c >> 8);
0876: }
0877: out.close();
0878: rdr.close();
0879: } catch (SQLException e) {
0880: // Turn back into an IOException
0881: throw new IOException(e.getMessage());
0882: }
0883: }
0884: return clob;
0885: }
0886:
0887: break;
0888:
0889: case SYBUNITEXT: // ASE 15+ unicode text type
0890: case SYBNTEXT:
0891: len = in.read();
0892:
0893: if (len > 0) {
0894: in.skip(24); // Skip textptr and timestamp
0895: int dataLen = in.readInt();
0896: if (dataLen == 0 && in.getTdsVersion() <= Driver.TDS50) {
0897: // Length of zero may indicate an initialized unitext
0898: // column that has been updated to null.
0899: break;
0900: }
0901: ClobImpl clob = new ClobImpl(connection);
0902: BlobBuffer blobBuffer = clob.getBlobBuffer();
0903: if (dataLen <= connection.getLobBuffer()) {
0904: //
0905: // OK Small enough to load into memory
0906: //
0907: byte[] data = new byte[dataLen];
0908: in.read(data);
0909: blobBuffer.setBuffer(data, false);
0910: if (dataLen == 2 && data[0] == 0x20 && data[1] == 0
0911: && in.getTdsVersion() == Driver.TDS50) {
0912: // Single space with Sybase equates to empty string
0913: dataLen = 0;
0914: }
0915: // Explicitly set length as multi byte character sets
0916: // may not fill array completely.
0917: blobBuffer.setLength(dataLen);
0918: } else {
0919: // Too big, need to write straight to disk
0920: try {
0921: OutputStream out = blobBuffer.setBinaryStream(
0922: 1, false);
0923: byte[] buffer = new byte[1024];
0924: int result;
0925: while ((result = in.read(buffer, 0, Math.min(
0926: dataLen, buffer.length))) != -1
0927: && dataLen != 0) {
0928: out.write(buffer, 0, result);
0929: dataLen -= result;
0930: }
0931: out.close();
0932: } catch (SQLException e) {
0933: // Transform setBinaryStream SQLException
0934: throw new IOException(e.getMessage());
0935: }
0936: }
0937: return clob;
0938: }
0939:
0940: break;
0941:
0942: case SYBCHAR:
0943: case SYBVARCHAR:
0944: len = in.read();
0945:
0946: if (len > 0) {
0947: String value = in.readNonUnicodeString(len,
0948: ci.charsetInfo == null ? connection
0949: .getCharsetInfo() : ci.charsetInfo);
0950:
0951: if (len == 1 && ci.tdsType == SYBVARCHAR
0952: && in.getTdsVersion() < Driver.TDS70) {
0953: // In TDS 4/5 zero length varchars are stored as a
0954: // single space to distinguish them from nulls.
0955: return (" ".equals(value)) ? "" : value;
0956: }
0957:
0958: return value;
0959: }
0960:
0961: break;
0962:
0963: case SYBNVARCHAR:
0964: len = in.read();
0965:
0966: if (len > 0) {
0967: return in.readUnicodeString(len / 2);
0968: }
0969:
0970: break;
0971:
0972: case XSYBCHAR:
0973: case XSYBVARCHAR:
0974: if (in.getTdsVersion() == Driver.TDS50) {
0975: // This is a Sybase wide table String
0976: len = in.readInt();
0977: if (len > 0) {
0978: String tmp = in.readNonUnicodeString(len);
0979: if (" ".equals(tmp) && !"char".equals(ci.sqlType)) {
0980: tmp = "";
0981: }
0982: return tmp;
0983: }
0984: } else {
0985: // This is a TDS 7+ long string
0986: len = in.readShort();
0987: if (len != -1) {
0988: return in.readNonUnicodeString(len,
0989: ci.charsetInfo == null ? connection
0990: .getCharsetInfo() : ci.charsetInfo);
0991: }
0992: }
0993:
0994: break;
0995:
0996: case XSYBNCHAR:
0997: case XSYBNVARCHAR:
0998: len = in.readShort();
0999:
1000: if (len != -1) {
1001: return in.readUnicodeString(len / 2);
1002: }
1003:
1004: break;
1005:
1006: case SYBVARBINARY:
1007: case SYBBINARY:
1008: len = in.read();
1009:
1010: if (len > 0) {
1011: byte[] bytes = new byte[len];
1012:
1013: in.read(bytes);
1014:
1015: return bytes;
1016: }
1017:
1018: break;
1019:
1020: case XSYBVARBINARY:
1021: case XSYBBINARY:
1022: len = in.readShort();
1023:
1024: if (len != -1) {
1025: byte[] bytes = new byte[len];
1026:
1027: in.read(bytes);
1028:
1029: return bytes;
1030: }
1031:
1032: break;
1033:
1034: case SYBLONGBINARY:
1035: len = in.readInt();
1036: if (len != 0) {
1037: if ("unichar".equals(ci.sqlType)
1038: || "univarchar".equals(ci.sqlType)) {
1039: char[] buf = new char[len / 2];
1040: in.read(buf);
1041: if ((len & 1) != 0) {
1042: // Bad length should be divisible by 2
1043: in.skip(1); // Deal with it anyway.
1044: }
1045: if (len == 2 && buf[0] == ' ') {
1046: return "";
1047: } else {
1048: return new String(buf);
1049: }
1050: } else {
1051: byte[] bytes = new byte[len];
1052: in.read(bytes);
1053: return bytes;
1054: }
1055: }
1056: break;
1057:
1058: case SYBMONEY4:
1059: case SYBMONEY:
1060: case SYBMONEYN:
1061: return getMoneyValue(in, ci.tdsType);
1062:
1063: case SYBDATETIME4:
1064: case SYBDATETIMN:
1065: case SYBDATETIME:
1066: return getDatetimeValue(in, ci.tdsType);
1067:
1068: case SYBDATEN:
1069: case SYBDATE:
1070: len = (ci.tdsType == SYBDATEN) ? in.read() : 4;
1071: if (len == 4) {
1072: return new DateTime(in.readInt(), -1);
1073: } else {
1074: // Invalid length or 0 for null
1075: in.skip(len);
1076: }
1077: break;
1078:
1079: case SYBTIMEN:
1080: case SYBTIME:
1081: len = (ci.tdsType == SYBTIMEN) ? in.read() : 4;
1082: if (len == 4) {
1083: return new DateTime(-1, in.readInt());
1084: } else {
1085: // Invalid length or 0 for null
1086: in.skip(len);
1087: }
1088: break;
1089:
1090: case SYBBIT:
1091: return (in.read() != 0) ? Boolean.TRUE : Boolean.FALSE;
1092:
1093: case SYBBITN:
1094: len = in.read();
1095:
1096: if (len > 0) {
1097: return (in.read() != 0) ? Boolean.TRUE : Boolean.FALSE;
1098: }
1099:
1100: break;
1101:
1102: case SYBREAL:
1103: return new Float(Float.intBitsToFloat(in.readInt()));
1104:
1105: case SYBFLT8:
1106: return new Double(Double.longBitsToDouble(in.readLong()));
1107:
1108: case SYBFLTN:
1109: len = in.read();
1110:
1111: if (len == 4) {
1112: return new Float(Float.intBitsToFloat(in.readInt()));
1113: } else if (len == 8) {
1114: return new Double(Double
1115: .longBitsToDouble(in.readLong()));
1116: }
1117:
1118: break;
1119:
1120: case SYBUNIQUE:
1121: len = in.read();
1122:
1123: if (len > 0) {
1124: byte[] bytes = new byte[len];
1125:
1126: in.read(bytes);
1127:
1128: return new UniqueIdentifier(bytes);
1129: }
1130:
1131: break;
1132:
1133: case SYBNUMERIC:
1134: case SYBDECIMAL:
1135: len = in.read();
1136:
1137: if (len > 0) {
1138: int sign = in.read();
1139:
1140: len--;
1141: byte[] bytes = new byte[len];
1142: BigInteger bi;
1143:
1144: if (in.getServerType() == Driver.SYBASE) {
1145: // Sybase order is MSB first!
1146: for (int i = 0; i < len; i++) {
1147: bytes[i] = (byte) in.read();
1148: }
1149:
1150: bi = new BigInteger((sign == 0) ? 1 : -1, bytes);
1151: } else {
1152: while (len-- > 0) {
1153: bytes[len] = (byte) in.read();
1154: }
1155:
1156: bi = new BigInteger((sign == 0) ? -1 : 1, bytes);
1157: }
1158:
1159: return new BigDecimal(bi, ci.scale);
1160: }
1161:
1162: break;
1163:
1164: case SYBVARIANT:
1165: return getVariant(connection, in);
1166:
1167: default:
1168: throw new ProtocolException("Unsupported TDS data type 0x"
1169: + Integer.toHexString(ci.tdsType & 0xFF));
1170: }
1171:
1172: return null;
1173: }
1174:
1175: /**
1176: * Retrieve the signed status of the column.
1177: *
1178: * @param ci the column meta data
1179: * @return <code>true</code> if the column is a signed numeric.
1180: */
1181: static boolean isSigned(ColInfo ci) {
1182: int type = ci.tdsType;
1183:
1184: if (type < 0 || type > 255 || types[type] == null) {
1185: throw new IllegalArgumentException("TDS data type " + type
1186: + " invalid");
1187: }
1188: if (type == TdsData.SYBINTN && ci.bufferSize == 1) {
1189: type = TdsData.SYBINT1; // Tiny int is not signed!
1190: }
1191: return types[type].isSigned;
1192: }
1193:
1194: /**
1195: * Retrieve the collation status of the column.
1196: * <p/>
1197: * TDS 8.0 character columns include collation information.
1198: *
1199: * @param ci the column meta data
1200: * @return <code>true</code> if the column requires collation data.
1201: */
1202: static boolean isCollation(ColInfo ci) {
1203: int type = ci.tdsType;
1204:
1205: if (type < 0 || type > 255 || types[type] == null) {
1206: throw new IllegalArgumentException("TDS data type " + type
1207: + " invalid");
1208: }
1209:
1210: return types[type].isCollation;
1211: }
1212:
1213: /**
1214: * Retrieve the currency status of the column.
1215: *
1216: * @param ci The column meta data.
1217: * @return <code>boolean</code> true if the column is a currency type.
1218: */
1219: static boolean isCurrency(ColInfo ci) {
1220: int type = ci.tdsType;
1221:
1222: if (type < 0 || type > 255 || types[type] == null) {
1223: throw new IllegalArgumentException("TDS data type " + type
1224: + " invalid");
1225: }
1226:
1227: return type == SYBMONEY || type == SYBMONEY4
1228: || type == SYBMONEYN;
1229: }
1230:
1231: /**
1232: * Retrieve the searchable status of the column.
1233: *
1234: * @param ci the column meta data
1235: * @return <code>true</code> if the column is not a text or image type.
1236: */
1237: static boolean isSearchable(ColInfo ci) {
1238: int type = ci.tdsType;
1239:
1240: if (type < 0 || type > 255 || types[type] == null) {
1241: throw new IllegalArgumentException("TDS data type " + type
1242: + " invalid");
1243: }
1244:
1245: return types[type].size != -4;
1246: }
1247:
1248: /**
1249: * Determines whether the column is Unicode encoded.
1250: *
1251: * @param ci the column meta data
1252: * @return <code>true</code> if the column is Unicode encoded
1253: */
1254: static boolean isUnicode(ColInfo ci) {
1255: int type = ci.tdsType;
1256:
1257: if (type < 0 || type > 255 || types[type] == null) {
1258: throw new IllegalArgumentException("TDS data type " + type
1259: + " invalid");
1260: }
1261:
1262: switch (type) {
1263: case SYBNVARCHAR:
1264: case SYBNTEXT:
1265: case XSYBNCHAR:
1266: case XSYBNVARCHAR:
1267: case XSYBCHAR: // Not always
1268: case SYBVARIANT: // Not always
1269: return true;
1270:
1271: default:
1272: return false;
1273: }
1274: }
1275:
1276: /**
1277: * Fill in the TDS native type code and all other fields for a
1278: * <code>ColInfo</code> instance with the JDBC type set.
1279: *
1280: * @param ci the <code>ColInfo</code> instance
1281: */
1282: static void fillInType(ColInfo ci) throws SQLException {
1283: switch (ci.jdbcType) {
1284: case java.sql.Types.VARCHAR:
1285: ci.tdsType = SYBVARCHAR;
1286: ci.bufferSize = MS_LONGVAR_MAX;
1287: ci.displaySize = MS_LONGVAR_MAX;
1288: ci.precision = MS_LONGVAR_MAX;
1289: break;
1290: case java.sql.Types.INTEGER:
1291: ci.tdsType = SYBINT4;
1292: ci.bufferSize = 4;
1293: ci.displaySize = 11;
1294: ci.precision = 10;
1295: break;
1296: case java.sql.Types.SMALLINT:
1297: ci.tdsType = SYBINT2;
1298: ci.bufferSize = 2;
1299: ci.displaySize = 6;
1300: ci.precision = 5;
1301: break;
1302: case java.sql.Types.BIT:
1303: ci.tdsType = SYBBIT;
1304: ci.bufferSize = 1;
1305: ci.displaySize = 1;
1306: ci.precision = 1;
1307: break;
1308: default:
1309: throw new SQLException(Messages.get("error.baddatatype",
1310: Integer.toString(ci.jdbcType)), "HY000");
1311: }
1312: ci.sqlType = types[ci.tdsType].sqlType;
1313: ci.scale = 0;
1314: }
1315:
1316: /**
1317: * Retrieve the TDS native type code for the parameter.
1318: *
1319: * @param connection the connectionJDBC object
1320: * @param pi the parameter descriptor
1321: */
1322: static void getNativeType(ConnectionJDBC2 connection, ParamInfo pi)
1323: throws SQLException {
1324: int len;
1325: int jdbcType = pi.jdbcType;
1326:
1327: if (jdbcType == java.sql.Types.OTHER) {
1328: jdbcType = Support.getJdbcType(pi.value);
1329: }
1330:
1331: switch (jdbcType) {
1332: case java.sql.Types.CHAR:
1333: case java.sql.Types.VARCHAR:
1334: case java.sql.Types.LONGVARCHAR:
1335: case java.sql.Types.CLOB:
1336: if (pi.value == null) {
1337: len = 0;
1338: } else {
1339: len = pi.length;
1340: }
1341: if (connection.getTdsVersion() < Driver.TDS70) {
1342: String charset = connection.getCharset();
1343: if (len > 0
1344: && (len <= SYB_LONGVAR_MAX / 2 || connection
1345: .getSybaseInfo(TdsCore.SYB_UNITEXT))
1346: && connection
1347: .getSybaseInfo(TdsCore.SYB_UNICODE)
1348: && connection.getUseUnicode()
1349: && !"UTF-8".equals(charset)) {
1350: // Sybase can send values as unicode if conversion to the
1351: // server charset fails.
1352: // One option to determine if conversion will fail is to use
1353: // the CharSetEncoder class but this is only available from
1354: // JDK 1.4.
1355: // For now we will call a local method to see if the string
1356: // should be sent as unicode.
1357: // This behaviour can be disabled by setting the connection
1358: // property sendParametersAsUnicode=false.
1359: // TODO: Find a better way of testing for convertable charset.
1360: // With ASE 15 this code will read a CLOB into memory just to
1361: // check for unicode characters. This is wasteful if no unicode
1362: // data is present and we are targetting a text column. The option
1363: // of always sending unicode does not work as the server will
1364: // complain about image to text conversions unless the target
1365: // column actually is unitext.
1366: try {
1367: String tmp = pi.getString(charset);
1368: if (!canEncode(tmp, charset)) {
1369: // Conversion fails need to send as unicode.
1370: pi.length = tmp.length();
1371: if (pi.length > SYB_LONGVAR_MAX / 2) {
1372: pi.sqlType = "unitext";
1373: pi.tdsType = SYBLONGDATA;
1374: } else {
1375: pi.sqlType = "univarchar(" + pi.length
1376: + ')';
1377: pi.tdsType = SYBLONGBINARY;
1378: }
1379: break;
1380: }
1381: } catch (IOException e) {
1382: throw new SQLException(Messages
1383: .get("error.generic.ioerror", e
1384: .getMessage()), "HY000");
1385: }
1386: }
1387: //
1388: // If the client character set is wide then we need to ensure that the size
1389: // is within bounds even after conversion from Unicode
1390: //
1391: if (connection.isWideChar() && len <= SYB_LONGVAR_MAX) {
1392: try {
1393: byte tmp[] = pi.getBytes(charset);
1394: len = (tmp == null) ? 0 : tmp.length;
1395: } catch (IOException e) {
1396: throw new SQLException(Messages
1397: .get("error.generic.ioerror", e
1398: .getMessage()), "HY000");
1399: }
1400: }
1401: if (len <= VAR_MAX) {
1402: pi.tdsType = SYBVARCHAR;
1403: pi.sqlType = "varchar(255)";
1404: } else {
1405: if (connection.getSybaseInfo(TdsCore.SYB_LONGDATA)) {
1406: if (len > SYB_LONGVAR_MAX) {
1407: // Use special Sybase long data type which
1408: // allows text data to be sent as a statement parameter
1409: // (although not as a SP parameter).
1410: pi.tdsType = SYBLONGDATA;
1411: pi.sqlType = "text";
1412: } else {
1413: // Use Sybase 12.5+ long varchar type which
1414: // is limited to 16384 bytes.
1415: pi.tdsType = XSYBCHAR;
1416: pi.sqlType = "varchar(" + len + ')';
1417: }
1418: } else {
1419: pi.tdsType = SYBTEXT;
1420: pi.sqlType = "text";
1421: }
1422: }
1423: } else {
1424: if (pi.isUnicode && len <= MS_LONGVAR_MAX / 2) {
1425: pi.tdsType = XSYBNVARCHAR;
1426: pi.sqlType = "nvarchar(4000)";
1427: } else if (!pi.isUnicode && len <= MS_LONGVAR_MAX) {
1428: pi.tdsType = XSYBVARCHAR;
1429: pi.sqlType = "varchar(8000)";
1430: } else {
1431: if (pi.isOutput) {
1432: throw new SQLException(Messages
1433: .get("error.textoutparam"), "HY000");
1434: }
1435:
1436: if (pi.isUnicode) {
1437: pi.tdsType = SYBNTEXT;
1438: pi.sqlType = "ntext";
1439: } else {
1440: pi.tdsType = SYBTEXT;
1441: pi.sqlType = "text";
1442: }
1443: }
1444: }
1445: break;
1446:
1447: case java.sql.Types.TINYINT:
1448: case java.sql.Types.SMALLINT:
1449: case java.sql.Types.INTEGER:
1450: pi.tdsType = SYBINTN;
1451: pi.sqlType = "int";
1452: break;
1453:
1454: case JtdsStatement.BOOLEAN:
1455: case java.sql.Types.BIT:
1456: if (connection.getTdsVersion() >= Driver.TDS70
1457: || connection.getSybaseInfo(TdsCore.SYB_BITNULL)) {
1458: pi.tdsType = SYBBITN;
1459: } else {
1460: pi.tdsType = SYBBIT;
1461: }
1462:
1463: pi.sqlType = "bit";
1464: break;
1465:
1466: case java.sql.Types.REAL:
1467: pi.tdsType = SYBFLTN;
1468: pi.sqlType = "real";
1469: break;
1470:
1471: case java.sql.Types.FLOAT:
1472: case java.sql.Types.DOUBLE:
1473: pi.tdsType = SYBFLTN;
1474: pi.sqlType = "float";
1475: break;
1476:
1477: case java.sql.Types.DATE:
1478: if (connection.getSybaseInfo(TdsCore.SYB_DATETIME)) {
1479: pi.tdsType = SYBDATEN;
1480: pi.sqlType = "date";
1481: } else {
1482: pi.tdsType = SYBDATETIMN;
1483: pi.sqlType = "datetime";
1484: }
1485: break;
1486: case java.sql.Types.TIME:
1487: if (connection.getSybaseInfo(TdsCore.SYB_DATETIME)) {
1488: pi.tdsType = SYBTIMEN;
1489: pi.sqlType = "time";
1490: } else {
1491: pi.tdsType = SYBDATETIMN;
1492: pi.sqlType = "datetime";
1493: }
1494: break;
1495: case java.sql.Types.TIMESTAMP:
1496: pi.tdsType = SYBDATETIMN;
1497: pi.sqlType = "datetime";
1498: break;
1499:
1500: case java.sql.Types.BINARY:
1501: case java.sql.Types.VARBINARY:
1502: case java.sql.Types.BLOB:
1503: case java.sql.Types.LONGVARBINARY:
1504: if (pi.value == null) {
1505: len = 0;
1506: } else {
1507: len = pi.length;
1508: }
1509:
1510: if (connection.getTdsVersion() < Driver.TDS70) {
1511: if (len <= VAR_MAX) {
1512: pi.tdsType = SYBVARBINARY;
1513: pi.sqlType = "varbinary(255)";
1514: } else {
1515: if (connection.getSybaseInfo(TdsCore.SYB_LONGDATA)) {
1516: if (len > SYB_LONGVAR_MAX) {
1517: // Need to use special Sybase long binary type
1518: pi.tdsType = SYBLONGDATA;
1519: pi.sqlType = "image";
1520: } else {
1521: // Sybase long binary that can be used as a SP parameter
1522: pi.tdsType = SYBLONGBINARY;
1523: pi.sqlType = "varbinary(" + len + ')';
1524: }
1525: } else {
1526: // Sybase < 12.5 or SQL Server 6.5
1527: pi.tdsType = SYBIMAGE;
1528: pi.sqlType = "image";
1529: }
1530: }
1531: } else {
1532: if (len <= MS_LONGVAR_MAX) {
1533: pi.tdsType = XSYBVARBINARY;
1534: pi.sqlType = "varbinary(8000)";
1535: } else {
1536: if (pi.isOutput) {
1537: throw new SQLException(Messages
1538: .get("error.textoutparam"), "HY000");
1539: }
1540:
1541: pi.tdsType = SYBIMAGE;
1542: pi.sqlType = "image";
1543: }
1544: }
1545:
1546: break;
1547:
1548: case java.sql.Types.BIGINT:
1549: if (connection.getTdsVersion() >= Driver.TDS80
1550: || connection.getSybaseInfo(TdsCore.SYB_BIGINT)) {
1551: pi.tdsType = SYBINTN;
1552: pi.sqlType = "bigint";
1553: } else {
1554: // int8 not supported send as a decimal field
1555: pi.tdsType = SYBDECIMAL;
1556: pi.sqlType = "decimal(" + connection.getMaxPrecision()
1557: + ')';
1558: pi.scale = 0;
1559: }
1560:
1561: break;
1562:
1563: case java.sql.Types.DECIMAL:
1564: case java.sql.Types.NUMERIC:
1565: pi.tdsType = SYBDECIMAL;
1566: int prec = connection.getMaxPrecision();
1567: int scale = DEFAULT_SCALE;
1568: if (pi.value instanceof BigDecimal) {
1569: scale = ((BigDecimal) pi.value).scale();
1570: } else if (pi.scale >= 0 && pi.scale <= prec) {
1571: scale = pi.scale;
1572: }
1573: pi.sqlType = "decimal(" + prec + ',' + scale + ')';
1574:
1575: break;
1576:
1577: case java.sql.Types.OTHER:
1578: case java.sql.Types.NULL:
1579: // Send a null String in the absence of anything better
1580: pi.tdsType = SYBVARCHAR;
1581: pi.sqlType = "varchar(255)";
1582: break;
1583:
1584: default:
1585: throw new SQLException(Messages.get("error.baddatatype",
1586: Integer.toString(pi.jdbcType)), "HY000");
1587: }
1588: }
1589:
1590: /**
1591: * Calculate the size of the parameter descriptor array for TDS 5 packets.
1592: *
1593: * @param charset The encoding character set.
1594: * @param isWideChar True if multi byte encoding.
1595: * @param pi The parameter to describe.
1596: * @param useParamNames True if named parameters should be used.
1597: * @return The size of the parameter descriptor as an <code>int</code>.
1598: */
1599: static int getTds5ParamSize(String charset, boolean isWideChar,
1600: ParamInfo pi, boolean useParamNames) {
1601: int size = 8;
1602: if (pi.name != null && useParamNames) {
1603: // Size of parameter name
1604: if (isWideChar) {
1605: byte[] buf = Support.encodeString(charset, pi.name);
1606:
1607: size += buf.length;
1608: } else {
1609: size += pi.name.length();
1610: }
1611: }
1612:
1613: switch (pi.tdsType) {
1614: case SYBVARCHAR:
1615: case SYBVARBINARY:
1616: case SYBINTN:
1617: case SYBFLTN:
1618: case SYBDATETIMN:
1619: case SYBDATEN:
1620: case SYBTIMEN:
1621: size += 1;
1622: break;
1623: case SYBDECIMAL:
1624: case SYBLONGDATA:
1625: size += 3;
1626: break;
1627: case XSYBCHAR:
1628: case SYBLONGBINARY:
1629: size += 4;
1630: break;
1631: case SYBBIT:
1632: break;
1633: default:
1634: throw new IllegalStateException(
1635: "Unsupported output TDS type 0x"
1636: + Integer.toHexString(pi.tdsType));
1637: }
1638:
1639: return size;
1640: }
1641:
1642: /**
1643: * Write a TDS 5 parameter format descriptor.
1644: *
1645: * @param out The server RequestStream.
1646: * @param charset The encoding character set.
1647: * @param isWideChar True if multi byte encoding.
1648: * @param pi The parameter to describe.
1649: * @param useParamNames True if named parameters should be used.
1650: * @throws IOException
1651: */
1652: static void writeTds5ParamFmt(RequestStream out, String charset,
1653: boolean isWideChar, ParamInfo pi, boolean useParamNames)
1654: throws IOException {
1655: if (pi.name != null && useParamNames) {
1656: // Output parameter name.
1657: if (isWideChar) {
1658: byte[] buf = Support.encodeString(charset, pi.name);
1659:
1660: out.write((byte) buf.length);
1661: out.write(buf);
1662: } else {
1663: out.write((byte) pi.name.length());
1664: out.write(pi.name);
1665: }
1666: } else {
1667: out.write((byte) 0);
1668: }
1669:
1670: out.write((byte) (pi.isOutput ? 1 : 0)); // Output param
1671: if (pi.sqlType.startsWith("univarchar")) {
1672: out.write((int) UDT_UNIVARCHAR);
1673: } else if ("unitext".equals(pi.sqlType)) {
1674: out.write((int) UDT_UNITEXT);
1675: } else {
1676: out.write((int) 0); // user type
1677: }
1678: out.write((byte) pi.tdsType); // TDS data type token
1679:
1680: // Output length fields
1681: switch (pi.tdsType) {
1682: case SYBVARCHAR:
1683: case SYBVARBINARY:
1684: out.write((byte) VAR_MAX);
1685: break;
1686: case XSYBCHAR:
1687: out.write((int) 0x7FFFFFFF);
1688: break;
1689: case SYBLONGDATA:
1690: // It appears that type 3 = send text data
1691: // and type 4 = send image or unitext data
1692: // No idea if there is a type 1/2 or what they are.
1693: out.write("text".equals(pi.sqlType) ? (byte) 3 : (byte) 4);
1694: out.write((byte) 0);
1695: out.write((byte) 0);
1696: break;
1697: case SYBLONGBINARY:
1698: out.write((int) 0x7FFFFFFF);
1699: break;
1700: case SYBBIT:
1701: break;
1702: case SYBINTN:
1703: out
1704: .write("bigint".equals(pi.sqlType) ? (byte) 8
1705: : (byte) 4);
1706: break;
1707: case SYBFLTN:
1708: if (pi.value instanceof Float) {
1709: out.write((byte) 4);
1710: } else {
1711: out.write((byte) 8);
1712: }
1713: break;
1714: case SYBDATETIMN:
1715: out.write((byte) 8);
1716: break;
1717: case SYBDATEN:
1718: case SYBTIMEN:
1719: out.write((byte) 4);
1720: break;
1721: case SYBDECIMAL:
1722: out.write((byte) 17);
1723: out.write((byte) 38);
1724:
1725: if (pi.jdbcType == java.sql.Types.BIGINT) {
1726: out.write((byte) 0);
1727: } else {
1728: if (pi.value instanceof BigDecimal) {
1729: out.write((byte) ((BigDecimal) pi.value).scale());
1730: } else {
1731: if (pi.scale >= 0
1732: && pi.scale <= TdsData.DEFAULT_PRECISION_38) {
1733: out.write((byte) pi.scale);
1734: } else {
1735: out.write((byte) DEFAULT_SCALE);
1736: }
1737: }
1738: }
1739:
1740: break;
1741: default:
1742: throw new IllegalStateException(
1743: "Unsupported output TDS type "
1744: + Integer.toHexString(pi.tdsType));
1745: }
1746:
1747: out.write((byte) 0); // Locale information
1748: }
1749:
1750: /**
1751: * Write the actual TDS 5 parameter data.
1752: *
1753: * @param out the server RequestStream
1754: * @param charsetInfo the encoding character set
1755: * @param pi the parameter to output
1756: * @throws IOException
1757: * @throws SQLException
1758: */
1759: static void writeTds5Param(RequestStream out,
1760: CharsetInfo charsetInfo, ParamInfo pi) throws IOException,
1761: SQLException {
1762:
1763: if (pi.charsetInfo == null) {
1764: pi.charsetInfo = charsetInfo;
1765: }
1766: switch (pi.tdsType) {
1767:
1768: case SYBVARCHAR:
1769: if (pi.value == null) {
1770: out.write((byte) 0);
1771: } else {
1772: byte buf[] = pi.getBytes(pi.charsetInfo.getCharset());
1773:
1774: if (buf.length == 0) {
1775: buf = new byte[1];
1776: buf[0] = ' ';
1777: }
1778:
1779: if (buf.length > VAR_MAX) {
1780: throw new SQLException(Messages
1781: .get("error.generic.truncmbcs"), "HY000");
1782: }
1783:
1784: out.write((byte) buf.length);
1785: out.write(buf);
1786: }
1787:
1788: break;
1789:
1790: case SYBVARBINARY:
1791: if (pi.value == null) {
1792: out.write((byte) 0);
1793: } else {
1794: byte buf[] = pi.getBytes(pi.charsetInfo.getCharset());
1795: if (out.getTdsVersion() < Driver.TDS70
1796: && buf.length == 0) {
1797: // Sybase and SQL 6.5 do not allow zero length binary
1798: out.write((byte) 1);
1799: out.write((byte) 0);
1800: } else {
1801: out.write((byte) buf.length);
1802: out.write(buf);
1803: }
1804: }
1805:
1806: break;
1807:
1808: case XSYBCHAR:
1809: if (pi.value == null) {
1810: out.write((byte) 0);
1811: } else {
1812: byte buf[] = pi.getBytes(pi.charsetInfo.getCharset());
1813:
1814: if (buf.length == 0) {
1815: buf = new byte[1];
1816: buf[0] = ' ';
1817: }
1818: out.write((int) buf.length);
1819: out.write(buf);
1820: }
1821: break;
1822:
1823: case SYBLONGDATA:
1824: //
1825: // Write a three byte prefix usage unknown
1826: //
1827: out.write((byte) 0);
1828: out.write((byte) 0);
1829: out.write((byte) 0);
1830: //
1831: // Write BLOB direct from input stream
1832: //
1833: if (pi.value instanceof InputStream) {
1834: byte buffer[] = new byte[SYB_CHUNK_SIZE];
1835: int len = ((InputStream) pi.value).read(buffer);
1836: while (len > 0) {
1837: out.write((byte) len);
1838: out.write((byte) (len >> 8));
1839: out.write((byte) (len >> 16));
1840: out.write((byte) ((len >> 24) | 0x80)); // 0x80 means more to come
1841: out.write(buffer, 0, len);
1842: len = ((InputStream) pi.value).read(buffer);
1843: }
1844: } else
1845: //
1846: // Write CLOB direct from input Reader
1847: //
1848: if (pi.value instanceof Reader
1849: && !pi.charsetInfo.isWideChars()) {
1850: // For ASE 15+ the getNativeType() routine will already have
1851: // read the data from the reader so this code will not be
1852: // reached unless sendStringParametersAsUnicode=false.
1853: char buffer[] = new char[SYB_CHUNK_SIZE];
1854: int len = ((Reader) pi.value).read(buffer);
1855: while (len > 0) {
1856: out.write((byte) len);
1857: out.write((byte) (len >> 8));
1858: out.write((byte) (len >> 16));
1859: out.write((byte) ((len >> 24) | 0x80)); // 0x80 means more to come
1860: out.write(Support.encodeString(pi.charsetInfo
1861: .getCharset(), new String(buffer, 0, len)));
1862: len = ((Reader) pi.value).read(buffer);
1863: }
1864: } else
1865: //
1866: // Write data from memory buffer
1867: //
1868: if (pi.value != null) {
1869: //
1870: // Actual data needs to be written out in chunks of
1871: // 8192 bytes.
1872: //
1873: if ("unitext".equals(pi.sqlType)) {
1874: // Write out String as unicode bytes
1875: String buf = pi.getString(pi.charsetInfo
1876: .getCharset());
1877: int pos = 0;
1878: while (pos < buf.length()) {
1879: int clen = (buf.length() - pos >= SYB_CHUNK_SIZE / 2) ? SYB_CHUNK_SIZE / 2
1880: : buf.length() - pos;
1881: int len = clen * 2;
1882: out.write((byte) len);
1883: out.write((byte) (len >> 8));
1884: out.write((byte) (len >> 16));
1885: out.write((byte) ((len >> 24) | 0x80)); // 0x80 means more to come
1886: // Write data
1887: out.write(buf.substring(pos, pos + clen)
1888: .toCharArray(), 0, clen);
1889: pos += clen;
1890: }
1891: } else {
1892: // Write text as bytes
1893: byte buf[] = pi.getBytes(pi.charsetInfo
1894: .getCharset());
1895: int pos = 0;
1896: while (pos < buf.length) {
1897: int len = (buf.length - pos >= SYB_CHUNK_SIZE) ? SYB_CHUNK_SIZE
1898: : buf.length - pos;
1899: out.write((byte) len);
1900: out.write((byte) (len >> 8));
1901: out.write((byte) (len >> 16));
1902: out.write((byte) ((len >> 24) | 0x80)); // 0x80 means more to come
1903: // Write data
1904: for (int i = 0; i < len; i++) {
1905: out.write(buf[pos++]);
1906: }
1907: }
1908: }
1909: }
1910: // Write terminator
1911: out.write((int) 0);
1912: break;
1913:
1914: case SYBLONGBINARY:
1915: // Sybase data <= 16284 bytes long
1916: if (pi.value == null) {
1917: out.write((int) 0);
1918: } else {
1919: if (pi.sqlType.startsWith("univarchar")) {
1920: String tmp = pi.getString(pi.charsetInfo
1921: .getCharset());
1922: if (tmp.length() == 0) {
1923: tmp = " ";
1924: }
1925: out.write((int) tmp.length() * 2);
1926: out.write(tmp.toCharArray(), 0, tmp.length());
1927: } else {
1928: byte buf[] = pi.getBytes(pi.charsetInfo
1929: .getCharset());
1930: if (buf.length > 0) {
1931: out.write((int) buf.length);
1932: out.write(buf);
1933: } else {
1934: out.write((int) 1);
1935: out.write((byte) 0);
1936: }
1937: }
1938: }
1939: break;
1940:
1941: case SYBINTN:
1942: if (pi.value == null) {
1943: out.write((byte) 0);
1944: } else {
1945: if ("bigint".equals(pi.sqlType)) {
1946: out.write((byte) 8);
1947: out.write((long) ((Number) pi.value).longValue());
1948: } else {
1949: out.write((byte) 4);
1950: out.write((int) ((Number) pi.value).intValue());
1951: }
1952: }
1953:
1954: break;
1955:
1956: case SYBFLTN:
1957: if (pi.value == null) {
1958: out.write((byte) 0);
1959: } else {
1960: if (pi.value instanceof Float) {
1961: out.write((byte) 4);
1962: out.write(((Number) pi.value).floatValue());
1963: } else {
1964: out.write((byte) 8);
1965: out.write(((Number) pi.value).doubleValue());
1966: }
1967: }
1968:
1969: break;
1970:
1971: case SYBDATETIMN:
1972: putDateTimeValue(out, (DateTime) pi.value);
1973: break;
1974:
1975: case SYBDATEN:
1976: if (pi.value == null) {
1977: out.write((byte) 0);
1978: } else {
1979: out.write((byte) 4);
1980: out.write((int) ((DateTime) pi.value).getDate());
1981: }
1982: break;
1983:
1984: case SYBTIMEN:
1985: if (pi.value == null) {
1986: out.write((byte) 0);
1987: } else {
1988: out.write((byte) 4);
1989: out.write((int) ((DateTime) pi.value).getTime());
1990: }
1991: break;
1992:
1993: case SYBBIT:
1994: if (pi.value == null) {
1995: out.write((byte) 0);
1996: } else {
1997: out
1998: .write((byte) (((Boolean) pi.value)
1999: .booleanValue() ? 1 : 0));
2000: }
2001:
2002: break;
2003:
2004: case SYBNUMERIC:
2005: case SYBDECIMAL:
2006: BigDecimal value = null;
2007:
2008: if (pi.value != null) {
2009: if (pi.value instanceof Long) {
2010: // Long to BigDecimal conversion is buggy. It's actually
2011: // long to double to BigDecimal.
2012: value = new BigDecimal(pi.value.toString());
2013: } else {
2014: value = (BigDecimal) pi.value;
2015: }
2016: }
2017:
2018: out.write(value);
2019: break;
2020:
2021: default:
2022: throw new IllegalStateException(
2023: "Unsupported output TDS type "
2024: + Integer.toHexString(pi.tdsType));
2025: }
2026: }
2027:
2028: /**
2029: * TDS 8 requires collation information for char data descriptors.
2030: *
2031: * @param out The Server request stream.
2032: * @param pi The parameter descriptor.
2033: * @throws IOException
2034: */
2035: static void putCollation(RequestStream out, ParamInfo pi)
2036: throws IOException {
2037: //
2038: // For TDS 8 write a collation string
2039: // I am assuming this can be all zero for now if none is known
2040: //
2041: if (types[pi.tdsType].isCollation) {
2042: if (pi.collation != null) {
2043: out.write(pi.collation);
2044: } else {
2045: byte collation[] = { 0x00, 0x00, 0x00, 0x00, 0x00 };
2046:
2047: out.write(collation);
2048: }
2049: }
2050: }
2051:
2052: /**
2053: * Write a parameter to the server request stream.
2054: *
2055: * @param out the server request stream
2056: * @param charsetInfo the default character set
2057: * @param collation the default SQL Server 2000 collation
2058: * @param pi the parameter descriptor
2059: */
2060: static void writeParam(RequestStream out, CharsetInfo charsetInfo,
2061: byte[] collation, ParamInfo pi) throws IOException {
2062: int len;
2063: String tmp;
2064: byte[] buf;
2065: boolean isTds8 = out.getTdsVersion() >= Driver.TDS80;
2066:
2067: if (isTds8) {
2068: if (pi.collation == null) {
2069: pi.collation = collation;
2070: }
2071: }
2072: if (pi.charsetInfo == null) {
2073: pi.charsetInfo = charsetInfo;
2074: }
2075:
2076: switch (pi.tdsType) {
2077:
2078: case XSYBVARCHAR:
2079: if (pi.value == null) {
2080: out.write((byte) pi.tdsType);
2081: out.write((short) MS_LONGVAR_MAX);
2082:
2083: if (isTds8) {
2084: putCollation(out, pi);
2085: }
2086:
2087: out.write((short) 0xFFFF);
2088: } else {
2089: buf = pi.getBytes(pi.charsetInfo.getCharset());
2090:
2091: if (buf.length > MS_LONGVAR_MAX) {
2092: out.write((byte) SYBTEXT);
2093: out.write((int) buf.length);
2094:
2095: if (isTds8) {
2096: putCollation(out, pi);
2097: }
2098:
2099: out.write((int) buf.length);
2100: out.write(buf);
2101: } else {
2102: out.write((byte) pi.tdsType);
2103: out.write((short) MS_LONGVAR_MAX);
2104:
2105: if (isTds8) {
2106: putCollation(out, pi);
2107: }
2108:
2109: out.write((short) buf.length);
2110: out.write(buf);
2111: }
2112: }
2113:
2114: break;
2115:
2116: case SYBVARCHAR:
2117: if (pi.value == null) {
2118: out.write((byte) pi.tdsType);
2119: out.write((byte) VAR_MAX);
2120: out.write((byte) 0);
2121: } else {
2122: buf = pi.getBytes(pi.charsetInfo.getCharset());
2123:
2124: if (buf.length > VAR_MAX) {
2125: if (buf.length <= MS_LONGVAR_MAX
2126: && out.getTdsVersion() >= Driver.TDS70) {
2127: out.write((byte) XSYBVARCHAR);
2128: out.write((short) MS_LONGVAR_MAX);
2129:
2130: if (isTds8) {
2131: putCollation(out, pi);
2132: }
2133:
2134: out.write((short) buf.length);
2135: out.write(buf);
2136: } else {
2137: out.write((byte) SYBTEXT);
2138: out.write((int) buf.length);
2139:
2140: if (isTds8) {
2141: putCollation(out, pi);
2142: }
2143:
2144: out.write((int) buf.length);
2145: out.write(buf);
2146: }
2147: } else {
2148: if (buf.length == 0) {
2149: buf = new byte[1];
2150: buf[0] = ' ';
2151: }
2152:
2153: out.write((byte) pi.tdsType);
2154: out.write((byte) VAR_MAX);
2155: out.write((byte) buf.length);
2156: out.write(buf);
2157: }
2158: }
2159:
2160: break;
2161:
2162: case XSYBNVARCHAR:
2163: out.write((byte) pi.tdsType);
2164: out.write((short) MS_LONGVAR_MAX);
2165:
2166: if (isTds8) {
2167: putCollation(out, pi);
2168: }
2169:
2170: if (pi.value == null) {
2171: out.write((short) 0xFFFF);
2172: } else {
2173: tmp = pi.getString(pi.charsetInfo.getCharset());
2174: out.write((short) (tmp.length() * 2));
2175: out.write(tmp);
2176: }
2177:
2178: break;
2179:
2180: case SYBTEXT:
2181: if (pi.value == null) {
2182: len = 0;
2183: } else {
2184: len = pi.length;
2185:
2186: if (len == 0 && out.getTdsVersion() < Driver.TDS70) {
2187: pi.value = " ";
2188: len = 1;
2189: }
2190: }
2191:
2192: out.write((byte) pi.tdsType);
2193:
2194: if (len > 0) {
2195: if (pi.value instanceof InputStream) {
2196: // Write output directly from stream
2197: out.write((int) len);
2198:
2199: if (isTds8) {
2200: putCollation(out, pi);
2201: }
2202:
2203: out.write((int) len);
2204: out.writeStreamBytes((InputStream) pi.value, len);
2205: } else if (pi.value instanceof Reader
2206: && !pi.charsetInfo.isWideChars()) {
2207: // Write output directly from stream with character translation
2208: out.write((int) len);
2209:
2210: if (isTds8) {
2211: putCollation(out, pi);
2212: }
2213:
2214: out.write((int) len);
2215: out.writeReaderBytes((Reader) pi.value, len);
2216: } else {
2217: buf = pi.getBytes(pi.charsetInfo.getCharset());
2218: out.write((int) buf.length);
2219:
2220: if (isTds8) {
2221: putCollation(out, pi);
2222: }
2223:
2224: out.write((int) buf.length);
2225: out.write(buf);
2226: }
2227: } else {
2228: out.write((int) len); // Zero length
2229:
2230: if (isTds8) {
2231: putCollation(out, pi);
2232: }
2233:
2234: out.write((int) len);
2235: }
2236:
2237: break;
2238:
2239: case SYBNTEXT:
2240: if (pi.value == null) {
2241: len = 0;
2242: } else {
2243: len = pi.length;
2244: }
2245:
2246: out.write((byte) pi.tdsType);
2247:
2248: if (len > 0) {
2249: if (pi.value instanceof Reader) {
2250: out.write((int) len);
2251:
2252: if (isTds8) {
2253: putCollation(out, pi);
2254: }
2255:
2256: out.write((int) len * 2);
2257: out.writeReaderChars((Reader) pi.value, len);
2258: } else if (pi.value instanceof InputStream
2259: && !pi.charsetInfo.isWideChars()) {
2260: out.write((int) len);
2261:
2262: if (isTds8) {
2263: putCollation(out, pi);
2264: }
2265:
2266: out.write((int) len * 2);
2267: out.writeReaderChars(new InputStreamReader(
2268: (InputStream) pi.value, pi.charsetInfo
2269: .getCharset()), len);
2270: } else {
2271: tmp = pi.getString(pi.charsetInfo.getCharset());
2272: len = tmp.length();
2273: out.write((int) len);
2274:
2275: if (isTds8) {
2276: putCollation(out, pi);
2277: }
2278:
2279: out.write((int) len * 2);
2280: out.write(tmp);
2281: }
2282: } else {
2283: out.write((int) len);
2284:
2285: if (isTds8) {
2286: putCollation(out, pi);
2287: }
2288:
2289: out.write((int) len);
2290: }
2291:
2292: break;
2293:
2294: case XSYBVARBINARY:
2295: out.write((byte) pi.tdsType);
2296: out.write((short) MS_LONGVAR_MAX);
2297:
2298: if (pi.value == null) {
2299: out.write((short) 0xFFFF);
2300: } else {
2301: buf = pi.getBytes(pi.charsetInfo.getCharset());
2302: out.write((short) buf.length);
2303: out.write(buf);
2304: }
2305:
2306: break;
2307:
2308: case SYBVARBINARY:
2309: out.write((byte) pi.tdsType);
2310: out.write((byte) VAR_MAX);
2311:
2312: if (pi.value == null) {
2313: out.write((byte) 0);
2314: } else {
2315: buf = pi.getBytes(pi.charsetInfo.getCharset());
2316: if (out.getTdsVersion() < Driver.TDS70
2317: && buf.length == 0) {
2318: // Sybase and SQL 6.5 do not allow zero length binary
2319: out.write((byte) 1);
2320: out.write((byte) 0);
2321: } else {
2322: out.write((byte) buf.length);
2323: out.write(buf);
2324: }
2325: }
2326:
2327: break;
2328:
2329: case SYBIMAGE:
2330: if (pi.value == null) {
2331: len = 0;
2332: } else {
2333: len = pi.length;
2334: }
2335:
2336: out.write((byte) pi.tdsType);
2337:
2338: if (len > 0) {
2339: if (pi.value instanceof InputStream) {
2340: out.write((int) len);
2341: out.write((int) len);
2342: out.writeStreamBytes((InputStream) pi.value, len);
2343: } else {
2344: buf = pi.getBytes(pi.charsetInfo.getCharset());
2345: out.write((int) buf.length);
2346: out.write((int) buf.length);
2347: out.write(buf);
2348: }
2349: } else {
2350: if (out.getTdsVersion() < Driver.TDS70) {
2351: // Sybase and SQL 6.5 do not allow zero length binary
2352: out.write((int) 1);
2353: out.write((int) 1);
2354: out.write((byte) 0);
2355: } else {
2356: out.write((int) len);
2357: out.write((int) len);
2358: }
2359: }
2360:
2361: break;
2362:
2363: case SYBINTN:
2364: out.write((byte) pi.tdsType);
2365:
2366: if (pi.value == null) {
2367: out.write(("bigint".equals(pi.sqlType)) ? (byte) 8
2368: : (byte) 4);
2369: out.write((byte) 0);
2370: } else {
2371: if ("bigint".equals(pi.sqlType)) {
2372: out.write((byte) 8);
2373: out.write((byte) 8);
2374: out.write((long) ((Number) pi.value).longValue());
2375: } else {
2376: out.write((byte) 4);
2377: out.write((byte) 4);
2378: out.write((int) ((Number) pi.value).intValue());
2379: }
2380: }
2381:
2382: break;
2383:
2384: case SYBFLTN:
2385: out.write((byte) pi.tdsType);
2386: if (pi.value instanceof Float) {
2387: out.write((byte) 4);
2388: out.write((byte) 4);
2389: out.write(((Number) pi.value).floatValue());
2390: } else {
2391: out.write((byte) 8);
2392: if (pi.value == null) {
2393: out.write((byte) 0);
2394: } else {
2395: out.write((byte) 8);
2396: out.write(((Number) pi.value).doubleValue());
2397: }
2398: }
2399:
2400: break;
2401:
2402: case SYBDATETIMN:
2403: out.write((byte) SYBDATETIMN);
2404: out.write((byte) 8);
2405: putDateTimeValue(out, (DateTime) pi.value);
2406: break;
2407:
2408: case SYBBIT:
2409: out.write((byte) pi.tdsType);
2410:
2411: if (pi.value == null) {
2412: out.write((byte) 0);
2413: } else {
2414: out
2415: .write((byte) (((Boolean) pi.value)
2416: .booleanValue() ? 1 : 0));
2417: }
2418:
2419: break;
2420:
2421: case SYBBITN:
2422: out.write((byte) SYBBITN);
2423: out.write((byte) 1);
2424:
2425: if (pi.value == null) {
2426: out.write((byte) 0);
2427: } else {
2428: out.write((byte) 1);
2429: out
2430: .write((byte) (((Boolean) pi.value)
2431: .booleanValue() ? 1 : 0));
2432: }
2433:
2434: break;
2435:
2436: case SYBNUMERIC:
2437: case SYBDECIMAL:
2438: out.write((byte) pi.tdsType);
2439: BigDecimal value = null;
2440: int prec = out.getMaxPrecision();
2441: int scale;
2442:
2443: if (pi.value == null) {
2444: if (pi.jdbcType == java.sql.Types.BIGINT) {
2445: scale = 0;
2446: } else {
2447: if (pi.scale >= 0 && pi.scale <= prec) {
2448: scale = pi.scale;
2449: } else {
2450: scale = DEFAULT_SCALE;
2451: }
2452: }
2453: } else {
2454: if (pi.value instanceof Long) {
2455: value = new BigDecimal(((Long) pi.value).toString());
2456: scale = 0;
2457: } else {
2458: value = (BigDecimal) pi.value;
2459: scale = value.scale();
2460: }
2461: }
2462:
2463: out.write((byte) out.getMaxDecimalBytes());
2464: out.write((byte) prec);
2465: out.write((byte) scale);
2466: out.write(value);
2467: break;
2468:
2469: default:
2470: throw new IllegalStateException(
2471: "Unsupported output TDS type "
2472: + Integer.toHexString(pi.tdsType));
2473: }
2474: }
2475:
2476: //
2477: // ---------------------- Private methods from here -----------------------
2478: //
2479:
2480: /**
2481: * Private constructor to prevent users creating an
2482: * actual instance of this class.
2483: */
2484: private TdsData() {
2485: }
2486:
2487: /**
2488: * Get a DATETIME value from the server response stream.
2489: *
2490: * @param in The server response stream.
2491: * @param type The TDS data type.
2492: * @return The java.sql.Timestamp value or null.
2493: * @throws java.io.IOException
2494: */
2495: private static Object getDatetimeValue(ResponseStream in,
2496: final int type) throws IOException, ProtocolException {
2497: int len;
2498: int daysSince1900;
2499: int time;
2500: int minutes;
2501:
2502: if (type == SYBDATETIMN) {
2503: len = in.read(); // No need to & with 0xff
2504: } else if (type == SYBDATETIME4) {
2505: len = 4;
2506: } else {
2507: len = 8;
2508: }
2509:
2510: switch (len) {
2511: case 0:
2512: return null;
2513:
2514: case 8:
2515: // A datetime is made of of two 32 bit integers
2516: // The first one is the number of days since 1900
2517: // The second integer is the number of seconds*300
2518: // Negative days indicate dates earlier than 1900.
2519: // The full range is 1753-01-01 to 9999-12-31.
2520: daysSince1900 = in.readInt();
2521: time = in.readInt();
2522: return new DateTime(daysSince1900, time);
2523: case 4:
2524: // A smalldatetime is two 16 bit integers.
2525: // The first is the number of days past January 1, 1900,
2526: // the second smallint is the number of minutes past
2527: // midnight.
2528: // The full range is 1900-01-01 to 2079-06-06.
2529: daysSince1900 = ((int) in.readShort()) & 0xFFFF;
2530: minutes = in.readShort();
2531: return new DateTime((short) daysSince1900, (short) minutes);
2532: default:
2533: throw new ProtocolException(
2534: "Invalid DATETIME value with size of " + len
2535: + " bytes.");
2536: }
2537: }
2538:
2539: /**
2540: * Output a java.sql.Date/Time/Timestamp value to the server
2541: * as a Sybase datetime value.
2542: *
2543: * @param out the server request stream
2544: * @param value the date value to write
2545: */
2546: private static void putDateTimeValue(RequestStream out,
2547: DateTime value) throws IOException {
2548: if (value == null) {
2549: out.write((byte) 0);
2550: return;
2551: }
2552: out.write((byte) 8);
2553: out.write((int) value.getDate());
2554: out.write((int) value.getTime());
2555: }
2556:
2557: /**
2558: * Read a MONEY value from the server response stream.
2559: *
2560: * @param in The server response stream.
2561: * @param type The TDS data type.
2562: * @return The java.math.BigDecimal value or null.
2563: * @throws IOException
2564: * @throws ProtocolException
2565: */
2566: private static Object getMoneyValue(ResponseStream in,
2567: final int type) throws IOException, ProtocolException {
2568: final int len;
2569:
2570: if (type == SYBMONEY) {
2571: len = 8;
2572: } else if (type == SYBMONEYN) {
2573: len = in.read();
2574: } else {
2575: len = 4;
2576: }
2577:
2578: BigInteger x = null;
2579:
2580: if (len == 4) {
2581: x = BigInteger.valueOf(in.readInt());
2582: } else if (len == 8) {
2583: final byte b4 = (byte) in.read();
2584: final byte b5 = (byte) in.read();
2585: final byte b6 = (byte) in.read();
2586: final byte b7 = (byte) in.read();
2587: final byte b0 = (byte) in.read();
2588: final byte b1 = (byte) in.read();
2589: final byte b2 = (byte) in.read();
2590: final byte b3 = (byte) in.read();
2591: final long l = (long) (b0 & 0xff)
2592: + ((long) (b1 & 0xff) << 8)
2593: + ((long) (b2 & 0xff) << 16)
2594: + ((long) (b3 & 0xff) << 24)
2595: + ((long) (b4 & 0xff) << 32)
2596: + ((long) (b5 & 0xff) << 40)
2597: + ((long) (b6 & 0xff) << 48)
2598: + ((long) (b7 & 0xff) << 56);
2599:
2600: x = BigInteger.valueOf(l);
2601: } else if (len != 0) {
2602: throw new ProtocolException("Invalid money value.");
2603: }
2604:
2605: return (x == null) ? null : new BigDecimal(x, 4);
2606: }
2607:
2608: /**
2609: * Read a MSQL 2000 sql_variant data value from the input stream.
2610: * <p>SQL_VARIANT has the following structure:
2611: * <ol>
2612: * <li>INT4 total size of data
2613: * <li>INT1 TDS data type (text/image/ntext/sql_variant not allowed)
2614: * <li>INT1 Length of extra type descriptor information
2615: * <li>Optional additional type info required by some types
2616: * <li>byte[0...n] the actual data
2617: * </ol>
2618: *
2619: * @param connection used to obtain collation/charset information
2620: * @param in the server response stream
2621: * @return the SQL_VARIANT data
2622: */
2623: private static Object getVariant(ConnectionJDBC2 connection,
2624: ResponseStream in) throws IOException, ProtocolException {
2625: byte[] bytes;
2626: int len = in.readInt();
2627:
2628: if (len == 0) {
2629: // Length of zero means item is null
2630: return null;
2631: }
2632:
2633: ColInfo ci = new ColInfo();
2634: len -= 2;
2635: ci.tdsType = in.read(); // TDS Type
2636: len -= in.read(); // Size of descriptor
2637:
2638: switch (ci.tdsType) {
2639: case SYBINT1:
2640: return new Integer(in.read() & 0xFF);
2641:
2642: case SYBINT2:
2643: return new Integer(in.readShort());
2644:
2645: case SYBINT4:
2646: return new Integer(in.readInt());
2647:
2648: case SYBINT8:
2649: return new Long(in.readLong());
2650:
2651: case XSYBCHAR:
2652: case XSYBVARCHAR:
2653: // FIXME Use collation for reading
2654: getCollation(in, ci);
2655: try {
2656: setColumnCharset(ci, connection);
2657: } catch (SQLException ex) {
2658: // Skip the buffer size and value
2659: in.skip(2 + len);
2660: throw new ProtocolException(ex.toString()
2661: + " [SQLState: " + ex.getSQLState() + ']');
2662: }
2663:
2664: in.skip(2); // Skip buffer size
2665: return in.readNonUnicodeString(len);
2666:
2667: case XSYBNCHAR:
2668: case XSYBNVARCHAR:
2669: // XXX Why do we need collation for Unicode strings?
2670: in.skip(7); // Skip collation and buffer size
2671:
2672: return in.readUnicodeString(len / 2);
2673:
2674: case XSYBVARBINARY:
2675: case XSYBBINARY:
2676: in.skip(2); // Skip buffer size
2677: bytes = new byte[len];
2678: in.read(bytes);
2679:
2680: return bytes;
2681:
2682: case SYBMONEY4:
2683: case SYBMONEY:
2684: return getMoneyValue(in, ci.tdsType);
2685:
2686: case SYBDATETIME4:
2687: case SYBDATETIME:
2688: return getDatetimeValue(in, ci.tdsType);
2689:
2690: case SYBBIT:
2691: return (in.read() != 0) ? Boolean.TRUE : Boolean.FALSE;
2692:
2693: case SYBREAL:
2694: return new Float(Float.intBitsToFloat(in.readInt()));
2695:
2696: case SYBFLT8:
2697: return new Double(Double.longBitsToDouble(in.readLong()));
2698:
2699: case SYBUNIQUE:
2700: bytes = new byte[len];
2701: in.read(bytes);
2702:
2703: return new UniqueIdentifier(bytes);
2704:
2705: case SYBNUMERIC:
2706: case SYBDECIMAL:
2707: ci.precision = in.read();
2708: ci.scale = in.read();
2709: int sign = in.read();
2710: len--;
2711: bytes = new byte[len];
2712: BigInteger bi;
2713:
2714: while (len-- > 0) {
2715: bytes[len] = (byte) in.read();
2716: }
2717:
2718: bi = new BigInteger((sign == 0) ? -1 : 1, bytes);
2719:
2720: return new BigDecimal(bi, ci.scale);
2721:
2722: default:
2723: throw new ProtocolException("Unsupported TDS data type 0x"
2724: + Integer.toHexString(ci.tdsType)
2725: + " in sql_variant");
2726: }
2727: //
2728: // For compatibility with the MS driver convert to String.
2729: // Change the data type for sql_variant from OTHER to VARCHAR
2730: // Without this code the actual Object type can be retrieved
2731: // by using getObject(n).
2732: //
2733: // try {
2734: // value = Support.convert(value, java.sql.Types.VARCHAR, in.getCharset());
2735: // } catch (SQLException e) {
2736: // // Conversion failed just try toString();
2737: // value = value.toString();
2738: // }
2739: }
2740:
2741: /**
2742: * For SQL 2005 This routine will modify the meta data to allow the
2743: * caller to distinguish between varchar(max) and text or varbinary(max)
2744: * and image or nvarchar(max) and ntext.
2745: *
2746: * @param typeName the SQL type returned by sp_columns
2747: * @param tdsType the TDS type returned by sp_columns
2748: * @return the (possibly) modified SQL type name as a <code>String</code>
2749: */
2750: public static String getMSTypeName(String typeName, int tdsType) {
2751: if (typeName.equalsIgnoreCase("text") && tdsType != SYBTEXT) {
2752: return "varchar";
2753: } else if (typeName.equalsIgnoreCase("ntext")
2754: && tdsType != SYBTEXT) {
2755: return "nvarchar";
2756: } else if (typeName.equalsIgnoreCase("image")
2757: && tdsType != SYBIMAGE) {
2758: return "varbinary";
2759: } else {
2760: return typeName;
2761: }
2762: }
2763:
2764: /**
2765: * Extract the TDS protocol version from the value returned by the server in the LOGINACK
2766: * packet.
2767: *
2768: * @param rawTdsVersion the TDS protocol version as returned by the server
2769: * @return the jTDS internal value for the protocol version (i.e one of the
2770: * <code>Driver.TDS<i>XX</i></code> values)
2771: */
2772: public static int getTdsVersion(int rawTdsVersion) {
2773: if (rawTdsVersion >= 0x71000001) {
2774: return Driver.TDS81;
2775: } else if (rawTdsVersion >= 0x07010000) {
2776: return Driver.TDS80;
2777: } else if (rawTdsVersion >= 0x07000000) {
2778: return Driver.TDS70;
2779: } else if (rawTdsVersion >= 0x05000000) {
2780: return Driver.TDS50;
2781: } else {
2782: return Driver.TDS42;
2783: }
2784: }
2785:
2786: /**
2787: * Establish if a String can be converted to a byte based character set.
2788: *
2789: * @param value The String to test.
2790: * @param charset The server character set in force.
2791: * @return <code>boolean</code> true if string can be converted.
2792: */
2793: private static boolean canEncode(String value, String charset) {
2794: if (value == null) {
2795: return true;
2796: }
2797: if ("UTF-8".equals(charset)) {
2798: // Should be no problem with UTF-8
2799: return true;
2800: }
2801: if ("ISO-8859-1".equals(charset)) {
2802: // ISO_1 = lower byte of unicode
2803: for (int i = value.length() - 1; i >= 0; i--) {
2804: if (value.charAt(i) > 255) {
2805: return false; // Outside range
2806: }
2807: }
2808: return true;
2809: }
2810: if ("ISO-8859-15".equals(charset) || "Cp1252".equals(charset)) {
2811: // These will accept euro symbol
2812: for (int i = value.length() - 1; i >= 0; i--) {
2813: // FIXME This is not correct! Cp1252 also contains other characters.
2814: // No: I think it is OK the point is to ensure that all characters are either
2815: // < 256 in which case the sets are the same or the euro which is convertable.
2816: // Any other combination will cause the string to be sent as unicode.
2817: char c = value.charAt(i);
2818: if (c > 255 && c != 0x20AC) {
2819: return false; // Outside range
2820: }
2821: }
2822: return true;
2823: }
2824: if ("US-ASCII".equals(charset)) {
2825: for (int i = value.length() - 1; i >= 0; i--) {
2826: if (value.charAt(i) > 127) {
2827: return false; // Outside range
2828: }
2829: }
2830: return true;
2831: }
2832: // OK need to do an expensive check
2833: try {
2834: return new String(value.getBytes(charset), charset)
2835: .equals(value);
2836: } catch (UnsupportedEncodingException e) {
2837: return false;
2838: }
2839: }
2840: }
|