0001: //
0002: // Copyright 1998, 1999 CDS Networks, Inc., Medford Oregon
0003: //
0004: // All rights reserved.
0005: //
0006: // Redistribution and use in source and binary forms, with or without
0007: // modification, are permitted provided that the following conditions are met:
0008: // 1. Redistributions of source code must retain the above copyright
0009: // notice, this list of conditions and the following disclaimer.
0010: // 2. Redistributions in binary form must reproduce the above copyright
0011: // notice, this list of conditions and the following disclaimer in the
0012: // documentation and/or other materials provided with the distribution.
0013: // 3. All advertising materials mentioning features or use of this software
0014: // must display the following acknowledgement:
0015: // This product includes software developed by CDS Networks, Inc.
0016: // 4. The name of CDS Networks, Inc. may not be used to endorse or promote
0017: // products derived from this software without specific prior
0018: // written permission.
0019: //
0020: // THIS SOFTWARE IS PROVIDED BY CDS NETWORKS, INC. ``AS IS'' AND
0021: // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0022: // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0023: // ARE DISCLAIMED. IN NO EVENT SHALL CDS NETWORKS, INC. BE LIABLE
0024: // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
0025: // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
0026: // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
0027: // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
0028: // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
0029: // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0030: // SUCH DAMAGE.
0031: //
0032:
0033: package com.internetcds.jdbc.tds;
0034:
0035: import java.net.Socket;
0036: import java.util.Vector;
0037: import java.lang.Thread;
0038: import java.util.StringTokenizer;
0039: import java.sql.*;
0040: import com.internetcds.jdbc.tds.TdsComm;
0041: import com.internetcds.util.Logger;
0042: import java.math.BigInteger;
0043: import java.math.BigDecimal;
0044: import java.util.Calendar;
0045: import java.util.Properties;
0046: import java.util.TimeZone;
0047: import java.util.Locale;
0048:
0049: /**
0050: * Cancel the current SQL if the timeout expires.
0051: *
0052: * @version $Id: Tds.java,v 1.2 2007-10-19 13:21:40 sinisa Exp $
0053: * @author Craig Spannring
0054: */
0055: class TimeoutHandler extends Thread {
0056: public static final String cvsVersion = "$Id: Tds.java,v 1.2 2007-10-19 13:21:40 sinisa Exp $";
0057:
0058: java.sql.Statement stmt;
0059: int timeout;
0060:
0061: public TimeoutHandler(java.sql.Statement stmt_, int timeout_) {
0062: stmt = stmt_;
0063: timeout = timeout_;
0064: }
0065:
0066: public void run() {
0067: try {
0068: sleep(timeout * 1000);
0069: stmt.cancel();
0070: } catch (SQLException e) {
0071: // nop
0072: } catch (java.lang.InterruptedException e) {
0073: // nop
0074: }
0075: }
0076: }
0077:
0078: /**
0079: * Implement the TDS protocol.
0080: *
0081: * @version $Id: Tds.java,v 1.2 2007-10-19 13:21:40 sinisa Exp $
0082: * @author Craig Spannring
0083: * @author Igor Petrovski
0084: * @author The FreeTDS project
0085: */
0086: public class Tds implements TdsDefinitions {
0087: public static final String cvsVersion = "$Id: Tds.java,v 1.2 2007-10-19 13:21:40 sinisa Exp $";
0088:
0089: //
0090: // If the following variable is false we will consider calling
0091: // unimplemented methods to be an error and will raise an exception.
0092: // If you want to ignore any unimplemented methods, set it to
0093: // true. Only do this if you know what you are doing and can tolerate
0094: // bogus results from the unimplemented methods.
0095: //
0096: static boolean ignoreNotImplemented = false;
0097:
0098: Socket sock = null;
0099: TdsComm comm = null;
0100:
0101: String databaseProductName;
0102: String databaseProductVersion;
0103:
0104: java.sql.Connection connection;
0105: String host;
0106: int serverType = -1; // either Tds.SYBASE or Tds.SQLSERVER
0107: int port; // Port numbers are _unsigned_ 16 bit, short is too small
0108: String database;
0109: String user;
0110: String password;
0111: String appName;
0112: String serverName;
0113: String progName;
0114: byte progMajorVersion;
0115: byte progMinorVersion;
0116:
0117: boolean haveProcNameTable = false;
0118: String procNameGeneratorName = null;
0119: String procNameTableName = null;
0120:
0121: String initialSettings = null;
0122:
0123: private Properties initialProps = null;
0124: private EncodingHelper encoder = null;
0125: private String charset = null;
0126:
0127: // int rowCount = -1; // number of rows selected or updated
0128: private boolean moreResults = false; // Is there another result set?
0129:
0130: // Jens Jakobsen 1999-01-10
0131: // Until TDS_END_TOKEN is reached we assume that there are outstanding
0132: // UpdateCounts or ResultSets
0133: private boolean moreResults2 = true;
0134:
0135: CancelController cancelController = null;
0136:
0137: SqlMessage lastServerMessage = null;
0138:
0139: // Added 2000-06-07. Used to control TDS version-specific behavior.
0140: private int tdsVer = Tds.TDS42;
0141:
0142: // RMK 2000-06-08.
0143: private boolean showWarnings = false;
0144:
0145: // RMK 2000-06-12. Time zone offset on client (disregarding DST).
0146: private int zoneOffset = Calendar.getInstance().get(
0147: Calendar.ZONE_OFFSET);
0148:
0149: public Tds(java.sql.Connection connection_, Properties props_,
0150: String initialSettings) throws java.io.IOException,
0151: java.net.UnknownHostException, java.sql.SQLException,
0152: com.internetcds.jdbc.tds.TdsException {
0153: connection = (java.sql.Connection) connection_;
0154: initialProps = props_;
0155:
0156: host = props_.getProperty("HOST");
0157: serverType = Integer.parseInt(props_.getProperty("SERVERTYPE"));
0158: port = Integer.parseInt(props_.getProperty("PORT"));
0159: database = props_.getProperty("DBNAME");
0160: user = props_.getProperty("user");
0161: password = props_.getProperty("password");
0162: appName = props_.getProperty("APPNAME", "jdbclib");
0163: serverName = props_.getProperty("SERVERNAME", host);
0164: progName = props_.getProperty("PROGNAME", "java_app");
0165: progMajorVersion = (byte) DriverVersion.getDriverMajorVersion();
0166: progMinorVersion = (byte) DriverVersion.getDriverMinorVersion();
0167: // String verString = props_.getProperty("TDS", "42");
0168: String verString = props_.getProperty("TDS", "7.0");
0169:
0170: // XXX This driver doesn't properly support TDS 5.0, AFAIK.
0171: // Added 2000-06-07.
0172:
0173: tdsVer = TDS42;
0174: if (verString.equals("5.0")) {
0175: tdsVer = Tds.TDS50;
0176: } else if (verString.equals("7.0")) {
0177: tdsVer = Tds.TDS70;
0178: }
0179: // RMK 2000-06-08
0180: if (System.getProperty("TDS_SHOW_WARNINGS") != null
0181: || props_.getProperty("TDS_SHOW_WARNINGS") != null) {
0182: showWarnings = true;
0183: }
0184:
0185: cancelController = new CancelController();
0186:
0187: // Send the logon packet to the server
0188: sock = new Socket(host, port);
0189: sock.setTcpNoDelay(true);
0190: comm = new TdsComm(sock, tdsVer);
0191:
0192: setCharset(props_.getProperty("CHARSET"));
0193:
0194: if (logon()) {
0195: // everything is hunky-dory.
0196: } else {
0197: throw new SQLException("Logon failed. "
0198: + lastServerMessage);
0199: }
0200: }
0201:
0202: private void setCharset(String charset) {
0203: try {
0204: Logger.println("Trying to change charset to " + charset);
0205: } catch (java.io.IOException e) {
0206: // nop
0207: }
0208:
0209: if (charset == null || charset.length() > 30) {
0210: charset = "iso_1";
0211: }
0212:
0213: if (!charset.equals(this .charset)) {
0214: encoder = EncodingHelper.getHelper(charset);
0215: if (encoder == null) {
0216: charset = "iso_1";
0217: encoder = EncodingHelper.getHelper(charset);
0218: }
0219: this .charset = charset;
0220: }
0221: }
0222:
0223: EncodingHelper getEncoder() {
0224: return encoder;
0225: }
0226:
0227: public void close() {
0228: comm.close();
0229: try {
0230: sock.close();
0231: } catch (java.io.IOException e) {
0232: // XXX should do something here
0233: }
0234: }
0235:
0236: static private int toUInt(byte b) {
0237: int result = ((int) b) & 0x00ff;
0238: return result;
0239: }
0240:
0241: public String toString() {
0242: return "" + database + ", " + sock.getLocalAddress() + ":"
0243: + sock.getLocalPort() + " -> " + sock.getInetAddress()
0244: + ":" + sock.getPort();
0245: }
0246:
0247: /**
0248: * Convert a JDBC escaped SQL string into the native SQL
0249: *
0250: * @param input escaped string to convert
0251: *
0252: * @return native SQL string
0253: */
0254: static public String toNativeSql(String input, int serverType)
0255: throws SQLException {
0256: EscapeProcessor escape;
0257: if (serverType == TdsDefinitions.SYBASE) {
0258: escape = new SybaseEscapeProcessor(input);
0259: } else {
0260: escape = new MSSqlServerEscapeProcessor(input);
0261: }
0262:
0263: return escape.nativeString();
0264: }
0265:
0266: /**
0267: * Convert a JDBC java.sql.Types identifier to a SQLServer type identifier
0268: *
0269: * @author Craig Spannring
0270: *
0271: * @param jdbcType JDBC type to convert. Should be one of the
0272: * constants from java.sql.Types.
0273: *
0274: * @return The corresponding SQLServer type identifier.
0275: */
0276: public static byte cvtJdbcTypeToNativeType(int jdbcType)
0277: throws TdsNotImplemented {
0278: // This function is thread safe.
0279: byte result = 0;
0280: switch (jdbcType) {
0281: case java.sql.Types.CHAR:
0282: // case java.sql.Types.VARCHAR:
0283: // case java.sql.Types.LONGVARCHAR:
0284: {
0285: result = SYBCHAR;
0286: break;
0287: }
0288: //Sinisa
0289: //Add java.sql.types VARCHAR & LONGVARCHAR as a native type SYBVARCHAR
0290: case java.sql.Types.VARCHAR:
0291: case java.sql.Types.LONGVARCHAR: {
0292: result = SYBVARCHAR;
0293: break;
0294: }
0295: //Sinisa
0296: //Add java.sql.types VARCHAR & LONGVARCHAR as a native type SYBFLT8
0297: case java.sql.Types.DECIMAL: {
0298: result = SYBFLT8;
0299: break;
0300: }
0301: case java.sql.Types.INTEGER:
0302: case java.sql.Types.SMALLINT:
0303: case java.sql.Types.BIGINT: {
0304: result = SYBINT4;
0305: break;
0306: }
0307: case java.sql.Types.REAL:
0308: case java.sql.Types.DOUBLE: {
0309: result = SYBFLT8;
0310: break;
0311: }
0312: case java.sql.Types.DATE:
0313: case java.sql.Types.TIMESTAMP:
0314: case java.sql.Types.TIME: {
0315: result = SYBDATETIMN;
0316: break;
0317: }
0318: case java.sql.Types.VARBINARY:
0319: case java.sql.Types.LONGVARBINARY: {
0320: result = SYBIMAGE;
0321: break;
0322: }
0323: //Dusan
0324: case java.sql.Types.BIT: {
0325: result = SYBBIT;
0326: break;
0327: }
0328: default: {
0329: throw new TdsNotImplemented("cvtJdbcTypeToNativeType ("
0330: + TdsUtil.javaSqlTypeToString(jdbcType) + ")");
0331: }
0332: }
0333:
0334: return result;
0335: }
0336:
0337: /**
0338: * Convert a JDBC java.sql.Types identifier to a
0339: * SQLServer type identifier
0340: *
0341: * @author Craig Spannring
0342: *
0343: * @param nativeType SQLServer type to convert.
0344: * @param size Maximum size of data coming back from server.
0345: *
0346: * @return The corresponding JDBC type identifier.
0347: */
0348: public static int cvtNativeTypeToJdbcType(int nativeType, int size)
0349: throws TdsException {
0350:
0351: // This function is thread safe.
0352:
0353: int result = java.sql.Types.OTHER;
0354: switch (nativeType) {
0355: // XXX We need to figure out how to map _all_ of these types
0356: case SYBBINARY:
0357: result = java.sql.Types.BINARY;
0358: break;
0359: case SYBBIT:
0360: result = java.sql.Types.BIT;
0361: break;
0362: case SYBBITN:
0363: result = java.sql.Types.BIT;
0364: break;
0365: case SYBCHAR:
0366: result = java.sql.Types.CHAR;
0367: break;
0368: case SYBNCHAR:
0369: result = java.sql.Types.CHAR;
0370: break;
0371: case SYBDATETIME4:
0372: result = java.sql.Types.TIMESTAMP;
0373: break;
0374: case SYBDATETIME:
0375: result = java.sql.Types.TIMESTAMP;
0376: break;
0377: case SYBDATETIMN:
0378: result = java.sql.Types.TIMESTAMP;
0379: break;
0380: case SYBDECIMAL:
0381: result = java.sql.Types.DECIMAL;
0382: break;
0383: case SYBNUMERIC:
0384: result = java.sql.Types.NUMERIC;
0385: break;
0386: case SYBFLT8:
0387: result = java.sql.Types.DOUBLE;
0388: break;
0389: case SYBFLTN:
0390: result = java.sql.Types.DOUBLE;
0391: break;
0392: case SYBINT1:
0393: result = java.sql.Types.TINYINT;
0394: break;
0395: case SYBINT2:
0396: result = java.sql.Types.SMALLINT;
0397: break;
0398: case SYBINT4:
0399: result = java.sql.Types.INTEGER;
0400: break;
0401: case SYBINTN: {
0402: switch (size) {
0403: case 1:
0404: result = java.sql.Types.TINYINT;
0405: break;
0406: case 2:
0407: result = java.sql.Types.SMALLINT;
0408: break;
0409: case 4:
0410: result = java.sql.Types.INTEGER;
0411: break;
0412: default:
0413: throw new TdsException("Bad size of SYBINTN");
0414: }
0415: break;
0416: }
0417: // XXX Should money types by NUMERIC or OTHER?
0418: case SYBSMALLMONEY:
0419: result = java.sql.Types.NUMERIC;
0420: break;
0421: case SYBMONEY4:
0422: result = java.sql.Types.NUMERIC;
0423: break;
0424: case SYBMONEY:
0425: result = java.sql.Types.NUMERIC;
0426: break;
0427: case SYBMONEYN:
0428: result = java.sql.Types.NUMERIC;
0429: break;
0430: // case SYBNUMERIC: result = java.sql.Types.NUMERIC; break;
0431: case SYBREAL:
0432: result = java.sql.Types.REAL;
0433: break;
0434: case SYBTEXT:
0435: result = java.sql.Types.LONGVARCHAR;
0436: break;
0437: case SYBNTEXT:
0438: result = java.sql.Types.LONGVARCHAR;
0439: break;
0440: case SYBIMAGE:
0441: result = java.sql.Types.VARBINARY;
0442: break;
0443: case SYBVARBINARY:
0444: result = java.sql.Types.VARBINARY;
0445: break;
0446: case SYBVARCHAR:
0447: result = java.sql.Types.VARCHAR;
0448: break;
0449: case SYBNVARCHAR:
0450: result = java.sql.Types.VARCHAR;
0451: break;
0452: // case SYBVOID: result = java.sql.Types. ; break;
0453: default:
0454: throw new TdsException("Unknown native data type "
0455: + Integer.toHexString(nativeType & 0xff));
0456: }
0457: return result;
0458: } /* cvtNativeTypeToJdbcType() */
0459:
0460: /**
0461: * Return the type of server that we attempted to connect to.
0462: *
0463: * @return TdsDefinitions.SYBASE or TdsDefinitions.SQLSERVER
0464: */
0465: public int getServerType() {
0466: return serverType;
0467: }
0468:
0469: /**
0470: * Try to figure out what client name we should identify
0471: * ourselves as. Get the hostname of this machine,
0472: *
0473: * @return name we will use as the client.
0474: */
0475: private String getClientName() {
0476: // This method is thread safe.
0477: String tmp;
0478: try {
0479: tmp = java.net.InetAddress.getLocalHost().getHostName();
0480: } catch (java.net.UnknownHostException e) {
0481: tmp = "";
0482: }
0483: StringTokenizer st = new StringTokenizer(tmp, ".");
0484:
0485: if (!st.hasMoreTokens()) {
0486: // This means hostname wasn't found for this machine.
0487: return "JOHNDOE";
0488: }
0489:
0490: // Look at the first (and possibly only) word in the name.
0491: tmp = st.nextToken();
0492: if (tmp.length() == 0) {
0493: // This means the hostname had no leading component.
0494: // (This case would be strange.)
0495: return "JANEDOE";
0496: } else if (Character.isDigit(tmp.charAt(0))) {
0497: // This probably means that the name was a quad-decimal
0498: // number. We don't want to send that as our name,
0499: // so make one up.
0500: return "BABYDOE";
0501: } else {
0502: // Ah, Life is good. We have a name. All other
0503: // applications I've seen have upper case client names,
0504: // and so shall we.
0505: return tmp.toUpperCase();
0506: }
0507: }
0508:
0509: /**
0510: * Log onto the SQLServer
0511: * <p>
0512: *
0513: * This method is not synchronized and does not need to be so long
0514: * as it can only be called from the constructor.
0515: *
0516: * <p>
0517: * <U>Login Packet</U>
0518: * <P>
0519: * Packet type (first byte) is 2. The following is from tds.h the numbers
0520: * on the left are offsets <I>not including</I> the packet header.
0521: * <br>
0522: * Note: The logical logon packet is split into two physical
0523: * packets. Each physical packet has its own header.
0524: * <br>
0525: * <PRE>
0526: * -- 0 -- DBCHAR host_name[30];
0527: * -- 30 -- DBTINYINT host_name_length;
0528: * -- 31 -- DBCHAR user_name[30];
0529: * -- 61 -- DBTINYINT user_name_length;
0530: * -- 62 -- DBCHAR password[30];
0531: * -- 92 -- DBTINYINT password_length;
0532: * -- 93 -- DBCHAR host_process[30];
0533: * -- 123 -- DBTINYINT host_process_length;
0534: * -- 124 -- DBCHAR magic1[6]; -- here were most of the mystery stuff is --
0535: * -- 130 -- DBTINYINT bulk_copy;
0536: * -- 131 -- DBCHAR magic2[9]; -- here were most of the mystery stuff is --
0537: * -- 140 -- DBCHAR app_name[30];
0538: * -- 170 -- DBTINYINT app_name_length;
0539: * -- 171 -- DBCHAR server_name[30];
0540: * -- 201 -- DBTINYINT server_name_length;
0541: * -- 202 -- DBCHAR magic3; -- 0, dont know this one either --
0542: * -- 203 -- DBTINYINT password2_length;
0543: * -- 204 -- DBCHAR password2[30];
0544: * -- 234 -- DBCHAR magic4[223];
0545: * -- 457 -- DBTINYINT password2_length_plus2;
0546: * -- 458 -- DBSMALLINT major_version; -- TDS version --
0547: * -- 460 -- DBSMALLINT minor_version; -- TDS version --
0548: * -- 462 -- DBCHAR library_name[10]; -- Ct-Library or DB-Library --
0549: * -- 472 -- DBTINYINT library_length; -- Ct-Library or DB-Library --
0550: * -- 473 -- DBSMALLINT major_version2; -- program version --
0551: * -- 475 -- DBSMALLINT minor_version2; -- program version --
0552: * -- 477 -- DBCHAR magic6[3]; -- ? last two octets are 13 and 17 --
0553: * -- bdw reports last two as 12 and 16 here --
0554: * -- possibly a bitset flag --
0555: * -- 480 -- DBCHAR language[30]; -- ie us-english --
0556: * -- second packet --
0557: * -- 524 -- DBTINYINT language_length; -- 10 in this case --
0558: * -- 525 -- DBCHAR magic7; -- no clue... has 1 in the first octet --
0559: * -- bdw reports 0x0 --
0560: * -- 526 -- DBSMALLINT old_secure; -- explaination? --
0561: * -- 528 -- DBTINYINT encrypted; -- 1 means encrypted all password fields blank --
0562: * -- 529 -- DBCHAR magic8; -- no clue... zeros --
0563: * -- 530 -- DBCHAR sec_spare[9]; -- explaination --
0564: * -- 539 -- DBCHAR char_set[30]; -- ie iso_1 --
0565: * -- 569 -- DBTINYINT char_set_length; -- 5 --
0566: * -- 570 -- DBTINYINT magic9; -- 1 --
0567: * -- 571 -- DBCHAR block_size[6]; -- in text --
0568: * -- 577 -- DBTINYINT block_size_length;
0569: * -- 578 -- DBCHAR magic10[25]; -- lots of stuff here...no clue --
0570: *
0571: * </PRE>
0572: *
0573: * This routine will basically eat all of the data returned from the
0574: * SQLServer.
0575: *
0576: * @author Craig Spannring
0577: *
0578: * @exception TdsUnknownPacketSubType
0579: * @exception com.internetcds.jdbc.tds.TdsException
0580: * @exception java.io.IOException
0581: * @exception java.sql.SQLException
0582: */
0583: private boolean logon() throws java.sql.SQLException,
0584: TdsUnknownPacketSubType, java.io.IOException,
0585: com.internetcds.jdbc.tds.TdsException {
0586: boolean isOkay = true;
0587: byte pad = (byte) 0;
0588: byte[] empty = new byte[0];
0589:
0590: // Added 2000-06-07.
0591: if (tdsVer == Tds.TDS70)
0592: send70Login();
0593: else {
0594:
0595: comm.startPacket(TdsComm.LOGON);
0596:
0597: // hostname (offset0)
0598: // comm.appendString("TOLEDO", 30, (byte)0);
0599: byte[] tmp = encoder.getBytes(getClientName());
0600: comm.appendBytes(tmp, 30, pad);
0601: comm.appendByte((byte) (tmp.length < 30 ? tmp.length : 30));
0602:
0603: // username (offset 31 0x1f)
0604: tmp = encoder.getBytes(user);
0605: comm.appendBytes(tmp, 30, pad);
0606: comm.appendByte((byte) (tmp.length < 30 ? tmp.length : 30));
0607:
0608: // password (offset 62 0x3e)
0609: tmp = encoder.getBytes(password);
0610: comm.appendBytes(tmp, 30, pad);
0611: comm.appendByte((byte) (tmp.length < 30 ? tmp.length : 30));
0612:
0613: // hostproc (offset 93 0x5d)
0614: tmp = encoder.getBytes("00000116");
0615: comm.appendBytes(tmp, 8, pad);
0616:
0617: // unused (offset 109 0x6d)
0618: comm.appendBytes(empty, (30 - 14), pad);
0619:
0620: // apptype (offset )
0621: comm.appendByte((byte) 0x0);
0622: comm.appendByte((byte) 0xA0);
0623: comm.appendByte((byte) 0x24);
0624: comm.appendByte((byte) 0xCC);
0625: comm.appendByte((byte) 0x50);
0626: comm.appendByte((byte) 0x12);
0627:
0628: // hostproc length (offset )
0629: comm.appendByte((byte) 8);
0630:
0631: // type of int2
0632: comm.appendByte((byte) 3);
0633:
0634: // type of int4
0635: comm.appendByte((byte) 1);
0636:
0637: // type of char
0638: comm.appendByte((byte) 6);
0639:
0640: // type of flt
0641: comm.appendByte((byte) 10);
0642:
0643: // type of date
0644: comm.appendByte((byte) 9);
0645:
0646: // notify of use db
0647: comm.appendByte((byte) 1);
0648:
0649: // disallow dump/load and bulk insert
0650: comm.appendByte((byte) 1);
0651:
0652: // sql interface type
0653: comm.appendByte((byte) 0);
0654:
0655: // type of network connection
0656: comm.appendByte((byte) 0);
0657:
0658: // spare[7]
0659: comm.appendBytes(empty, 7, pad);
0660:
0661: // appname
0662: tmp = encoder.getBytes(appName);
0663: comm.appendBytes(tmp, 30, pad);
0664: comm.appendByte((byte) (tmp.length < 30 ? tmp.length : 30));
0665:
0666: // server name
0667: tmp = encoder.getBytes(serverName);
0668: comm.appendBytes(tmp, 30, pad);
0669: comm.appendByte((byte) (tmp.length < 30 ? tmp.length : 30));
0670:
0671: // remote passwords
0672: comm.appendBytes(empty, 2, pad);
0673: tmp = encoder.getBytes(password);
0674: comm.appendBytes(tmp, 253, pad);
0675: comm.appendByte((byte) (tmp.length < 253 ? tmp.length + 2
0676: : 253 + 2));
0677:
0678: // tds version
0679: comm.appendByte((byte) 4);
0680: comm.appendByte((byte) 2);
0681: comm.appendByte((byte) 0);
0682: comm.appendByte((byte) 0);
0683:
0684: // prog name
0685: tmp = encoder.getBytes(progName);
0686: comm.appendBytes(tmp, 10, pad);
0687: comm.appendByte((byte) (tmp.length < 10 ? tmp.length : 10));
0688:
0689: // prog version
0690: comm.appendByte((byte) 6); // Tell the server we can handle SQLServer version 6
0691: comm.appendByte((byte) 0); // Send zero to tell the server we can't handle any other version
0692: comm.appendByte((byte) 0);
0693: comm.appendByte((byte) 0);
0694:
0695: // auto convert short
0696: comm.appendByte((byte) 0);
0697:
0698: // type of flt4
0699: comm.appendByte((byte) 0x0D);
0700:
0701: // type of date4
0702: comm.appendByte((byte) 0x11);
0703:
0704: // language
0705: tmp = encoder.getBytes("us_english");
0706: comm.appendBytes(tmp, 30, pad);
0707: comm.appendByte((byte) (tmp.length < 30 ? tmp.length : 30));
0708:
0709: // notify on lang change
0710: comm.appendByte((byte) 1);
0711:
0712: // security label hierachy
0713: comm.appendShort((short) 0);
0714:
0715: // security components
0716: comm.appendBytes(empty, 8, pad);
0717:
0718: // security spare
0719: comm.appendShort((short) 0);
0720:
0721: // security login role
0722: comm.appendByte((byte) 0);
0723:
0724: // charset
0725: tmp = encoder.getBytes(charset);
0726: comm.appendBytes(tmp, 30, pad);
0727: comm.appendByte((byte) (tmp.length < 30 ? tmp.length : 30));
0728:
0729: // notify on charset change
0730: comm.appendByte((byte) 1);
0731:
0732: // length of tds packets
0733: tmp = encoder.getBytes("512");
0734: comm.appendBytes(tmp, 6, pad);
0735: comm.appendByte((byte) 3);
0736:
0737: // pad out to a longword
0738: comm.appendBytes(empty, 8, pad);
0739:
0740: moreResults2 = true; //JJ 1999-01-10
0741: }
0742:
0743: comm.sendPacket();
0744:
0745: // Get the reply to the logon packet.
0746: PacketResult result;
0747:
0748: while (!((result = processSubPacket()) instanceof PacketEndTokenResult)) {
0749: if (result instanceof PacketErrorResult) {
0750: isOkay = false;
0751: }
0752: // XXX Should really process some more types of packets.
0753: }
0754:
0755: if (isOkay) {
0756: // XXX Should we move this to the Connection class?
0757: isOkay = changeSettings(database, initialSettings);
0758: }
0759:
0760: // XXX Possible bug. What happend if this is cancelled before the logon
0761: // takes place? Should isOkay be false?
0762: return isOkay;
0763: }
0764:
0765: /*
0766: * New code added to handle TDS 7.0 login, which uses a completely
0767: * different packet layout. Logic taken directly from freetds C
0768: * code in tds/login.c. Lots of magic values: I don't pretend
0769: * to have any idea what most of this means.
0770: *
0771: * Added 2000-06-05.
0772: */
0773: private void send70Login() throws java.io.IOException {
0774:
0775: byte[] magic1 = { (byte) 0006, (byte) 0203, (byte) 0362,
0776: (byte) 0370, (byte) 0377, (byte) 0000, (byte) 0000,
0777: (byte) 0000, (byte) 0000, (byte) 0340, (byte) 0003,
0778: (byte) 0000, (byte) 0000, (byte) 0210, (byte) 0377,
0779: (byte) 0377, (byte) 0377, (byte) 0066, (byte) 0004,
0780: (byte) 0000, (byte) 0000 };
0781: byte[] magic2 = { (byte) 0000, (byte) 0100, (byte) 0063,
0782: (byte) 0232, (byte) 0153, (byte) 0120 };
0783: byte[] magic3 = encoder.getBytes("NTLMSSP");
0784: String libName = "DB-Library";
0785: byte pad = (byte) 0;
0786: byte[] empty = new byte[0];
0787: String appName = "CDR";
0788: short len = (short) (86 + 2 * (user.length()
0789: + password.length() + appName.length()
0790: + serverName.length() + libName.length()));
0791: short packSize = (short) (len + 48);
0792: comm.startPacket(TdsComm.LOGON70);
0793: comm.appendTdsShort(packSize);
0794: comm.appendBytes(empty, 5, pad);
0795: comm.appendByte((byte) 0x70);
0796: comm.appendBytes(empty, 7, pad);
0797: comm.appendBytes(magic1, 21, pad);
0798:
0799: // Pack up value lengths, positions.
0800: short curPos = 86;
0801:
0802: // Unknown
0803: comm.appendTdsShort(curPos);
0804: comm.appendTdsShort((short) 0);
0805:
0806: // Username
0807: comm.appendTdsShort(curPos);
0808: comm.appendTdsShort((short) user.length());
0809: curPos += user.length() * 2;
0810:
0811: // Password
0812: comm.appendTdsShort(curPos);
0813: comm.appendTdsShort((short) password.length());
0814: curPos += password.length() * 2;
0815:
0816: // App name
0817: comm.appendTdsShort(curPos);
0818: comm.appendTdsShort((short) appName.length());
0819: curPos += appName.length() * 2;
0820:
0821: // Server name
0822: comm.appendTdsShort(curPos);
0823: comm.appendTdsShort((short) serverName.length());
0824: curPos += serverName.length() * 2;
0825:
0826: // Another unknown value
0827: comm.appendTdsShort((short) 0);
0828: comm.appendTdsShort((short) 0);
0829:
0830: // Library name
0831: comm.appendTdsShort(curPos);
0832: comm.appendTdsShort((short) libName.length());
0833: curPos += libName.length() * 2;
0834:
0835: // Two more unknowns
0836: comm.appendTdsShort(curPos);
0837: comm.appendTdsShort((short) 0);
0838: comm.appendTdsShort(curPos);
0839: comm.appendTdsShort((short) 0);
0840:
0841: // More magic.
0842: comm.appendBytes(magic2, 6, pad);
0843: comm.appendTdsShort(len);
0844: comm.appendTdsShort((short) 0x30);
0845: comm.appendTdsShort(packSize);
0846: comm.appendTdsShort((short) 0);
0847:
0848: // Pack up the login values.
0849: String scrambledPw = tds7CryptPass(password);
0850: comm.appendChars(user);
0851: comm.appendChars(scrambledPw);
0852: comm.appendChars(appName);
0853: comm.appendChars(serverName);
0854: comm.appendChars(libName);
0855:
0856: // Still more magic!
0857: comm.appendBytes(magic3, 7, pad);
0858: comm.appendByte((byte) 0);
0859: comm.appendByte((byte) 1);
0860: comm.appendBytes(empty, 3, pad);
0861: comm.appendByte((byte) 6);
0862: comm.appendByte((byte) 130);
0863: comm.appendBytes(empty, 22, pad);
0864: comm.appendByte((byte) 48);
0865: comm.appendBytes(empty, 7, pad);
0866: comm.appendByte((byte) 48);
0867: comm.appendBytes(empty, 3, pad);
0868: }
0869:
0870: /**
0871: * This is a <B>very</B> poor man's "encryption."
0872: */
0873: private static String tds7CryptPass(String pw) {
0874: int xormask = 0x5A5A;
0875: int len = pw.length();
0876: char[] chars = new char[len];
0877: for (int i = 0; i < len; ++i) {
0878: int c = (int) (pw.charAt(i)) ^ xormask;
0879: int m1 = (c >> 4) & 0x0F0F;
0880: int m2 = (c << 4) & 0xF0F0;
0881: chars[i] = (char) (m1 | m2);
0882: }
0883: return new String(chars);
0884: }
0885:
0886: /**
0887: * change the connection level settings for this connection
0888: * stream to the database.
0889: *
0890: * @return true if the database accepted the changes, false if rejected.
0891: */
0892: synchronized public boolean changeSettings(String database,
0893: String settings) throws java.sql.SQLException {
0894: boolean isOkay = true;
0895: try {
0896: PacketResult result;
0897:
0898: if (database != null) {
0899: isOkay = changeDB(database);
0900: }
0901:
0902: if (isOkay && (settings != null && settings.length() > 0)) {
0903: String query = settings;
0904: comm.startPacket(TdsComm.QUERY);
0905: if (tdsVer == Tds.TDS70)
0906: comm.appendChars(query);
0907: else {
0908: byte[] queryBytes = encoder.getBytes(query);
0909: comm.appendBytes(queryBytes, queryBytes.length,
0910: (byte) 0);
0911: }
0912: moreResults2 = true; //JJ 1999-01-10
0913: comm.sendPacket();
0914:
0915: boolean done = false;
0916: while (!done) {
0917: result = processSubPacket();
0918: done = (result instanceof PacketEndTokenResult)
0919: && !((PacketEndTokenResult) result)
0920: .moreResults();
0921: if (result instanceof PacketErrorResult) {
0922: isOkay = false;
0923: }
0924: // XXX Should really process some more types of packets.
0925: }
0926: }
0927: } catch (com.internetcds.jdbc.tds.TdsUnknownPacketSubType e) {
0928: throw new SQLException("Unknown response. "
0929: + e.getMessage());
0930: } catch (java.io.IOException e) {
0931: throw new SQLException("Network problem. " + e.getMessage());
0932: } catch (com.internetcds.jdbc.tds.TdsException e) {
0933: throw new SQLException(e.getMessage());
0934: }
0935: return isOkay;
0936: } // changeSettings
0937:
0938: /**
0939: * Select a new database to use.
0940: *
0941: * @param database Name of the database to use.
0942: *
0943: * @return true if the change was accepted, false otherwise
0944: */
0945: synchronized private boolean changeDB(String database)
0946: throws java.sql.SQLException {
0947: boolean isOkay = true;
0948: ;
0949:
0950: try {
0951: PacketResult result;
0952: int i;
0953:
0954: // XXX Check to make sure the database name
0955: // doesn't have funny characters.
0956:
0957: // if (database name has funny characters)
0958: if (database.length() > 32) {
0959: throw new SQLException("Name too long " + database);
0960: }
0961:
0962: for (i = 0; i < database.length(); i++) {
0963: char ch;
0964: ch = database.charAt(i);
0965: if (!((ch == '_' && i != 0) || (ch >= 'a' && ch <= 'z')
0966: || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9'))) {
0967: throw new SQLException("Bad database name- "
0968: + database);
0969: }
0970: }
0971:
0972: String query = "use " + database;
0973: comm.startPacket(TdsComm.QUERY);
0974: if (tdsVer == Tds.TDS70)
0975: comm.appendChars(query);
0976: else {
0977: byte[] queryBytes = encoder.getBytes(query);
0978: comm.appendBytes(queryBytes, queryBytes.length,
0979: (byte) 0);
0980: }
0981: moreResults2 = true; //JJ 1999-01-10
0982: comm.sendPacket();
0983:
0984: // XXX Should we check that the change actual was okay
0985: // and throw some sort of exception if it wasn't?
0986: // Get the reply to the change database request.
0987: while (!((result = processSubPacket()) instanceof PacketEndTokenResult)) {
0988: if (result instanceof PacketErrorResult) {
0989: isOkay = false;
0990: }
0991: // XXX Should really process some more types of packets.
0992: }
0993: } catch (com.internetcds.jdbc.tds.TdsUnknownPacketSubType e) {
0994: throw new SQLException("Unknown response. "
0995: + e.getMessage());
0996: } catch (java.io.IOException e) {
0997: throw new SQLException("Network problem. " + e.getMessage());
0998: } catch (com.internetcds.jdbc.tds.TdsException e) {
0999: throw new SQLException(e.getMessage());
1000: }
1001:
1002: return isOkay;
1003: } // changeDB()
1004:
1005: public void cancel() throws java.io.IOException,
1006: com.internetcds.jdbc.tds.TdsException {
1007: // XXX How should this be synchronized? What sort of deadlock
1008: // conditions do we need to consider?
1009:
1010: cancelController.doCancel(comm);
1011: }
1012:
1013: public boolean moreResults() {
1014: return moreResults2;
1015: }
1016:
1017: /**
1018: * Get the length of the current subpacket.
1019: * <p>
1020: * This will eat two bytes from the input socket.
1021: *
1022: * @return length of the current subpacket.
1023: */
1024: private int getSubPacketLength() throws java.io.IOException,
1025: com.internetcds.jdbc.tds.TdsException {
1026: return comm.getTdsShort();
1027: }
1028:
1029: /**
1030: * This will read a error (or warning) message from the SQLServer and
1031: * create a SqlMessage object from that message.
1032: * <p>
1033: * <b> Warning! </b> This is not synchronized because it assumes
1034: * it will only be called by processSubPacket() which is synchronized.
1035: *
1036: * @param packetSubType type of the current subpacket
1037: *
1038: * @return The message returned by the SQLServer.
1039: *
1040: */
1041: private PacketMsgResult processMsg(byte packetSubType)
1042: throws java.io.IOException,
1043: com.internetcds.jdbc.tds.TdsException {
1044: SqlMessage msg = new SqlMessage();
1045:
1046: int len = getSubPacketLength();
1047:
1048: msg.number = comm.getTdsInt();
1049:
1050: msg.state = comm.getByte();
1051:
1052: msg.level = comm.getByte(); // ?class?
1053:
1054: int msgLen = comm.getTdsShort();
1055: msg.message = comm.getString(msgLen);
1056:
1057: // RMK 2000-06-08: the getWarnings() methods aren't implemented, so we
1058: // need to do something with these.
1059: if (showWarnings && msg.message != null) {
1060: String warn = msg.message.trim();
1061: if (warn.length() > 0)
1062: System.err.println("Server message: " + warn);
1063: }
1064:
1065: int srvNameLen = comm.getByte() & 0xFF;
1066: msg.server = comm.getString(srvNameLen);
1067:
1068: if (packetSubType == TDS_MSG_TOKEN
1069: || packetSubType == TDS_ERR_TOKEN) {
1070: // nop
1071: int procNameLen = comm.getByte() & 0xFF;
1072: msg.procName = comm.getString(procNameLen);
1073: } else {
1074: throw new TdsConfused(
1075: "Was expecting a msg or error token. "
1076: + "Found 0x"
1077: + Integer.toHexString(packetSubType & 0xff));
1078: }
1079:
1080: msg.line = comm.getByte();
1081:
1082: // unknonw byte
1083: comm.getByte();
1084:
1085: lastServerMessage = msg;
1086:
1087: if (packetSubType == TDS_ERR_TOKEN) {
1088: return new PacketErrorResult(packetSubType, msg);
1089: } else {
1090: return new PacketMsgResult(packetSubType, msg);
1091: }
1092: }
1093:
1094: /**
1095: * Process an env change message (TDS_ENV_CHG_TOKEN)
1096: * <p>
1097: * <b> Warning! </b> This is not synchronized because it assumes
1098: * it will only be called by processSubPacket() which is synchronized.
1099: *
1100: * @exception java.io.IOException
1101: * @exception com.internetcds.jdbc.tds.TdsException
1102: */
1103: private PacketResult processEnvChange() throws java.io.IOException,
1104: com.internetcds.jdbc.tds.TdsException {
1105: final byte CHARSET_CHANGE = (byte) 3;
1106:
1107: int len = getSubPacketLength();
1108: int type = comm.getByte();
1109: switch (type) {
1110: case CHARSET_CHANGE: {
1111: int clen = comm.getByte() & 0xFF;
1112: String charset;
1113: if (tdsVer == TDS70) {
1114: charset = comm.getString(clen);
1115: comm.skip(len - 2 - clen * 2);
1116: } else {
1117: charset = encoder.getString(comm.getBytes(clen));
1118: comm.skip(len - 2 - clen);
1119: }
1120: setCharset(charset);
1121: break;
1122: }
1123: default: {
1124: // XXX Should actually look at the env change
1125: // instead of ignoring it.
1126: comm.skip(len - 1);
1127: break;
1128: }
1129: }
1130:
1131: return new PacketResult(TDS_ENV_CHG_TOKEN);
1132: }
1133:
1134: /**
1135: * Process an column name subpacket.
1136: * <p>
1137: * <p>
1138: * <b> Warning! </b> This is not synchronized because it assumes
1139: * it will only be called by processSubPacket() which is synchronized.
1140: *
1141: */
1142: private PacketColumnNamesResult processColumnNames()
1143: throws java.io.IOException,
1144: com.internetcds.jdbc.tds.TdsException {
1145: Columns columns = new Columns();
1146:
1147: int totalLen = comm.getTdsShort();
1148:
1149: int bytesRead = 0;
1150: int i = 0;
1151: while (bytesRead < totalLen) {
1152: int colNameLen = comm.getByte();
1153: String colName = encoder.getString(comm
1154: .getBytes(colNameLen));
1155: bytesRead = bytesRead + 1 + colNameLen;
1156: i++;
1157: columns.setName(i, colName);
1158: columns.setLabel(i, colName);
1159: }
1160:
1161: return new PacketColumnNamesResult(columns);
1162: } // processColumnNames()
1163:
1164: /**
1165: * Process the columns information subpacket.
1166: * <p>
1167: * <b> Warning! </b> This is not synchronized because it assumes
1168: * it will only be called by processSubPacket() which is synchronized.
1169: *
1170: */
1171: private PacketColumnInfoResult processColumnInfo()
1172: throws java.io.IOException,
1173: com.internetcds.jdbc.tds.TdsException {
1174: Columns columns = new Columns();
1175: int precision;
1176: int scale;
1177:
1178: int totalLen = comm.getTdsShort();
1179:
1180: int bytesRead = 0;
1181: int numColumns = 0;
1182: while (bytesRead < totalLen) {
1183: scale = -1;
1184: precision = -1;
1185:
1186: int sizeOfColumn = -1;
1187:
1188: byte flagData[] = new byte[4];
1189: for (int i = 0; i < 4; i++) {
1190: flagData[i] = comm.getByte();
1191: bytesRead++;
1192: }
1193: boolean nullable = (flagData[2] & 0x01) > 0;
1194: boolean writeable = (flagData[2] & 0x08) > 0;
1195: boolean autoIncrement = (flagData[2] & 0x10) > 0;
1196:
1197: // Get the type of column
1198: byte columnType = comm.getByte();
1199: bytesRead++;
1200:
1201: if (columnType == SYBTEXT || columnType == SYBIMAGE) {
1202: int i;
1203: int tmpByte;
1204:
1205: // XXX Need to find out what these next 4 bytes are
1206: // Could they be the column size?
1207: comm.skip(4);
1208: bytesRead += 4;
1209:
1210: int tableNameLen = comm.getTdsShort();
1211: bytesRead += 2;
1212: String tableName = encoder.getString(comm
1213: .getBytes(tableNameLen));
1214: bytesRead += tableNameLen;
1215:
1216: sizeOfColumn = 2 << 31 - 1;
1217: } else if (columnType == SYBDECIMAL
1218: || columnType == SYBNUMERIC) {
1219: int tmp;
1220: sizeOfColumn = comm.getByte();
1221: bytesRead++;
1222: precision = comm.getByte(); // Total number of digits
1223: bytesRead++;
1224: scale = comm.getByte(); // # of digits after the decimal point
1225: bytesRead++;
1226: } else if (isFixedSizeColumn(columnType)) {
1227: sizeOfColumn = lookupColumnSize(columnType);
1228: } else {
1229: sizeOfColumn = ((int) comm.getByte() & 0xff);
1230: bytesRead++;
1231: }
1232: numColumns++;
1233:
1234: if (scale != -1) {
1235: columns.setScale(numColumns, scale);
1236: }
1237: if (precision != -1) {
1238: columns.setPrecision(numColumns, precision);
1239: }
1240: columns.setType(numColumns, columnType);
1241: columns.setDisplaySize(numColumns, sizeOfColumn);
1242: columns.setNullable(numColumns,
1243: (nullable ? ResultSetMetaData.columnNullable
1244: : ResultSetMetaData.columnNoNulls));
1245: columns.setAutoIncrement(numColumns, autoIncrement);
1246: columns.setReadOnly(numColumns, !writeable);
1247: }
1248:
1249: // Don't know what the rest is except that the
1250: int skipLen = totalLen - bytesRead;
1251: if (skipLen != 0) {
1252: throw new TdsException("skipping " + skipLen + " bytes");
1253: }
1254:
1255: return new PacketColumnInfoResult(columns);
1256: } // processColumnInfo
1257:
1258: private PacketTabNameResult processTabName()
1259: throws java.io.IOException,
1260: com.internetcds.jdbc.tds.TdsException {
1261: int totalLen = comm.getTdsShort();
1262:
1263: // RMK 2000-06-11. Not sure why the original code is bothering
1264: // to extract the bytes with such meticulous care if it isn't
1265: // going to use the extracted strings for creating the returned
1266: // object. At any rate, this approach doesn't work under TDS 7.0,
1267: // because (1) the name length is a short, not a byte under 7.0,
1268: // and (2) the name string is nameLen wide characters for 7.0,
1269: // not 8-bit characters. So I'm commenting the wasted effort
1270: // and replacing it with a simple call to TdsComm.skip().
1271: //int bytesRead = 0;
1272: //int nameLen = 0;
1273: //String tabName = null;
1274:
1275: //while(bytesRead < totalLen)
1276: //{
1277: // nameLen = comm.getByte();
1278: // bytesRead++;
1279: // tabName = new String(comm.getBytes(nameLen));
1280: // bytesRead += nameLen;
1281: //}
1282:
1283: comm.skip(totalLen);
1284:
1285: return new PacketTabNameResult();
1286: } // processTabName()
1287:
1288: /**
1289: * Process an end subpacket.
1290: * <p>
1291: * This routine assumes that the TDS_END_TOKEN byte has already
1292: * been read.
1293: *
1294: * @return
1295: *
1296: * @exception com.internetcds.jdbc.tds.TdsException
1297: *
1298: * @exception java.io.IOException
1299: * Thrown if some sort of error occured reading bytes from the network.
1300: */
1301: private PacketEndTokenResult processEndToken(byte packetType)
1302: throws com.internetcds.jdbc.tds.TdsException,
1303: java.io.IOException {
1304: byte status = comm.getByte();
1305: comm.skip(3);
1306: int rowCount = comm.getTdsInt();
1307:
1308: if (packetType == TdsDefinitions.TDS_DONEINPROC) {
1309: throw new TdsException("Internal error. TDS_DONEINPROC "
1310: + " is no longer considered an end token");
1311: }
1312:
1313: PacketEndTokenResult result = new PacketEndTokenResult(
1314: packetType, status, rowCount);
1315:
1316: moreResults = result.moreResults();
1317:
1318: // XXX If we executed something that returns multiple result
1319: // sets then we don't want to clear the query in progress flag.
1320: // See the CancelController class for details.
1321: cancelController.finishQuery(result.wasCanceled(), result
1322: .moreResults());
1323:
1324: // XXX Problem handling cancels that were sent after the server
1325: // send the endToken packet
1326: return result;
1327: }
1328:
1329: private PacketDoneInProcResult processDoneInProc(byte packetType)
1330: throws TdsException, java.io.IOException {
1331: byte status = comm.getByte();
1332: comm.skip(3);
1333: int rowCount = comm.getTdsInt();
1334: PacketDoneInProcResult result = new PacketDoneInProcResult(
1335: packetType, status, rowCount);
1336: if (!result.moreResults()) {
1337: throw new TdsException(
1338: "What? No more results with a DONEINPROC!");
1339: }
1340:
1341: if (result.moreResults()
1342: && peek() == TdsDefinitions.TDS_DONEINPROC) {
1343: result = (PacketDoneInProcResult) processSubPacket();
1344: }
1345:
1346: while (result.moreResults()
1347: && (peek() == TdsDefinitions.TDS_PROCID || peek() == TdsDefinitions.TDS_RET_STAT_TOKEN)) {
1348: if (peek() == TDS_PROCID) {
1349: PacketResult tmp = processSubPacket();
1350: } else if (peek() == TDS_RET_STAT_TOKEN) {
1351: PacketRetStatResult tmp = (PacketRetStatResult) processSubPacket();
1352: result.setRetStat(tmp.getRetStat());
1353: }
1354: }
1355: // XXX If we executed something that returns multiple result
1356: // sets then we don't want to clear the query in progress flag.
1357: // See the CancelController class for details.
1358: cancelController.finishQuery(result.wasCanceled(), result
1359: .moreResults());
1360: return result;
1361: }
1362:
1363: /**
1364: * Process a subpacket reply
1365: * <p>
1366: * <b>Note-</b> All subpackets must be processed through here.
1367: * This is the only routine has the proper locking to support
1368: * the cancel method in the Statement class.
1369: * <br>
1370: *
1371: * @return packet subtype the was processed.
1372: */
1373: PacketResult processSubPacket() throws TdsUnknownPacketSubType,
1374: java.io.IOException, com.internetcds.jdbc.tds.TdsException {
1375: return processSubPacket(null);
1376: }
1377:
1378: /**
1379: * Process a subpacket reply
1380: * <p>
1381: * <b>Note-</b> All subpackets must be processed through here. Only this
1382: * routine has the proper locking to support the cancel method in the
1383: * Statement class.
1384: * <br>
1385: *
1386: *
1387: * @return packet subtype the was processed.
1388: */
1389: synchronized PacketResult processSubPacket(Context context)
1390: throws TdsUnknownPacketSubType, java.io.IOException,
1391: com.internetcds.jdbc.tds.TdsException {
1392: // NOTE!!! Before adding anything to this list you must
1393: // consider the ramifications to the the handling of cancels
1394: // as implemented by the CancelController class.
1395: //
1396: // The CancelController class might implicitly assume it can call
1397: // processSubPacket() whenever it is looking for a cancel
1398: // acknowledgment. It assumes that any results of the call
1399: // can be discarded.
1400:
1401: PacketResult result = null;
1402: moreResults = false;
1403:
1404: byte packetSubType = comm.getByte();
1405: Logger.println("processSubPacket: "
1406: + Integer.toHexString(packetSubType & 0xFF) + " "
1407: + "moreResults: " + moreResults());
1408:
1409: switch (packetSubType) {
1410: case TDS_ENV_CHG_TOKEN: {
1411: result = processEnvChange();
1412: break;
1413: }
1414: case TDS_ERR_TOKEN:
1415: case TDS_MSG_TOKEN:
1416: case TDS_MSG50_TOKEN: {
1417: result = processMsg(packetSubType);
1418: break;
1419: }
1420: case TDS_TEXT_UPD_TOKEN: {
1421: int len = getSubPacketLength();
1422: comm.skip(len);
1423: result = new PacketResult(TDS_TEXT_UPD_TOKEN);
1424: break;
1425: }
1426: case TDS_LOGIN_ACK_TOKEN: {
1427: result = processLoginAck();
1428: break;
1429: }
1430: case TDS_RET_STAT_TOKEN: {
1431: result = processRetStat();
1432: break;
1433: }
1434: case TDS_PROCID: {
1435: result = processProcId();
1436: break;
1437: }
1438: case TDS_DONEINPROC: {
1439: result = processDoneInProc(packetSubType);
1440: break;
1441: }
1442: case TDS_DONEPROC:
1443: case TDS_END_TOKEN: {
1444: result = processEndToken(packetSubType);
1445: moreResults2 = ((PacketEndTokenResult) result)
1446: .moreResults();
1447: break;
1448: }
1449: case TDS_COL_NAME_TOKEN: {
1450: result = processColumnNames();
1451: break;
1452: }
1453: case TDS_COL_INFO_TOKEN: {
1454: result = processColumnInfo();
1455: break;
1456: }
1457: case TDS_UNKNOWN_0xA5:
1458: case TDS_UNKNOWN_0xA7:
1459: case TDS_UNKNOWN_0xA8: {
1460: // XXX Need to figure out what this packet is
1461: comm.skip(comm.getTdsShort());
1462: result = new PacketUnknown(packetSubType);
1463: break;
1464: }
1465: case TDS_TABNAME: {
1466: result = processTabName();
1467: break;
1468: }
1469: case TDS_ORDER: {
1470: int len = comm.getTdsShort();
1471: comm.skip(len);
1472:
1473: result = new PacketColumnOrderResult();
1474: break;
1475: }
1476: case TDS_CONTROL: {
1477: int len = comm.getTdsShort();
1478: comm.skip(len);
1479: // FIXME - I'm just ignoring this
1480: result = new PacketControlResult();
1481: break;
1482: }
1483: case TDS_ROW_TOKEN: {
1484: result = getRow(context.getColumnInfo());
1485: break;
1486: }
1487: case TDS7_RESULT_TOKEN: {
1488:
1489: result = processTds7Result();
1490: break;
1491: }
1492: default: {
1493: throw new TdsUnknownPacketSubType(packetSubType);
1494: }
1495: }
1496: return result;
1497: }
1498:
1499: /**
1500: * Find out how many bytes a particular SQLServer data type takes.
1501: *
1502: * @param nativeColumnType
1503: *
1504: * @return number of bytes required by the given type
1505: *
1506: * @exception com.internetcds.jdbc.tds.TdsException
1507: * Thrown if the given type either doesn't exist or is a variable
1508: * sized data type.
1509: */
1510: private int lookupColumnSize(byte nativeColumnType)
1511: throws com.internetcds.jdbc.tds.TdsException {
1512: switch (nativeColumnType) {
1513: case SYBINT1: {
1514: return 1;
1515: }
1516: case SYBINT2: {
1517: return 2;
1518: }
1519: case SYBINT4: {
1520: return 4;
1521: }
1522: case SYBREAL: {
1523: return 4;
1524: }
1525: case SYBFLT8: {
1526: return 8;
1527: }
1528: case SYBDATETIME: {
1529: return 8;
1530: }
1531: case SYBDATETIME4: {
1532: return 8;
1533: }
1534: case SYBBIT: {
1535: return 1;
1536: }
1537: case SYBMONEY: {
1538: return 8;
1539: }
1540: case SYBMONEY4:
1541: case SYBSMALLMONEY: {
1542: return 4;
1543: }
1544: default: {
1545: throw new TdsException("Not fixed size column "
1546: + nativeColumnType);
1547: }
1548: }
1549: } // lookupColumnSize()
1550:
1551: /**
1552: * determine if a given datatype is a fixed size
1553: *
1554: * @param nativeColumnType The SQLServer datatype to check
1555: *
1556: * @return <code>true</code> if the datatype is a fixed size,
1557: * <code>false</code> if the datatype is a variable size
1558: *
1559: * @exception com.internetcds.jdbc.tds.TdsException
1560: * If the <code>nativeColumnType</code> is not a knowm datatype.
1561: *
1562: */
1563: private boolean isFixedSizeColumn(byte nativeColumnType)
1564: throws com.internetcds.jdbc.tds.TdsException {
1565: switch (nativeColumnType) {
1566: case SYBINT1:
1567: case SYBINT2:
1568: case SYBINT4:
1569: case SYBFLT8:
1570: case SYBDATETIME:
1571: case SYBBIT:
1572: case SYBMONEY:
1573: case SYBMONEY4:
1574: case SYBSMALLMONEY:
1575: case SYBREAL:
1576: case SYBDATETIME4: {
1577: return true;
1578: }
1579: case SYBINTN:
1580: case SYBMONEYN:
1581: case SYBVARCHAR:
1582: case SYBNVARCHAR:
1583: case SYBDATETIMN:
1584: case SYBFLTN:
1585: case SYBCHAR:
1586: case SYBNCHAR:
1587: case SYBNTEXT:
1588: case SYBIMAGE:
1589: case SYBVARBINARY:
1590: case SYBBINARY:
1591: case SYBDECIMAL:
1592: case SYBNUMERIC:
1593: case SYBBITN: {
1594: return false;
1595: }
1596: default: {
1597: throw new TdsException("Unrecognized column type 0x"
1598: + Integer.toHexString(nativeColumnType));
1599: }
1600: }
1601: }
1602:
1603: private Object readFloatN(int len) throws TdsException,
1604: java.io.IOException {
1605: Object tmp;
1606:
1607: switch (len) {
1608: case 8: {
1609: long l = comm.getTdsInt64();
1610: tmp = new Double(Double.longBitsToDouble(l));
1611: break;
1612: }
1613: case 4: {
1614: int i = comm.getTdsInt();
1615: tmp = new Float(Float.intBitsToFloat(i));
1616: break;
1617: }
1618: case 0: {
1619: tmp = null;
1620: break;
1621: }
1622: default: {
1623: throw new TdsNotImplemented("Don't now how to handle "
1624: + "float with size of " + len + "(0x"
1625: + Integer.toHexString(len & 0xff) + ")");
1626: }
1627: }
1628: return tmp;
1629: }
1630:
1631: private Object getMoneyValue(int type) throws java.io.IOException,
1632: TdsException {
1633: int len;
1634: Object result;
1635:
1636: if (type == SYBMONEYN) {
1637: len = comm.getByte();
1638: } else {
1639: len = lookupColumnSize((byte) type);
1640: }
1641:
1642: if (len == 0) {
1643: result = null;
1644: } else {
1645: BigInteger x = null;
1646:
1647: if (len == 4) {
1648: x = BigInteger.valueOf(comm.getTdsInt());
1649: } else if (len == 8) {
1650: byte b4 = comm.getByte();
1651: byte b5 = comm.getByte();
1652: byte b6 = comm.getByte();
1653: byte b7 = comm.getByte();
1654: byte b0 = comm.getByte();
1655: byte b1 = comm.getByte();
1656: byte b2 = comm.getByte();
1657: byte b3 = comm.getByte();
1658: long l = (long) (b0 & 0xff) + ((long) (b1 & 0xff) << 8)
1659: + ((long) (b2 & 0xff) << 16)
1660: + ((long) (b3 & 0xff) << 24)
1661: + ((long) (b4 & 0xff) << 32)
1662: + ((long) (b5 & 0xff) << 40)
1663: + ((long) (b6 & 0xff) << 48)
1664: + ((long) (b7 & 0xff) << 56);
1665: x = BigInteger.valueOf(l);
1666: } else {
1667: throw new TdsConfused(
1668: "Don't know what to do with len of " + len);
1669: }
1670: x = x.divide(BigInteger.valueOf(100));
1671: result = new BigDecimal(x, 2);
1672: }
1673: return result;
1674: } // getMoneyValue
1675:
1676: /**
1677: * Extracts decimal value from the server's results packet. Takes
1678: * advantage of Java's superb handling of large numbers, which does
1679: * all the heavy lifting for us. Format is:
1680: * <UL>
1681: * <LI>Length byte <code>len</code>; count includes sign byte.</LI>
1682: * <LI>Sign byte (0=negative; 1=positive).</LI>
1683: * <LI>Magnitude bytes (array of <code>len</code> - 1 bytes,
1684: * in little-endian order.</LI>
1685: * </UL>
1686: *
1687: * @param scale number of decimal digits after the decimal
1688: * point.
1689: * @return <code>BigDecimal</code> for extracted value
1690: * (or (<code>null</code> if appropriate).
1691: */
1692: private Object getDecimalValue(int scale) throws TdsException,
1693: java.io.IOException, NumberFormatException {
1694: int len = comm.getByte() & 0xff;
1695: if (--len < 1)
1696: return null;
1697:
1698: // RMK 2000-06-10. Deduced from some testing/packet sniffing.
1699: byte[] bytes = new byte[len];
1700: int signum = comm.getByte() == 0 ? -1 : 1;
1701: while (len > 0)
1702: bytes[--len] = comm.getByte();
1703: BigInteger bigInt = new BigInteger(signum, bytes);
1704: return new BigDecimal(bigInt, scale);
1705: }
1706:
1707: private Object getDatetimeValue(int type)
1708: throws java.io.IOException, TdsException {
1709: // Some useful constants
1710: final long SECONDS_PER_DAY = 24L * 60L * 60L;
1711: final long DAYS_BETWEEN_1900_AND_1970 = 25567L;
1712:
1713: int len;
1714: Object result;
1715:
1716: if (type == SYBDATETIMN) {
1717: len = comm.getByte();
1718: } else if (type == SYBDATETIME4) {
1719: len = 4;
1720: } else {
1721: len = 8; // XXX shouldn't this be an error?
1722: }
1723:
1724: switch (len) {
1725: case 0: {
1726: result = null;
1727: break;
1728: }
1729: case 8: {
1730: // It appears that a datetime is made of of 2 32bit ints
1731: // The first one is the number of days since 1900
1732: // The second integer is the number of seconds*300
1733: // The reason the calculations below are sliced up into
1734: // such small baby steps is to avoid a bug in JDK1.2.2's
1735: // runtime, which got confused by the original complexity.
1736: long tdsDays = (long) comm.getTdsInt();
1737: long tdsTime = (long) comm.getTdsInt();
1738: long sqlDays = tdsDays - DAYS_BETWEEN_1900_AND_1970;
1739: long seconds = sqlDays * SECONDS_PER_DAY + tdsTime / 300L;
1740: long micros = ((tdsTime % 300L) * 1000000L) / 300L;
1741: long millis = seconds * 1000L + micros / 1000L - zoneOffset;
1742:
1743: // Round up if appropriate.
1744: if (micros % 1000L >= 500L)
1745: millis++;
1746:
1747: result = new Timestamp(millis - getDstOffset(millis));
1748: break;
1749: }
1750: case 4: {
1751: // Accroding to Transact SQL Reference
1752: // a smalldatetime is two small integers.
1753: // The first is the number of days past January 1, 1900,
1754: // the second smallint is the number of minutes past
1755: // midnight.
1756:
1757: long tdsDays = (long) comm.getTdsShort();
1758: long minutes = (long) comm.getTdsShort();
1759: long sqlDays = tdsDays - DAYS_BETWEEN_1900_AND_1970;
1760: long seconds = sqlDays * SECONDS_PER_DAY + minutes * 60L;
1761: long millis = seconds * 1000L - zoneOffset;
1762:
1763: result = new Timestamp(millis - getDstOffset(millis));
1764: break;
1765:
1766: }
1767: default: {
1768: result = null;
1769: throw new TdsNotImplemented("Don't now how to handle "
1770: + "date with size of " + len);
1771: }
1772: }
1773: return result;
1774: } // getDatetimeValue()
1775:
1776: /**
1777: * Determines the number of milliseconds needed to adjust for daylight
1778: * savings time for a given date/time value. Note that there is a problem
1779: * with the way SQL Server sends a DATETIME value, since it is constructed
1780: * to represent the local time for the server. This means that each fall
1781: * there is a window of approximately one hour during which a single value
1782: * can represent two different times.
1783: */
1784: private long getDstOffset(long time) {
1785: Calendar cal = Calendar.getInstance();
1786: cal.setTime(new java.util.Date(time));
1787: return cal.get(Calendar.DST_OFFSET);
1788: }
1789:
1790: private Object getIntValue(int type) throws java.io.IOException,
1791: TdsException {
1792: Object result;
1793: int len;
1794:
1795: switch (type) {
1796: case SYBINTN: {
1797: len = comm.getByte();
1798: break;
1799: }
1800: case SYBINT4: {
1801: len = 4;
1802: break;
1803: }
1804: case SYBINT2: {
1805: len = 2;
1806: break;
1807: }
1808: case SYBINT1: {
1809: len = 1;
1810: break;
1811: }
1812: default: {
1813: result = null;
1814: throw new TdsNotImplemented("can't handle integer of type "
1815: + Integer.toHexString(type));
1816: }
1817: }
1818:
1819: switch (len) {
1820: case 4: {
1821: result = new Long(comm.getTdsInt());
1822: break;
1823: }
1824: case 2: {
1825: result = new Long(comm.getTdsShort());
1826: break;
1827: }
1828: case 1: {
1829: int tmp = toUInt(comm.getByte()); // XXX Are we sure this should be unsigned?
1830: result = new Long(tmp);
1831: break;
1832: }
1833: case 0: {
1834: result = null;
1835: break;
1836: }
1837: default: {
1838: result = null;
1839: throw new TdsConfused("Bad SYBINTN length of " + len);
1840: }
1841: }
1842: return result;
1843: } // getIntValue()
1844:
1845: private Object getCharValue(boolean wideChars) throws TdsException,
1846: java.io.IOException {
1847: Object result;
1848: int len = tdsVer == Tds.TDS70 ? comm.getTdsShort() : comm
1849: .getByte() & 0xFF;
1850:
1851: if (len == 0 || tdsVer == Tds.TDS70 && len == 0xFFFF) {
1852: result = null;
1853: } else if (len > 0) {
1854: if (wideChars)
1855: result = comm.getString(len / 2);
1856: else
1857: result = encoder.getString(comm.getBytes(len));
1858:
1859: if (result.equals(" ")) {
1860: // In SQL trailing spaces are stripped from strings
1861: // MS SQLServer denotes a zero length string
1862: // as a single space.
1863: result = "";
1864: }
1865: } else {
1866: throw new TdsConfused("String with length<0");
1867: }
1868: return result;
1869: } // getCharValue()
1870:
1871: private Object getTextValue(boolean wideChars) throws TdsException,
1872: java.io.IOException {
1873: String result;
1874:
1875: byte hasValue = comm.getByte();
1876:
1877: if (hasValue == 0) {
1878: result = null;
1879: } else {
1880: // XXX Have no idea what these 24 bytes are
1881: // 2000-06-06 RMK They are the TEXTPTR (16 bytes) and the TIMESTAMP.
1882: comm.skip(24);
1883:
1884: int len = comm.getTdsInt();
1885:
1886: // RMK 2000-06-11
1887: // The logic immediately below does not agree with test t0031,
1888: // so I'm commenting it out. On the other hand, it's a bit
1889: // puzzling that a column defined as TEXT NOT NULL needs the
1890: // hasValue byte read just above, but apparently it does.
1891: //if (len == 0)
1892: //{
1893: // result = null;
1894: //}
1895: //else
1896: if (len >= 0) {
1897: if (wideChars) {
1898: result = comm.getString(len / 2);
1899: } else {
1900: result = encoder.getString(comm.getBytes(len));
1901: }
1902:
1903: if (" ".equals(result)) {
1904: // In SQL trailing spaces are stripped from strings
1905: // MS SQLServer denotes a zero length string
1906: // as a single space.
1907: result = "";
1908: }
1909: } else {
1910: throw new TdsConfused("String with length<0");
1911: }
1912: }
1913: return result;
1914: } // getTextValue()
1915:
1916: private Object getImageValue() throws TdsException,
1917: java.io.IOException {
1918: byte[] result;
1919:
1920: byte hasValue = comm.getByte();
1921:
1922: if (hasValue == 0) {
1923: result = null;
1924: } else {
1925: // XXX Have no idea what these 24 bytes are
1926: // 2000-06-06 RMK They are the TEXTPTR (16 bytes) and the TIMESTAMP.
1927: comm.skip(24);
1928:
1929: int len = comm.getTdsInt();
1930:
1931: // RMK 2000-06-11
1932: // The logic immediately below does not agree with test t0031,
1933: // so I'm commenting it out. On the other hand, it's a bit
1934: // puzzling that a column defined as TEXT NOT NULL needs the
1935: // hasValue byte read just above, but apparently it does.
1936: //if (len == 0)
1937: //{
1938: // result = null;
1939: //}
1940: //else
1941: if (len >= 0) {
1942: result = comm.getBytes(len);
1943: } else {
1944: throw new TdsConfused("String with length<0");
1945: }
1946: }
1947: return result;
1948: } // getImageValue()
1949:
1950: /**
1951: * get one result row from the TDS stream
1952: * <p>
1953: * This will read a full row from the TDS stream and store it in
1954: * a PacketRowResult object.
1955: *
1956: */
1957: synchronized private PacketRowResult getRow(Columns columnsInfo)
1958: throws TdsException, java.io.IOException {
1959: PacketRowResult result = null;
1960:
1961: int i;
1962:
1963: result = new PacketRowResult(columnsInfo.getColumnCount());
1964:
1965: for (i = 1; i <= columnsInfo.getColumnCount(); i++) {
1966: Object element;
1967: int colType = columnsInfo.getType(i);
1968:
1969: Logger.println("colno=" + i + " type=" + colType
1970: + " offset="
1971: + Integer.toHexString(comm.inBufferIndex));
1972: switch (colType) {
1973: case SYBINTN:
1974: case SYBINT1:
1975: case SYBINT2:
1976: case SYBINT4: {
1977: element = getIntValue(colType);
1978: break;
1979: }
1980: case SYBIMAGE: {
1981: element = getImageValue();
1982: break;
1983: }
1984: case SYBTEXT: {
1985: element = getTextValue(false);
1986: break;
1987: }
1988: case SYBNTEXT: {
1989: element = getTextValue(true);
1990: break;
1991: }
1992: case SYBCHAR:
1993: case SYBVARCHAR: {
1994: element = getCharValue(false);
1995: break;
1996: }
1997: case SYBNCHAR:
1998: case SYBNVARCHAR: {
1999: element = getCharValue(true);
2000: break;
2001: }
2002: case SYBREAL: {
2003: element = readFloatN(4);
2004: break;
2005: }
2006: case SYBFLT8: {
2007: element = readFloatN(8);
2008: break;
2009: }
2010: case SYBFLTN: {
2011: int len;
2012:
2013: len = comm.getByte();
2014:
2015: element = readFloatN(len);
2016: break;
2017: }
2018: case SYBSMALLMONEY:
2019: case SYBMONEY:
2020: case SYBMONEYN: {
2021: element = getMoneyValue(colType);
2022: break;
2023: }
2024: case SYBNUMERIC:
2025: case SYBDECIMAL: {
2026: element = getDecimalValue(columnsInfo.getScale(i));
2027: break;
2028: }
2029: case SYBDATETIME4:
2030: case SYBDATETIMN:
2031: case SYBDATETIME: {
2032: element = getDatetimeValue(colType);
2033: break;
2034: }
2035: case SYBVARBINARY:
2036: case SYBBINARY: {
2037: int len = tdsVer == Tds.TDS70 ? comm.getTdsShort()
2038: : (comm.getByte() & 0xff);
2039: if (tdsVer == Tds.TDS70 && len == 0xffff)
2040: element = null;
2041: else
2042: element = comm.getBytes(len);
2043: break;
2044: }
2045: case SYBBITN:
2046: case SYBBIT: {
2047: if (colType == SYBBITN && comm.getByte() == 0)
2048: element = null;
2049: else
2050: element = new Boolean((comm.getByte() != 0) ? true
2051: : false);
2052: break;
2053: }
2054: default: {
2055: element = null;
2056: throw new TdsNotImplemented("Don't now how to handle "
2057: + "column type 0x"
2058: + Integer.toHexString(colType));
2059: }
2060: }
2061: result.setElementAt(element, i);
2062: }
2063:
2064: return result;
2065: } // getRow()
2066:
2067: private boolean createStoredProcedureNameTable() {
2068: boolean result = false;
2069: String sql = null;
2070:
2071: try {
2072: java.sql.Statement stmt = connection.createStatement();
2073:
2074: // ignore any of the exceptions thrown because they either
2075: // don't matter or they will make themselves known when we try
2076: // to use the name generator stored procedure.
2077: try {
2078: sql = "" + "create table " + procNameTableName
2079: + "( "
2080: + " id NUMERIC(10, 0) IDENTITY, "
2081: + " session int not null, "
2082: + " name char(29) not null "
2083: + ") ";
2084: stmt.executeUpdate(sql);
2085: } catch (java.sql.SQLException e) {
2086: // don't care
2087: }
2088:
2089: try {
2090: sql = "" + "create procedure "
2091: + procNameGeneratorName
2092: + " "
2093: + "as "
2094: + "begin tran "
2095: + "insert into "
2096: + procNameTableName
2097: + " "
2098: + " (session, name) "
2099: + " values "
2100: + " (@@spid, '') "
2101: + " "
2102: + "update "
2103: + procNameTableName
2104: + " "
2105: + " set name=('"
2106: + user
2107: + ".jdbctmpsp' + "
2108: + " convert(varchar, @@IDENTITY)) "
2109: + " where id = @@IDENTITY "
2110: + " "
2111: + "select name from "
2112: + procNameTableName
2113: + " "
2114: + " where id=@@IDENTITY "
2115: + " "
2116: + "commit tran "
2117: + "";
2118:
2119: stmt.execute(sql);
2120: stmt.execute("sp_procxmode " + procNameGeneratorName
2121: + ", 'anymode' ");
2122: } catch (java.sql.SQLException e) {
2123: // don't care
2124: }
2125:
2126: stmt = null;
2127: } catch (java.sql.SQLException e) {
2128: // don't care
2129: }
2130: return result;
2131: }
2132:
2133: private String generateUniqueProcName()
2134: throws java.sql.SQLException {
2135: java.sql.Statement stmt = connection.createStatement();
2136:
2137: boolean wasRs;
2138:
2139: wasRs = stmt.execute("exec " + procNameGeneratorName);
2140: if (!wasRs) {
2141: throw new java.sql.SQLException(
2142: "Confused. Was expecting a result set.");
2143: }
2144:
2145: java.sql.ResultSet rs;
2146: rs = stmt.getResultSet();
2147: if (!rs.next()) {
2148: throw new java.sql.SQLException(
2149: "Couldn't get stored proc name");
2150: }
2151: return rs.getString(1);
2152: }
2153:
2154: /**
2155: * Create a new and unique name for a store procedure.
2156: *
2157: * This routine will return a unique name for a stored procedure
2158: * that will be associated with a PreparedStatement().
2159: * <p>
2160: * Since SQLServer supports temporary procedure names we can just
2161: * use UniqueId.getUniqueId() to generate a unique (for the connection)
2162: * name.
2163: * <p>
2164: * Sybase does not support temporary procedure names so we will have
2165: * to have a per user table devoted to storing user specific stored
2166: * procedures. The table name will be of the form
2167: * database.user.jdbc_temp_stored_proc_names. The table will be defined
2168: * as
2169: * <code>
2170: * CREATE TABLE database.user.jdbc_temp_stored_proc_names (
2171: * id NUMERIC(10, 0) IDENTITY;
2172: * session int not null;
2173: * name char(29)
2174: * )
2175: * <code>
2176: * This routine will use that table to track names that are being
2177: * used.
2178: */
2179: public String getUniqueProcedureName() throws java.sql.SQLException {
2180: String result = null;
2181:
2182: if (serverType == SYBASE) {
2183: if (null == procNameTableName) {
2184: procNameTableName = database + "." + user
2185: + ".jdbc_temp_stored_proc_names";
2186: procNameGeneratorName = user
2187: + ".jdbc_gen_temp_sp_names";
2188: }
2189:
2190: //
2191: // Attempt to create the table for the stored procedure names
2192: // If it already exists we'll get an error, but we don't care.
2193: // Also create a stored procedure for generating the unique
2194: // names.
2195: //
2196: haveProcNameTable = createStoredProcedureNameTable();
2197:
2198: result = generateUniqueProcName();
2199: } else {
2200: result = "#jdbc#" + UniqueId.getUniqueId();
2201: }
2202: return result;
2203: } // getUniqueProcedureName()
2204:
2205: /**
2206: *
2207: */
2208: synchronized public PacketResult submitProcedure(String sql,
2209: SQLWarningChain chain) throws SQLException {
2210:
2211: PacketResult result = null;
2212: PacketResult tmp = null;
2213: boolean okay = true;
2214: byte tmpByte;
2215: SQLException exception = null;
2216:
2217: try {
2218: executeQuery(sql, null, 0);
2219:
2220: tmpByte = (byte) (comm.peek() & 0xff);
2221:
2222: while (!((tmp = processSubPacket()) instanceof PacketEndTokenResult)) {
2223: if (tmp instanceof PacketErrorResult) {
2224: result = tmp;
2225: okay = false;
2226: // XXX I'm sure we need to do more here.
2227:
2228: // how about throwing an Exception? --SB
2229: exception = ((PacketErrorResult) tmp).getMsg()
2230: .toSQLException();
2231: } else if (tmp instanceof PacketMsgResult) {
2232: chain.addOrReturn((PacketMsgResult) tmp);
2233: } else {
2234: throw new SQLException(
2235: "Confused. Was expecting the "
2236: + "end of result, found a "
2237: + tmp.getClass().getName());
2238: }
2239: }
2240: if (result == null) {
2241: result = tmp;
2242: }
2243: } catch (java.io.IOException e) {
2244: throw new SQLException("Network error" + e.getMessage());
2245: } catch (com.internetcds.jdbc.tds.TdsUnknownPacketSubType e) {
2246: throw new SQLException(e.getMessage());
2247: } catch (com.internetcds.jdbc.tds.TdsException e) {
2248: throw new SQLException(e.getMessage());
2249: }
2250:
2251: if (!okay) {
2252: throw exception;
2253: }
2254: return result;
2255: }
2256:
2257: /**
2258: * Execute a stored procedure on the SQLServer
2259: * <p>
2260: *
2261: * @param procedure the stored procedure to execute.
2262: * @param parameterList the parameter to pass to the stored procedure
2263: *
2264: * @exception java.sql.SQLException
2265: * @exception com.internetcds.jdbc.tds.TdsException
2266: */
2267: synchronized public void executeProcedure(String procedureName,
2268: ParameterListItem[] formalParameterList,
2269: ParameterListItem[] actualParameterList,
2270: java.sql.Statement stmt, int timeout)
2271: throws java.sql.SQLException,
2272: com.internetcds.jdbc.tds.TdsException {
2273:
2274: // A stored procedure has a packet type of 0x03 in the header packet.
2275: // for non-image date the packets consists of
2276: // offset length desc.
2277: // 0 1 The length of the name of the stored proc
2278: // 1 N1 The name of the stored proc
2279: // N1 + 1 2 unknown (filled with zeros?)
2280: // N1 + 3 2 unknown prefix for param 1 (zero filled?)
2281: // N1 + 5 1 datatype for param 1
2282: // N1 + 6 1 max length of param 1
2283: // N1 + 7 N2 parameter 1 data
2284: // ...
2285: //
2286: // For image data (datatype 0x22) the packet consists of
2287: // 0 1 The length of the name of the stored proc
2288: // 1 N1 The name of the stored proc
2289: // N1 + 1 2 unknown (filled with zeros?)
2290: // N1 + 3 2 unknown prefix for param 1 (zero filled?)
2291: // N1 + 5 1 datatype for param 1
2292: // N1 + 6 4 length of param 1
2293: // N1 + 10 4 length of param 1 (duplicated?)
2294: // N1 + 7 N2 parameter 1 data
2295: // ...
2296:
2297: int i;
2298:
2299: try {
2300: // mark that we are performing a query
2301: cancelController.setQueryInProgressFlag();
2302:
2303: // Start sending the procedure execute packet.
2304: comm.startPacket(TdsComm.PROC);
2305:
2306: if (tdsVer == Tds.TDS70) {
2307: comm.appendTdsShort((short) (procedureName.length()));
2308: comm.appendChars(procedureName);
2309: } else {
2310: byte[] nameBytes = encoder.getBytes(procedureName);
2311: comm.appendByte((byte) nameBytes.length);
2312: comm.appendBytes(nameBytes, nameBytes.length, (byte) 0);
2313: }
2314: comm.appendByte((byte) 0);
2315: comm.appendByte((byte) 0);
2316: // Now handle the parameters
2317: for (i = 0; i < formalParameterList.length; i++) {
2318: byte nativeType = cvtJdbcTypeToNativeType(formalParameterList[i].type);
2319:
2320: comm.appendByte((byte) 0);
2321: comm.appendByte((byte) 0);
2322:
2323: switch (nativeType) {
2324: case SYBCHAR:
2325: case SYBVARCHAR: {
2326: String val = (String) actualParameterList[i].value;
2327: int len = val != null ? val.length() : 0;
2328: int max = formalParameterList[i].maxLength;
2329: //Sinisa
2330: //using actualParameters caused problems
2331: // if (actualParameterList[i].formalType.startsWith("n")) {
2332: if (formalParameterList[i].formalType
2333: .startsWith("n")) {
2334: /*
2335: * This is a Unicode column, save to assume TDS 7.0
2336: */
2337: if (max > 4000) {
2338: comm.appendByte(SYBNTEXT);
2339: comm.appendTdsInt(max * 2);
2340: if (val == null)
2341: comm.appendTdsInt(0xFFFFFFFF);
2342: else {
2343: comm.appendTdsInt(len * 2);
2344: comm.appendChars(val);
2345: }
2346: } else {
2347: comm
2348: .appendByte((byte) (SYBNVARCHAR | 0x80));
2349: comm.appendTdsShort((short) (max * 2));
2350: if (val == null)
2351: comm.appendTdsShort((short) 0xFFFF);
2352: else {
2353: comm.appendTdsShort((short) (len * 2));
2354: comm.appendChars(val);
2355: }
2356: }
2357:
2358: } else {
2359: /*
2360: * Either VARCHAR or TEXT, TEXT can not happen
2361: * with TDS 7.0 as we would always use NTEXT there
2362: */
2363: if (tdsVer != TDS70 && max > 255) {
2364: // TEXT
2365: comm.appendByte((byte) SYBTEXT);
2366: sendSybImage(encoder
2367: .getBytes((String) actualParameterList[i].value));
2368: } else {
2369: // VARCHAR
2370: sendSybChar(
2371: ((String) actualParameterList[i].value),
2372: formalParameterList[i].maxLength);
2373: }
2374: }
2375: break;
2376:
2377: }
2378:
2379: case SYBINT4:
2380: case SYBINTN: {
2381: if (nativeType == SYBINTN) {
2382: comm.appendByte(nativeType);
2383: // set the maximum length of the field,
2384: comm.appendByte((byte) 4);
2385:
2386: // set the actual length, and the data
2387: if (actualParameterList[i].value == null) {
2388: comm.appendByte((byte) 0);
2389: // comm.appendTdsInt((byte)0);
2390: } else {
2391: comm.appendByte((byte) 4);
2392: comm
2393: .appendTdsInt(((Number) (actualParameterList[i].value))
2394: .intValue());
2395: }
2396: } else if (actualParameterList[i].value == null) {
2397: comm.appendByte(SYBINTN);
2398: comm.appendByte((byte) 4);
2399: comm.appendByte((byte) 0);
2400: } else {
2401: comm.appendByte(nativeType);
2402: comm
2403: .appendTdsInt(((Number) (actualParameterList[i].value))
2404: .intValue());
2405: }
2406: break;
2407: }
2408: case SYBFLT8: {
2409: //sinisa
2410: //Add possibility with NULL value as a SYBFLT8 parameter
2411: if (actualParameterList[i].value != null) {
2412: Number n = (Number) (actualParameterList[i].value);
2413: Double d = new Double(n.doubleValue());
2414: comm.appendByte((byte) nativeType);
2415: comm.appendFlt8(d);
2416: }
2417: //sinisa
2418: //If parameter value is NULL send INTEGER value for NULL value to database
2419: else {
2420: comm.appendByte(SYBINTN);
2421: comm.appendByte((byte) 4);
2422: comm.appendByte((byte) 0);
2423: }
2424: break;
2425: }
2426: case SYBDATETIMN: {
2427: comm.appendByte((byte) nativeType);
2428:
2429: comm.appendByte((byte) 8);
2430: if (actualParameterList[i].value == null) {
2431: comm.appendByte((byte) 0);
2432: } else {
2433: Timestamp value;
2434: if (actualParameterList[i].value instanceof java.sql.Timestamp) {
2435: value = (Timestamp) actualParameterList[i].value;
2436: } else {
2437: value = new Timestamp(
2438: ((java.util.Date) actualParameterList[i].value)
2439: .getTime());
2440: }
2441:
2442: comm.appendByte((byte) 8);
2443:
2444: final int secondsPerDay = 24 * 60 * 60;
2445: final int msPerDay = secondsPerDay * 1000;
2446: final int nsPerMs = 1000 * 1000;
2447: // epochsDifference is the number of days between unix
2448: // epoch (1970 based) and the sybase epoch (1900 based)
2449: final int epochsDifference = 25567;
2450:
2451: long nanoseconds = value.getNanos();
2452:
2453: // ms is the number of milliseconds into unix epoch
2454:
2455: long ms = ((value.getTime() + (nanoseconds / nsPerMs)) + zoneOffset);
2456: ms -= getDstOffset(ms);
2457: long msIntoCurrentDay = ms % msPerDay;
2458:
2459: long daysIntoUnixEpoch = (ms - msIntoCurrentDay)
2460: / msPerDay;
2461:
2462: int jiffies = (int) ((msIntoCurrentDay * 300) / 1000);
2463: int daysIntoSybaseEpoch = (int) daysIntoUnixEpoch
2464: + epochsDifference;
2465:
2466: comm.appendTdsInt(daysIntoSybaseEpoch);
2467: comm.appendTdsInt(jiffies);
2468: }
2469: break;
2470: }
2471: case SYBIMAGE: {
2472: comm.appendByte((byte) nativeType);
2473:
2474: sendSybImage((byte[]) actualParameterList[i].value);
2475: break;
2476: }
2477: case SYBTEXT: {
2478: comm.appendByte((byte) SYBTEXT);
2479: sendSybImage(encoder
2480: .getBytes((String) actualParameterList[i].value));
2481: break;
2482: }
2483: //Sinisa
2484: //Add implementation of SYBBIT native type
2485:
2486: case SYBBIT: {
2487: if (actualParameterList[i].value == null) {
2488: comm.appendByte(SYBINTN);
2489: comm.appendByte((byte) 1);
2490: comm.appendByte((byte) 0);
2491: } else {
2492: comm.appendByte(nativeType);
2493: if (((Byte) (actualParameterList[i].value))
2494: .intValue() == 1)
2495: comm.appendByte((byte) 1);
2496: else
2497: comm.appendByte((byte) 0);
2498: }
2499: break;
2500:
2501: // comm.appendByte((byte)nativeType);
2502: //sendSybImage(byte[])actualParameterList[i].value);
2503: // sendSybChar(((String)actualParameterList[i].value),1);
2504: // break;
2505:
2506: }
2507: case SYBVOID:
2508: case SYBVARBINARY:
2509: //Sinisa
2510: // case SYBVARCHAR:
2511: case SYBBINARY:
2512: case SYBINT1:
2513: //case SYBBIT:
2514: case SYBINT2:
2515: case SYBDATETIME4:
2516: case SYBREAL:
2517: case SYBMONEY:
2518: case SYBDATETIME:
2519: case SYBDECIMAL:
2520: case SYBNUMERIC:
2521: case SYBFLTN:
2522: case SYBMONEYN:
2523: case SYBMONEY4:
2524: default: {
2525: throw new SQLException(
2526: "Not implemented for nativeType 0x"
2527: + Integer.toHexString(nativeType));
2528: }
2529: }
2530: }
2531: //sinisa
2532: // moreResults2=true;
2533: comm.sendPacket();
2534: waitForDataOrTimeout(stmt, timeout);
2535: } catch (java.io.IOException e) {
2536: throw new SQLException("Network error- " + e.getMessage());
2537: }
2538: } /* executeProcedure() */
2539:
2540: private void sendSybImage(byte[] value) throws java.io.IOException {
2541: int i;
2542: int length = (value == null ? 0 : value.length);
2543:
2544: // send the lenght of this piece of data
2545: comm.appendTdsInt(length);
2546:
2547: // send the length of this piece of data again
2548: comm.appendTdsInt(length);
2549:
2550: // send the data
2551: for (i = 0; i < length; i++) {
2552: comm.appendByte(value[i]);
2553: }
2554: }
2555:
2556: private void sendSybChar(String value, int maxLength)
2557: throws java.io.IOException {
2558:
2559: byte[] converted;
2560: if (value == null) {
2561: converted = new byte[0];
2562: } else {
2563: converted = encoder.getBytes(value);
2564: }
2565:
2566: if (converted.length > 255 && tdsVer != TDS70) {
2567: throw new java.io.IOException("String too long");
2568: }
2569:
2570: // set the type of the column
2571: // set the maximum length of the field
2572: // set the actual lenght of the field.
2573: if (converted.length > 256) {
2574: comm.appendByte((byte) (SYBVARCHAR | 0x80));
2575: comm.appendTdsShort((short) (maxLength));
2576: comm.appendTdsShort((short) (converted.length));
2577: } else {
2578: comm.appendByte(SYBVARCHAR);
2579: comm.appendByte((byte) (maxLength));
2580: comm.appendByte((byte) (converted.length));
2581: }
2582:
2583: comm.appendBytes(converted);
2584: }
2585:
2586: /**
2587: * Process a login ack supacket
2588: */
2589: private PacketResult processLoginAck()
2590: throws com.internetcds.jdbc.tds.TdsException,
2591: java.io.IOException {
2592: int len = getSubPacketLength();
2593: int bytesRead = 0;
2594:
2595: if (tdsVer == Tds.TDS70) {
2596: comm.skip(5);
2597: int nameLen = comm.getByte();
2598: databaseProductName = comm.getString(nameLen);
2599: databaseProductVersion = ("" + comm.getByte() + "."
2600: + comm.getByte() + "." + ((256 * comm.getByte()) + comm
2601: .getByte()));
2602: } else {
2603: comm.skip(5);
2604: short nameLen = comm.getByte();
2605: databaseProductName = comm.getString(nameLen);
2606: comm.skip(1);
2607: databaseProductVersion = ("" + comm.getByte() + "." + comm
2608: .getByte());
2609: comm.skip(1);
2610: }
2611:
2612: if (databaseProductName.length() > 1
2613: && -1 != databaseProductName.indexOf('\0')) {
2614: int last = databaseProductName.indexOf('\0');
2615: databaseProductName = databaseProductName
2616: .substring(0, last);
2617: }
2618:
2619: return new PacketResult(TDS_LOGIN_ACK_TOKEN);
2620: }
2621:
2622: /**
2623: * Process an proc id subpacket.
2624: * <p>
2625: * This routine assumes that the TDS_PROCID byte has already
2626: * been read.
2627: *
2628: * @exception com.internetcds.jdbc.tds.TdsException
2629: *
2630: * @exception java.io.IOException
2631: * Thrown if some sort of error occured reading bytes from the network.
2632: */
2633: private PacketResult processProcId() throws java.io.IOException,
2634: com.internetcds.jdbc.tds.TdsException {
2635: // XXX Try to find out what meaning this subpacket has.
2636: int i;
2637: byte tmp;
2638:
2639: for (i = 0; i < 8; i++) {
2640: tmp = comm.getByte();
2641: }
2642: return new PacketResult(TDS_PROCID);
2643: }
2644:
2645: /**
2646: * Process a TDS_RET_STAT_TOKEN subpacket.
2647: * <p>
2648: * This routine assumes that the TDS_RET_STAT_TOKEN
2649: * byte has already been read.
2650: *
2651: * @exception com.internetcds.jdbc.tds.TdsException
2652: *
2653: * @exception java.io.IOException
2654: * Thrown if some sort of error occured reading bytes from the network.
2655: */
2656: private PacketRetStatResult processRetStat()
2657: throws java.io.IOException,
2658: com.internetcds.jdbc.tds.TdsException {
2659: // XXX Not completely sure of this.
2660: return new PacketRetStatResult(comm.getTdsInt());
2661: }
2662:
2663: /**
2664: * Processes a TDS 7.0-style result packet, extracting column information
2665: * for the result set.
2666: *
2667: * Added 2000-06-05.
2668: */
2669: private PacketResult processTds7Result()
2670: throws java.io.IOException,
2671: com.internetcds.jdbc.tds.TdsException {
2672: int numColumns = comm.getTdsShort();
2673: Columns columns = new Columns();
2674:
2675: //try {
2676: //throw new Exception();
2677: //}
2678: //catch (Exception e) {
2679: //e.printStackTrace();
2680: //}
2681: for (int colNum = 1; colNum <= numColumns; ++colNum) {
2682:
2683: /*
2684: * The freetds C code didn't know what to do with these four
2685: * bytes, but initial inspection appears to tentatively confirm
2686: * that they serve the same purpose as the flag bytes read by the
2687: * Java code for 4.2 column information.
2688: */
2689: byte flagData[] = new byte[4];
2690: for (int i = 0; i < 4; i++)
2691: flagData[i] = comm.getByte();
2692: boolean nullable = (flagData[2] & 0x01) > 0;
2693: boolean writeable = (flagData[2] & 0x08) > 0;
2694: boolean autoIncrement = (flagData[2] & 0x10) > 0;
2695:
2696: /*
2697: * Get the type of column. Large types have 2-byte size fields and
2698: * type codes OR'd with 0x80. Except SYBNCHAR, whose type code
2699: * is already above 0x80.
2700: */
2701: int columnType = comm.getByte() & 0xFF;
2702: if (columnType == 0xEF)
2703: columnType = SYBNCHAR;
2704: int xColType = -1;
2705: if (isLargeType(columnType)) {
2706: xColType = columnType;
2707: if (columnType != SYBNCHAR)
2708: columnType -= 128;
2709: }
2710: // Determine the column size.
2711: int colSize;
2712: if (isBlobType(columnType)) {
2713:
2714: // Text and image columns have 4-byte size fields.
2715: colSize = comm.getTdsInt();
2716:
2717: // Swallow table name.
2718: comm.getString(comm.getTdsShort());
2719: }
2720:
2721: // Fixed types have no size field in the packet.
2722: else if (isFixedSizeColumn((byte) columnType))
2723: colSize = lookupColumnSize((byte) columnType);
2724:
2725: else if (isLargeType(xColType))
2726: colSize = comm.getTdsShort();
2727:
2728: else
2729: colSize = comm.getByte();
2730:
2731: // Get precision, scale for decimal types.
2732: int precision = -1;
2733: int scale = -1;
2734: if (columnType == SYBDECIMAL || columnType == SYBNUMERIC) {
2735: precision = comm.getByte();
2736: scale = comm.getByte();
2737: }
2738:
2739: /*
2740: * NB: under 7.0 lengths are number of characters, not number of
2741: * bytes. The getString() method handles this.
2742: */
2743: int colNameLen = comm.getByte();
2744: String columnName = comm.getString(colNameLen);
2745:
2746: // Populate the Column object.
2747: columns.setType(colNum, columnType);
2748: columns.setName(colNum, columnName);
2749: columns.setLabel(colNum, columnName);
2750: columns.setDisplaySize(colNum, colSize);
2751: columns.setNullable(colNum,
2752: (nullable ? ResultSetMetaData.columnNullable
2753: : ResultSetMetaData.columnNoNulls));
2754: columns.setAutoIncrement(colNum, autoIncrement);
2755: columns.setReadOnly(colNum, !writeable);
2756: if (precision != -1)
2757: columns.setPrecision(colNum, precision);
2758: if (scale != -1)
2759: columns.setScale(colNum, scale);
2760: }
2761: return new PacketColumnNamesResult(columns);
2762:
2763: } // processTds7Result()
2764:
2765: /**
2766: * Reports whether the type is for a large object. Name is a bit of a
2767: * misnomer, since it returns true for large text types, not just binary
2768: * objects (took it over from the freetds C code).
2769: */
2770: private static boolean isBlobType(int type) {
2771: return type == SYBTEXT || type == SYBIMAGE || type == SYBNTEXT;
2772: }
2773:
2774: /**
2775: * Reports whether the type uses a 2-byte size value.
2776: */
2777: private static boolean isLargeType(int type) {
2778: return type == SYBNCHAR || type > 128;
2779: }
2780:
2781: private void waitForDataOrTimeout(java.sql.Statement stmt,
2782: int timeout) throws java.io.IOException,
2783: com.internetcds.jdbc.tds.TdsException {
2784:
2785: // XXX How should this be syncrhonized?
2786: if (timeout == 0 || stmt == null) {
2787: comm.peek();
2788: } else {
2789: // start the timeout thread
2790: TimeoutHandler t = new TimeoutHandler(stmt, timeout);
2791:
2792: t.start();
2793:
2794: // wait until there is at least one byte of data
2795: comm.peek();
2796:
2797: // kill the timeout thread
2798: t.stop();
2799: t = null;
2800: }
2801: }
2802:
2803: /**
2804: * send a query to the SQLServer for execution.
2805: * <p>
2806: *
2807: * @param sql sql statement to execute.
2808: * @param stmt
2809: * @param timeout
2810: *
2811: * @exception com.internetcds.jdbc.tds.TdsException
2812: * @exception java.io.IOException
2813: */
2814: synchronized public void executeQuery(String sql,
2815: java.sql.Statement stmt, int timeout)
2816: throws java.io.IOException, java.sql.SQLException,
2817: TdsException {
2818: {
2819: cancelController.setQueryInProgressFlag();
2820: comm.startPacket(TdsComm.QUERY);
2821:
2822: if (tdsVer == Tds.TDS70) {
2823: comm.appendChars(sql);
2824: } else {
2825: byte[] sqlBytes = encoder.getBytes(sql);
2826: comm.appendBytes(sqlBytes, sqlBytes.length, (byte) 0);
2827: }
2828: moreResults2 = true; //JJ 1999-01-10
2829: comm.sendPacket();
2830:
2831: waitForDataOrTimeout(stmt, timeout);
2832: }
2833: }
2834:
2835: /**
2836: * skip over and discard any remaining data from a result set.
2837: *
2838: * @exception com.internetcds.jdbc.tds.TdsException
2839: * @exception java.io.IOException
2840: */
2841: synchronized public void discardResultSet(Columns columnsInfo)
2842: throws java.io.IOException,
2843: com.internetcds.jdbc.tds.TdsException {
2844: while (isResultRow()) {
2845: if (columnsInfo == null) {
2846: throw new com.internetcds.jdbc.tds.TdsConfused();
2847: }
2848: comm.skip(1);
2849: getRow(columnsInfo);
2850: }
2851:
2852: if (comm.peek() == TDS_DONEINPROC) {
2853: PacketResult tmp = processSubPacket();
2854: }
2855:
2856: // XXX Is there ever going to be a situation where the
2857: // TDS_DONEINPROC is the real end of data? If so then the
2858: // next section of code will hang forever waiting for more data
2859: // from the socket.
2860: if (isEndOfResults()) {
2861: processSubPacket();
2862: }
2863:
2864: // RMK 2000-06-08 Don't choke on additional result sets.
2865: else if (!isResultSet()) {
2866: throw new TdsConfused(
2867: "Was expecting an end of results token. "
2868: + "Found a 0x"
2869: + Integer.toHexString(comm.peek() & 0xff));
2870: }
2871: }
2872:
2873: synchronized public byte peek() throws java.io.IOException,
2874: com.internetcds.jdbc.tds.TdsException {
2875: return comm.peek();
2876: } // peek()
2877:
2878: /**
2879: * Determine if the next subpacket is a result set.
2880: * <p>
2881: * This does not eat any input.
2882: *
2883: * @return true if the next piece of data to read is a result set.
2884: *
2885: * @exception com.internetcds.jdbc.tds.TdsException
2886: * @exception java.io.IOException
2887: */
2888: synchronized public boolean isResultSet()
2889: throws com.internetcds.jdbc.tds.TdsException,
2890: java.io.IOException {
2891: byte type = comm.peek();
2892:
2893: /*
2894: * XXX to support 5.0 we need to expand our view of what a result
2895: * set is.
2896: */
2897: return type == TDS_COL_NAME_TOKEN || type == TDS7_RESULT_TOKEN;
2898: }
2899:
2900: /**
2901: * Determine if the next subpacket is a ret stat
2902: * <p>
2903: * This does not eat any input.
2904: *
2905: * @return true if the next piece of data to read is a result row.
2906: *
2907: * @exception com.internetcds.jdbc.tds.TdsException
2908: * @exception java.io.IOException
2909: */
2910: synchronized public boolean isRetStat()
2911: throws com.internetcds.jdbc.tds.TdsException,
2912: java.io.IOException {
2913: byte type = comm.peek();
2914:
2915: return type == TDS_RET_STAT_TOKEN;
2916: }
2917:
2918: /**
2919: * Determine if the next subpacket is a result row.
2920: * <p>
2921: * This does not eat any input.
2922: *
2923: * @return true if the next piece of data to read is a result row.
2924: *
2925: * @exception com.internetcds.jdbc.tds.TdsException
2926: * @exception java.io.IOException
2927: */
2928: synchronized public boolean isResultRow()
2929: throws com.internetcds.jdbc.tds.TdsException,
2930: java.io.IOException {
2931: byte type = comm.peek();
2932:
2933: return type == TDS_ROW_TOKEN;
2934: }
2935:
2936: /**
2937: * Determine if the next subpacket is an end of result set marker.
2938: * <p>
2939: * This does not eat any input.
2940: *
2941: * @return true if the next piece of data to read is end of result set
2942: * marker.
2943: *
2944: * @exception com.internetcds.jdbc.tds.TdsException
2945: * @exception java.io.IOException
2946: */
2947: synchronized public boolean isEndOfResults()
2948: throws com.internetcds.jdbc.tds.TdsException,
2949: java.io.IOException {
2950: byte type = comm.peek();
2951:
2952: return type == TDS_END_TOKEN || type == TDS_DONEPROC;
2953: }
2954:
2955: /**
2956: * Determine if the next subpacket is a DONEINPROC marker
2957: * <p>
2958: * This does not eat any input.
2959: *
2960: * @return
2961: *
2962: * @exception com.internetcds.jdbc.tds.TdsException
2963: * @exception java.io.IOException
2964: */
2965: synchronized public boolean isDoneInProc()
2966: throws com.internetcds.jdbc.tds.TdsException,
2967: java.io.IOException {
2968: byte type = comm.peek();
2969:
2970: return type == TDS_DONEINPROC;
2971: }
2972:
2973: /**
2974: * Determine if the next subpacket is a message packet
2975: * <p>
2976: * This does not eat any input.
2977: *
2978: * @return true if the next piece of data to read is message
2979: *
2980: * @exception com.internetcds.jdbc.tds.TdsException
2981: * @exception java.io.IOException
2982: */
2983: synchronized public boolean isMessagePacket()
2984: throws com.internetcds.jdbc.tds.TdsException,
2985: java.io.IOException {
2986: byte type = comm.peek();
2987: return type == TDS_MSG_TOKEN;
2988: }
2989:
2990: /**
2991: * Determine if the next subpacket is a text update packet
2992: * <p>
2993: * This does not eat any input.
2994: *
2995: * @return true if the next piece of data to read is text update
2996: *
2997: * @exception com.internetcds.jdbc.tds.TdsException
2998: * @exception java.io.IOException
2999: */
3000: synchronized public boolean isTextUpdate()
3001: throws com.internetcds.jdbc.tds.TdsException,
3002: java.io.IOException {
3003: byte type = comm.peek();
3004: return type == TDS_TEXT_UPD_TOKEN;
3005: }
3006:
3007: /**
3008: * Determine if the next subpacket is an error packet
3009: * <p>
3010: * This does not eat any input.
3011: *
3012: * @return true if the next piece of data to read is an error
3013: *
3014: * @exception com.internetcds.jdbc.tds.TdsException
3015: * @exception java.io.IOException
3016: */
3017: synchronized public boolean isErrorPacket()
3018: throws com.internetcds.jdbc.tds.TdsException,
3019: java.io.IOException {
3020: byte type = comm.peek();
3021: return type == TDS_ERR_TOKEN;
3022: }
3023:
3024: //sinisa
3025: /**
3026: * Determine if the next subpacket is an return status packet
3027: * <p>
3028: * This does not eat any input.
3029: *
3030: * @return true if the next piece of data to read is an return status
3031: *
3032: * @exception com.internetcds.jdbc.tds.TdsException
3033: * @exception java.io.IOException
3034: */
3035: synchronized public boolean isReturnStatus()
3036: throws com.internetcds.jdbc.tds.TdsException,
3037: java.io.IOException {
3038: byte type = comm.peek();
3039: return type == TDS_RET_STAT_TOKEN;
3040: }
3041:
3042: /**
3043: * Determine if the next subpacket is an procid subpacket
3044: * <p>
3045: * This does not eat any input.
3046: *
3047: * @return true if the next piece of data to read is end of result set
3048: * marker.
3049: *
3050: * @exception com.internetcds.jdbc.tds.TdsException
3051: * @exception java.io.IOException
3052: */
3053: synchronized public boolean isProcId()
3054: throws com.internetcds.jdbc.tds.TdsException,
3055: java.io.IOException {
3056:
3057: byte type = comm.peek();
3058: return type == TDS_PROCID;
3059: }
3060:
3061: /**
3062: * Accessor method to determine the TDS level used.
3063: *
3064: * @return TDS42, TDS50, or TDS70.
3065: */
3066: int getTdsVer() {
3067: return tdsVer;
3068: }
3069:
3070: /**
3071: * Return the name that this database server program calls itself.
3072: */
3073: String getDatabaseProductName() {
3074: return databaseProductName;
3075: }
3076:
3077: /**
3078: * Return the name that this database server program calls itself.
3079: */
3080: String getDatabaseProductVersion() {
3081: return databaseProductVersion;
3082: }
3083: }
|