View Javadoc
1 // 2 // Copyright 1998, 1999 CDS Networks, Inc., Medford Oregon 3 // 4 // All rights reserved. 5 // 6 // Redistribution and use in source and binary forms, with or without 7 // modification, are permitted provided that the following conditions are met: 8 // 1. Redistributions of source code must retain the above copyright 9 // notice, this list of conditions and the following disclaimer. 10 // 2. Redistributions in binary form must reproduce the above copyright 11 // notice, this list of conditions and the following disclaimer in the 12 // documentation and/or other materials provided with the distribution. 13 // 3. All advertising materials mentioning features or use of this software 14 // must display the following acknowledgement: 15 // This product includes software developed by CDS Networks, Inc. 16 // 4. The name of CDS Networks, Inc. may not be used to endorse or promote 17 // products derived from this software without specific prior 18 // written permission. 19 // 20 // THIS SOFTWARE IS PROVIDED BY CDS NETWORKS, INC. ``AS IS'' AND 21 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 // ARE DISCLAIMED. IN NO EVENT SHALL CDS NETWORKS, INC. BE LIABLE 24 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 // SUCH DAMAGE. 31 // 32 33 34 35 36 package com.internetcds.jdbc.tds; 37 38 import java.net.Socket; 39 import java.util.Vector; 40 import java.lang.Thread; 41 import java.util.StringTokenizer; 42 import java.sql.*; 43 import com.internetcds.jdbc.tds.TdsComm; 44 import com.internetcds.util.Logger; 45 import java.math.BigInteger; 46 import java.math.BigDecimal; 47 import java.util.Calendar; 48 import java.util.Properties; 49 import java.util.TimeZone; 50 import java.util.Locale; 51 52 53 54 /*** 55 * Cancel the current SQL if the timeout expires. 56 * 57 * @version $Id: Tds.html,v 1.1 2003/05/12 16:19:44 sinisa Exp $ 58 * @author Craig Spannring 59 */ 60 class TimeoutHandler extends Thread 61 { 62 public static final String cvsVersion = "$Id: Tds.html,v 1.1 2003/05/12 16:19:44 sinisa Exp $"; 63 64 java.sql.Statement stmt; 65 int timeout; 66 67 68 public TimeoutHandler( 69 java.sql.Statement stmt_, 70 int timeout_) 71 { 72 stmt = stmt_; 73 timeout = timeout_; 74 } 75 76 77 public void run() 78 { 79 try 80 { 81 sleep(timeout * 1000); 82 stmt.cancel(); 83 } 84 catch(SQLException e) 85 { 86 // nop 87 } 88 catch(java.lang.InterruptedException e) 89 { 90 // nop 91 } 92 } 93 } 94 95 96 97 /*** 98 * Implement the TDS protocol. 99 * 100 * @version $Id: Tds.html,v 1.1 2003/05/12 16:19:44 sinisa Exp $ 101 * @author Craig Spannring 102 * @author Igor Petrovski 103 * @author The FreeTDS project 104 */ 105 public class Tds implements TdsDefinitions 106 { 107 public static final String cvsVersion = "$Id: Tds.html,v 1.1 2003/05/12 16:19:44 sinisa Exp $"; 108 109 110 // 111 // If the following variable is false we will consider calling 112 // unimplemented methods to be an error and will raise an exception. 113 // If you want to ignore any unimplemented methods, set it to 114 // true. Only do this if you know what you are doing and can tolerate 115 // bogus results from the unimplemented methods. 116 // 117 static boolean ignoreNotImplemented = false; 118 119 120 Socket sock = null; 121 TdsComm comm = null; 122 123 String databaseProductName; 124 String databaseProductVersion; 125 126 java.sql.Connection connection; 127 String host; 128 int serverType = -1; // either Tds.SYBASE or Tds.SQLSERVER 129 int port; // Port numbers are _unsigned_ 16 bit, short is too small 130 String database; 131 String user; 132 String password; 133 String appName; 134 String serverName; 135 String progName; 136 byte progMajorVersion; 137 byte progMinorVersion; 138 139 boolean haveProcNameTable = false; 140 String procNameGeneratorName = null; 141 String procNameTableName = null; 142 143 String initialSettings = null; 144 145 private Properties initialProps = null; 146 private EncodingHelper encoder = null; 147 private String charset = null; 148 149 // int rowCount = -1; // number of rows selected or updated 150 private boolean moreResults = false; // Is there another result set? 151 152 // Jens Jakobsen 1999-01-10 153 // Until TDS_END_TOKEN is reached we assume that there are outstanding 154 // UpdateCounts or ResultSets 155 private boolean moreResults2 = true; 156 157 CancelController cancelController = null; 158 159 SqlMessage lastServerMessage = null; 160 161 // Added 2000-06-07. Used to control TDS version-specific behavior. 162 private int tdsVer = Tds.TDS42; 163 164 // RMK 2000-06-08. 165 private boolean showWarnings = false; 166 167 // RMK 2000-06-12. Time zone offset on client (disregarding DST). 168 private int zoneOffset = Calendar.getInstance().get(Calendar.ZONE_OFFSET); 169 170 public Tds( 171 java.sql.Connection connection_, 172 Properties props_, 173 String initialSettings) 174 throws java.io.IOException, java.net.UnknownHostException, 175 java.sql.SQLException, com.internetcds.jdbc.tds.TdsException 176 { 177 connection = (java.sql.Connection)connection_; 178 initialProps = props_; 179 180 host = props_.getProperty("HOST"); 181 serverType = Integer.parseInt(props_.getProperty("SERVERTYPE")); 182 port = Integer.parseInt(props_.getProperty("PORT")); 183 database = props_.getProperty("DBNAME"); 184 user = props_.getProperty("user"); 185 password = props_.getProperty("password"); 186 appName = props_.getProperty("APPNAME", "jdbclib"); 187 serverName = props_.getProperty("SERVERNAME", host); 188 progName = props_.getProperty("PROGNAME", "java_app"); 189 progMajorVersion = (byte)DriverVersion.getDriverMajorVersion(); 190 progMinorVersion = (byte)DriverVersion.getDriverMinorVersion(); 191 // String verString = props_.getProperty("TDS", "42"); 192 String verString = props_.getProperty("TDS", "7.0"); 193 194 // XXX This driver doesn't properly support TDS 5.0, AFAIK. 195 // Added 2000-06-07. 196 197 198 tdsVer = TDS42; 199 if (verString.equals("5.0")) 200 { 201 tdsVer = Tds.TDS50; 202 } 203 else if (verString.equals("7.0")) 204 { 205 tdsVer = Tds.TDS70; 206 } 207 // RMK 2000-06-08 208 if (System.getProperty("TDS_SHOW_WARNINGS") != null 209 || 210 props_.getProperty("TDS_SHOW_WARNINGS") != null) 211 { 212 showWarnings = true; 213 } 214 215 cancelController = new CancelController(); 216 217 // Send the logon packet to the server 218 sock = new Socket(host, port); 219 sock.setTcpNoDelay(true); 220 comm = new TdsComm(sock, tdsVer); 221 222 setCharset(props_.getProperty("CHARSET")); 223 224 if (logon()) 225 { 226 // everything is hunky-dory. 227 } 228 else 229 { 230 throw new SQLException("Logon failed. " + lastServerMessage); 231 } 232 } 233 234 private void setCharset(String charset) 235 { 236 try 237 { 238 Logger.println("Trying to change charset to " + charset); 239 } 240 catch(java.io.IOException e) 241 { 242 // nop 243 } 244 245 if (charset == null || charset.length() > 30) 246 { 247 charset = "iso_1"; 248 } 249 250 if (!charset.equals(this.charset)) 251 { 252 encoder = EncodingHelper.getHelper(charset); 253 if (encoder == null) 254 { 255 charset = "iso_1"; 256 encoder = EncodingHelper.getHelper(charset); 257 } 258 this.charset = charset; 259 } 260 } 261 262 EncodingHelper getEncoder() { 263 return encoder; 264 } 265 266 public void close() 267 { 268 comm.close(); 269 try 270 { 271 sock.close(); 272 } 273 catch(java.io.IOException e) 274 { 275 // XXX should do something here 276 } 277 } 278 279 static private int toUInt(byte b) 280 { 281 int result = ((int)b) & 0x00ff; 282 return result; 283 } 284 285 public String toString() 286 { 287 return "" 288 + database + ", " 289 + sock.getLocalAddress() + ":" + sock.getLocalPort() 290 + " -> " + sock.getInetAddress() + ":" + sock.getPort(); 291 } 292 293 294 /*** 295 * Convert a JDBC escaped SQL string into the native SQL 296 * 297 * @param input escaped string to convert 298 * 299 * @return native SQL string 300 */ 301 static public String toNativeSql(String input, int serverType) 302 throws SQLException 303 { 304 EscapeProcessor escape; 305 if (serverType==TdsDefinitions.SYBASE) 306 { 307 escape = new SybaseEscapeProcessor(input); 308 } 309 else 310 { 311 escape = new MSSqlServerEscapeProcessor(input); 312 } 313 314 return escape.nativeString(); 315 } 316 317 318 /*** 319 * Convert a JDBC java.sql.Types identifier to a SQLServer type identifier 320 * 321 * @author Craig Spannring 322 * 323 * @param jdbcType JDBC type to convert. Should be one of the 324 * constants from java.sql.Types. 325 * 326 * @return The corresponding SQLServer type identifier. 327 */ 328 public static byte cvtJdbcTypeToNativeType(int jdbcType) 329 throws TdsNotImplemented 330 { 331 // This function is thread safe. 332 byte result = 0; 333 switch(jdbcType) 334 { 335 case java.sql.Types.CHAR: 336 // case java.sql.Types.VARCHAR: 337 // case java.sql.Types.LONGVARCHAR: 338 { 339 result = SYBCHAR; 340 break; 341 } 342 //Sinisa 343 //Add java.sql.types VARCHAR & LONGVARCHAR as a native type SYBVARCHAR 344 case java.sql.Types.VARCHAR: 345 case java.sql.Types.LONGVARCHAR: 346 { 347 result = SYBVARCHAR; 348 break; 349 } 350 //Sinisa 351 //Add java.sql.types VARCHAR & LONGVARCHAR as a native type SYBFLT8 352 case java.sql.Types.DECIMAL: 353 { 354 result = SYBFLT8; 355 break; 356 } 357 case java.sql.Types.INTEGER: 358 case java.sql.Types.SMALLINT: 359 case java.sql.Types.BIGINT: 360 { 361 result = SYBINT4; 362 break; 363 } 364 case java.sql.Types.REAL: 365 case java.sql.Types.DOUBLE: 366 { 367 result = SYBFLT8; 368 break; 369 } 370 case java.sql.Types.DATE: 371 case java.sql.Types.TIMESTAMP: 372 case java.sql.Types.TIME: 373 { 374 result = SYBDATETIMN; 375 break; 376 } 377 case java.sql.Types.VARBINARY: 378 case java.sql.Types.LONGVARBINARY: 379 { 380 result = SYBIMAGE; 381 break; 382 } 383 //Dusan 384 case java.sql.Types.BIT: 385 { 386 result = SYBBIT; 387 break; 388 } 389 default: 390 { 391 throw new TdsNotImplemented("cvtJdbcTypeToNativeType (" 392 + TdsUtil.javaSqlTypeToString(jdbcType) + ")"); 393 } 394 } 395 396 return result; 397 } 398 399 400 /*** 401 * Convert a JDBC java.sql.Types identifier to a 402 * SQLServer type identifier 403 * 404 * @author Craig Spannring 405 * 406 * @param nativeType SQLServer type to convert. 407 * @param size Maximum size of data coming back from server. 408 * 409 * @return The corresponding JDBC type identifier. 410 */ 411 public static int cvtNativeTypeToJdbcType(int nativeType, 412 int size) 413 throws TdsException 414 { 415 416 // This function is thread safe. 417 418 int result = java.sql.Types.OTHER; 419 switch(nativeType) 420 { 421 // XXX We need to figure out how to map _all_ of these types 422 case SYBBINARY: result = java.sql.Types.BINARY; break; 423 case SYBBIT: result = java.sql.Types.BIT; break; 424 case SYBBITN: result = java.sql.Types.BIT; break; 425 case SYBCHAR: result = java.sql.Types.CHAR; break; 426 case SYBNCHAR: result = java.sql.Types.CHAR; break; 427 case SYBDATETIME4: result = java.sql.Types.TIMESTAMP; break; 428 case SYBDATETIME: result = java.sql.Types.TIMESTAMP; break; 429 case SYBDATETIMN: result = java.sql.Types.TIMESTAMP; break; 430 case SYBDECIMAL: result = java.sql.Types.DECIMAL; break; 431 case SYBNUMERIC: result = java.sql.Types.NUMERIC; break; 432 case SYBFLT8: result = java.sql.Types.DOUBLE; break; 433 case SYBFLTN: result = java.sql.Types.DOUBLE; break; 434 case SYBINT1: result = java.sql.Types.TINYINT; break; 435 case SYBINT2: result = java.sql.Types.SMALLINT; break; 436 case SYBINT4: result = java.sql.Types.INTEGER; break; 437 case SYBINTN: 438 { 439 switch (size) 440 { 441 case 1: result = java.sql.Types.TINYINT; break; 442 case 2: result = java.sql.Types.SMALLINT; break; 443 case 4: result = java.sql.Types.INTEGER; break; 444 default: throw new TdsException("Bad size of SYBINTN"); 445 } 446 break; 447 } 448 // XXX Should money types by NUMERIC or OTHER? 449 case SYBSMALLMONEY: result = java.sql.Types.NUMERIC; break; 450 case SYBMONEY4: result = java.sql.Types.NUMERIC; break; 451 case SYBMONEY: result = java.sql.Types.NUMERIC; break; 452 case SYBMONEYN: result = java.sql.Types.NUMERIC; break; 453 // case SYBNUMERIC: result = java.sql.Types.NUMERIC; break; 454 case SYBREAL: result = java.sql.Types.REAL; break; 455 case SYBTEXT: result = java.sql.Types.LONGVARCHAR; break; 456 case SYBNTEXT: result = java.sql.Types.LONGVARCHAR; break; 457 case SYBIMAGE: result = java.sql.Types.VARBINARY; break; 458 case SYBVARBINARY: result = java.sql.Types.VARBINARY; break; 459 case SYBVARCHAR: result = java.sql.Types.VARCHAR; break; 460 case SYBNVARCHAR: result = java.sql.Types.VARCHAR; break; 461 // case SYBVOID: result = java.sql.Types. ; break; 462 default: throw new TdsException("Unknown native data type " 463 + Integer.toHexString( 464 nativeType&0xff)); 465 } 466 return result; 467 } /* cvtNativeTypeToJdbcType() */ 468 469 470 /*** 471 * Return the type of server that we attempted to connect to. 472 * 473 * @return TdsDefinitions.SYBASE or TdsDefinitions.SQLSERVER 474 */ 475 public int getServerType() 476 { 477 return serverType; 478 } 479 480 481 /*** 482 * Try to figure out what client name we should identify 483 * ourselves as. Get the hostname of this machine, 484 * 485 * @return name we will use as the client. 486 */ 487 private String getClientName() 488 { 489 // This method is thread safe. 490 String tmp; 491 try 492 { 493 tmp = java.net.InetAddress.getLocalHost().getHostName(); 494 } 495 catch(java.net.UnknownHostException e) 496 { 497 tmp = ""; 498 } 499 StringTokenizer st = new StringTokenizer(tmp, "."); 500 501 502 503 if (!st.hasMoreTokens()) 504 { 505 // This means hostname wasn't found for this machine. 506 return "JOHNDOE"; 507 } 508 509 // Look at the first (and possibly only) word in the name. 510 tmp = st.nextToken(); 511 if (tmp.length()==0) 512 { 513 // This means the hostname had no leading component. 514 // (This case would be strange.) 515 return "JANEDOE"; 516 } 517 else if (Character.isDigit(tmp.charAt(0))) 518 { 519 // This probably means that the name was a quad-decimal 520 // number. We don't want to send that as our name, 521 // so make one up. 522 return "BABYDOE"; 523 } 524 else 525 { 526 // Ah, Life is good. We have a name. All other 527 // applications I've seen have upper case client names, 528 // and so shall we. 529 return tmp.toUpperCase(); 530 } 531 } 532 533 534 /*** 535 * Log onto the SQLServer 536 * <p> 537 * 538 * This method is not synchronized and does not need to be so long 539 * as it can only be called from the constructor. 540 * 541 * <p> 542 * <U>Login Packet</U> 543 * <P> 544 * Packet type (first byte) is 2. The following is from tds.h the numbers 545 * on the left are offsets <I>not including</I> the packet header. 546 * <br> 547 * Note: The logical logon packet is split into two physical 548 * packets. Each physical packet has its own header. 549 * <br> 550 * <PRE> 551 * -- 0 -- DBCHAR host_name[30]; 552 * -- 30 -- DBTINYINT host_name_length; 553 * -- 31 -- DBCHAR user_name[30]; 554 * -- 61 -- DBTINYINT user_name_length; 555 * -- 62 -- DBCHAR password[30]; 556 * -- 92 -- DBTINYINT password_length; 557 * -- 93 -- DBCHAR host_process[30]; 558 * -- 123 -- DBTINYINT host_process_length; 559 * -- 124 -- DBCHAR magic1[6]; -- here were most of the mystery stuff is -- 560 * -- 130 -- DBTINYINT bulk_copy; 561 * -- 131 -- DBCHAR magic2[9]; -- here were most of the mystery stuff is -- 562 * -- 140 -- DBCHAR app_name[30]; 563 * -- 170 -- DBTINYINT app_name_length; 564 * -- 171 -- DBCHAR server_name[30]; 565 * -- 201 -- DBTINYINT server_name_length; 566 * -- 202 -- DBCHAR magic3; -- 0, dont know this one either -- 567 * -- 203 -- DBTINYINT password2_length; 568 * -- 204 -- DBCHAR password2[30]; 569 * -- 234 -- DBCHAR magic4[223]; 570 * -- 457 -- DBTINYINT password2_length_plus2; 571 * -- 458 -- DBSMALLINT major_version; -- TDS version -- 572 * -- 460 -- DBSMALLINT minor_version; -- TDS version -- 573 * -- 462 -- DBCHAR library_name[10]; -- Ct-Library or DB-Library -- 574 * -- 472 -- DBTINYINT library_length; -- Ct-Library or DB-Library -- 575 * -- 473 -- DBSMALLINT major_version2; -- program version -- 576 * -- 475 -- DBSMALLINT minor_version2; -- program version -- 577 * -- 477 -- DBCHAR magic6[3]; -- ? last two octets are 13 and 17 -- 578 * -- bdw reports last two as 12 and 16 here -- 579 * -- possibly a bitset flag -- 580 * -- 480 -- DBCHAR language[30]; -- ie us-english -- 581 * -- second packet -- 582 * -- 524 -- DBTINYINT language_length; -- 10 in this case -- 583 * -- 525 -- DBCHAR magic7; -- no clue... has 1 in the first octet -- 584 * -- bdw reports 0x0 -- 585 * -- 526 -- DBSMALLINT old_secure; -- explaination? -- 586 * -- 528 -- DBTINYINT encrypted; -- 1 means encrypted all password fields blank -- 587 * -- 529 -- DBCHAR magic8; -- no clue... zeros -- 588 * -- 530 -- DBCHAR sec_spare[9]; -- explaination -- 589 * -- 539 -- DBCHAR char_set[30]; -- ie iso_1 -- 590 * -- 569 -- DBTINYINT char_set_length; -- 5 -- 591 * -- 570 -- DBTINYINT magic9; -- 1 -- 592 * -- 571 -- DBCHAR block_size[6]; -- in text -- 593 * -- 577 -- DBTINYINT block_size_length; 594 * -- 578 -- DBCHAR magic10[25]; -- lots of stuff here...no clue -- 595 * 596 * </PRE> 597 * 598 * This routine will basically eat all of the data returned from the 599 * SQLServer. 600 * 601 * @author Craig Spannring 602 * 603 * @exception TdsUnknownPacketSubType 604 * @exception com.internetcds.jdbc.tds.TdsException 605 * @exception java.io.IOException 606 * @exception java.sql.SQLException 607 */ 608 private boolean logon() 609 throws java.sql.SQLException, 610 TdsUnknownPacketSubType, java.io.IOException, 611 com.internetcds.jdbc.tds.TdsException 612 { 613 boolean isOkay = true; 614 byte pad = (byte) 0; 615 byte[] empty = new byte[0]; 616 617 // Added 2000-06-07. 618 if (tdsVer == Tds.TDS70) 619 send70Login(); 620 else { 621 622 comm.startPacket(TdsComm.LOGON); 623 624 // hostname (offset0) 625 // comm.appendString("TOLEDO", 30, (byte)0); 626 byte[] tmp = encoder.getBytes(getClientName()); 627 comm.appendBytes(tmp, 30, pad); 628 comm.appendByte((byte)(tmp.length < 30 ? tmp.length : 30)); 629 630 // username (offset 31 0x1f) 631 tmp = encoder.getBytes(user); 632 comm.appendBytes(tmp, 30, pad); 633 comm.appendByte((byte)(tmp.length < 30 ? tmp.length : 30)); 634 635 // password (offset 62 0x3e) 636 tmp = encoder.getBytes(password); 637 comm.appendBytes(tmp, 30, pad); 638 comm.appendByte((byte)(tmp.length < 30 ? tmp.length : 30)); 639 640 // hostproc (offset 93 0x5d) 641 tmp = encoder.getBytes("00000116"); 642 comm.appendBytes(tmp, 8, pad); 643 644 // unused (offset 109 0x6d) 645 comm.appendBytes(empty, (30-14), pad); 646 647 // apptype (offset ) 648 comm.appendByte((byte)0x0); 649 comm.appendByte((byte)0xA0); 650 comm.appendByte((byte)0x24); 651 comm.appendByte((byte)0xCC); 652 comm.appendByte((byte)0x50); 653 comm.appendByte((byte)0x12); 654 655 // hostproc length (offset ) 656 comm.appendByte((byte)8); 657 658 // type of int2 659 comm.appendByte((byte)3); 660 661 // type of int4 662 comm.appendByte((byte)1); 663 664 // type of char 665 comm.appendByte((byte)6); 666 667 // type of flt 668 comm.appendByte((byte)10); 669 670 // type of date 671 comm.appendByte((byte)9); 672 673 // notify of use db 674 comm.appendByte((byte)1); 675 676 // disallow dump/load and bulk insert 677 comm.appendByte((byte)1); 678 679 // sql interface type 680 comm.appendByte((byte)0); 681 682 // type of network connection 683 comm.appendByte((byte)0); 684 685 // spare[7] 686 comm.appendBytes(empty, 7, pad); 687 688 // appname 689 tmp = encoder.getBytes(appName); 690 comm.appendBytes(tmp, 30, pad); 691 comm.appendByte((byte)(tmp.length < 30 ? tmp.length : 30)); 692 693 // server name 694 tmp = encoder.getBytes(serverName); 695 comm.appendBytes(tmp, 30, pad); 696 comm.appendByte((byte)(tmp.length < 30 ? tmp.length : 30)); 697 698 // remote passwords 699 comm.appendBytes(empty, 2, pad); 700 tmp = encoder.getBytes(password); 701 comm.appendBytes(tmp, 253, pad); 702 comm.appendByte((byte)(tmp.length < 253 ? tmp.length+2 : 253+2)); 703 704 // tds version 705 comm.appendByte((byte)4); 706 comm.appendByte((byte)2); 707 comm.appendByte((byte)0); 708 comm.appendByte((byte)0); 709 710 // prog name 711 tmp = encoder.getBytes(progName); 712 comm.appendBytes(tmp, 10, pad); 713 comm.appendByte((byte)(tmp.length < 10 ? tmp.length : 10)); 714 715 // prog version 716 comm.appendByte((byte) 6); // Tell the server we can handle SQLServer version 6 717 comm.appendByte((byte) 0); // Send zero to tell the server we can't handle any other version 718 comm.appendByte((byte) 0); 719 comm.appendByte((byte) 0); 720 721 // auto convert short 722 comm.appendByte((byte)0); 723 724 // type of flt4 725 comm.appendByte((byte)0x0D); 726 727 // type of date4 728 comm.appendByte((byte)0x11); 729 730 // language 731 tmp = encoder.getBytes("us_english"); 732 comm.appendBytes(tmp, 30, pad); 733 comm.appendByte((byte)(tmp.length < 30 ? tmp.length : 30)); 734 735 // notify on lang change 736 comm.appendByte((byte)1); 737 738 // security label hierachy 739 comm.appendShort((short)0); 740 741 // security components 742 comm.appendBytes(empty, 8, pad); 743 744 // security spare 745 comm.appendShort((short)0); 746 747 // security login role 748 comm.appendByte((byte)0); 749 750 // charset 751 tmp = encoder.getBytes(charset); 752 comm.appendBytes(tmp, 30, pad); 753 comm.appendByte((byte)(tmp.length < 30 ? tmp.length : 30)); 754 755 // notify on charset change 756 comm.appendByte((byte)1); 757 758 // length of tds packets 759 tmp = encoder.getBytes("512"); 760 comm.appendBytes(tmp, 6, pad); 761 comm.appendByte((byte)3); 762 763 // pad out to a longword 764 comm.appendBytes(empty, 8, pad); 765 766 moreResults2=true; //JJ 1999-01-10 767 } 768 769 comm.sendPacket(); 770 771 // Get the reply to the logon packet. 772 PacketResult result; 773 774 while (! ((result = processSubPacket()) instanceof PacketEndTokenResult)) 775 { 776 if (result instanceof PacketErrorResult) 777 { 778 isOkay = false; 779 } 780 // XXX Should really process some more types of packets. 781 } 782 783 784 if (isOkay) 785 { 786 // XXX Should we move this to the Connection class? 787 isOkay = changeSettings(database, initialSettings); 788 } 789 790 // XXX Possible bug. What happend if this is cancelled before the logon 791 // takes place? Should isOkay be false? 792 return isOkay; 793 } 794 795 796 /* 797 * New code added to handle TDS 7.0 login, which uses a completely 798 * different packet layout. Logic taken directly from freetds C 799 * code in tds/login.c. Lots of magic values: I don't pretend 800 * to have any idea what most of this means. 801 * 802 * Added 2000-06-05. 803 */ 804 private void send70Login() throws java.io.IOException { 805 806 byte[] magic1 = {(byte)0006, (byte)0203, (byte)0362, (byte)0370, 807 (byte)0377, (byte)0000, (byte)0000, (byte)0000, 808 (byte)0000, (byte)0340, (byte)0003, (byte)0000, 809 (byte)0000, (byte)0210, (byte)0377, (byte)0377, 810 (byte)0377, (byte)0066, (byte)0004, (byte)0000, 811 (byte)0000}; 812 byte[] magic2 = {(byte)0000, (byte)0100, (byte)0063, (byte)0232, 813 (byte)0153, (byte)0120}; 814 byte[] magic3 = encoder.getBytes("NTLMSSP"); 815 String libName = "DB-Library"; 816 byte pad = (byte)0; 817 byte[] empty = new byte[0]; 818 String appName = "CDR"; 819 short len = (short)(86 + 2 * (user.length() + 820 password.length() + 821 appName.length() + 822 serverName.length() + 823 libName.length())); 824 short packSize = (short)(len + 48); 825 comm.startPacket(TdsComm.LOGON70); 826 comm.appendTdsShort(packSize); 827 comm.appendBytes(empty, 5, pad); 828 comm.appendByte((byte)0x70); 829 comm.appendBytes(empty, 7, pad); 830 comm.appendBytes(magic1, 21, pad); 831 832 // Pack up value lengths, positions. 833 short curPos = 86; 834 835 // Unknown 836 comm.appendTdsShort(curPos); 837 comm.appendTdsShort((short)0); 838 839 // Username 840 comm.appendTdsShort(curPos); 841 comm.appendTdsShort((short)user.length()); 842 curPos += user.length() * 2; 843 844 // Password 845 comm.appendTdsShort(curPos); 846 comm.appendTdsShort((short)password.length()); 847 curPos += password.length() * 2; 848 849 // App name 850 comm.appendTdsShort(curPos); 851 comm.appendTdsShort((short)appName.length()); 852 curPos += appName.length() * 2; 853 854 // Server name 855 comm.appendTdsShort(curPos); 856 comm.appendTdsShort((short)serverName.length()); 857 curPos += serverName.length() * 2; 858 859 // Another unknown value 860 comm.appendTdsShort((short)0); 861 comm.appendTdsShort((short)0); 862 863 // Library name 864 comm.appendTdsShort(curPos); 865 comm.appendTdsShort((short)libName.length()); 866 curPos += libName.length() * 2; 867 868 // Two more unknowns 869 comm.appendTdsShort(curPos); 870 comm.appendTdsShort((short)0); 871 comm.appendTdsShort(curPos); 872 comm.appendTdsShort((short)0); 873 874 // More magic. 875 comm.appendBytes(magic2, 6, pad); 876 comm.appendTdsShort(len); 877 comm.appendTdsShort((short)0x30); 878 comm.appendTdsShort(packSize); 879 comm.appendTdsShort((short)0); 880 881 // Pack up the login values. 882 String scrambledPw = tds7CryptPass(password); 883 comm.appendChars(user); 884 comm.appendChars(scrambledPw); 885 comm.appendChars(appName); 886 comm.appendChars(serverName); 887 comm.appendChars(libName); 888 889 // Still more magic! 890 comm.appendBytes(magic3, 7, pad); 891 comm.appendByte((byte)0); 892 comm.appendByte((byte)1); 893 comm.appendBytes(empty, 3, pad); 894 comm.appendByte((byte)6); 895 comm.appendByte((byte)130); 896 comm.appendBytes(empty, 22, pad); 897 comm.appendByte((byte)48); 898 comm.appendBytes(empty, 7, pad); 899 comm.appendByte((byte)48); 900 comm.appendBytes(empty, 3, pad); 901 } 902 903 /*** 904 * This is a <B>very</B> poor man's "encryption." 905 */ 906 private static String tds7CryptPass(String pw) { 907 int xormask = 0x5A5A; 908 int len = pw.length(); 909 char[] chars = new char[len]; 910 for (int i = 0; i < len; ++i) { 911 int c = (int)(pw.charAt(i)) ^ xormask; 912 int m1 = (c >> 4) & 0x0F0F; 913 int m2 = (c << 4) & 0xF0F0; 914 chars[i] = (char)(m1 | m2); 915 } 916 return new String(chars); 917 } 918 919 /*** 920 * change the connection level settings for this connection 921 * stream to the database. 922 * 923 * @return true if the database accepted the changes, false if rejected. 924 */ 925 synchronized public boolean changeSettings( 926 String database, 927 String settings) 928 throws java.sql.SQLException 929 { 930 boolean isOkay = true; 931 try 932 { 933 PacketResult result; 934 935 if (database != null) 936 { 937 isOkay = changeDB(database); 938 } 939 940 if (isOkay && (settings!=null && settings.length()>0)) 941 { 942 String query = settings; 943 comm.startPacket(TdsComm.QUERY); 944 if (tdsVer == Tds.TDS70) 945 comm.appendChars(query); 946 else 947 { 948 byte[] queryBytes = encoder.getBytes(query); 949 comm.appendBytes(queryBytes, queryBytes.length, (byte)0); 950 } 951 moreResults2=true; //JJ 1999-01-10 952 comm.sendPacket(); 953 954 boolean done = false; 955 while (! done) 956 { 957 result = processSubPacket(); 958 done = ( result instanceof PacketEndTokenResult ) && 959 ! ((PacketEndTokenResult)result).moreResults() ; 960 if (result instanceof PacketErrorResult) 961 { 962 isOkay = false; 963 } 964 // XXX Should really process some more types of packets. 965 } 966 } 967 } 968 catch (com.internetcds.jdbc.tds.TdsUnknownPacketSubType e) 969 { 970 throw new SQLException("Unknown response. " + e.getMessage()); 971 } 972 catch (java.io.IOException e) 973 { 974 throw new SQLException("Network problem. " + e.getMessage()); 975 } 976 catch (com.internetcds.jdbc.tds.TdsException e) 977 { 978 throw new SQLException(e.getMessage()); 979 } 980 return isOkay; 981 } // changeSettings 982 983 /*** 984 * Select a new database to use. 985 * 986 * @param database Name of the database to use. 987 * 988 * @return true if the change was accepted, false otherwise 989 */ 990 synchronized private boolean changeDB(String database) 991 throws java.sql.SQLException 992 { 993 boolean isOkay = true;; 994 995 try 996 { 997 PacketResult result; 998 int i; 999 1000 // XXX Check to make sure the database name 1001 // doesn't have funny characters. 1002 1003 1004 // if (database name has funny characters) 1005 if (database.length()>32) 1006 { 1007 throw new SQLException("Name too long " + database); 1008 } 1009 1010 for(i=0; i<database.length(); i++) 1011 { 1012 char ch; 1013 ch = database.charAt(i); 1014 if (! 1015 ((ch=='_' && i!=0) 1016 || (ch >= 'a' && ch<='z') 1017 || (ch >= 'A' && ch<='Z') 1018 || (ch >='0' && ch<='9'))) 1019 { 1020 throw new SQLException("Bad database name- " 1021 + database); 1022 } 1023 } 1024 1025 String query = "use " + database; 1026 comm.startPacket(TdsComm.QUERY); 1027 if (tdsVer == Tds.TDS70) 1028 comm.appendChars(query); 1029 else 1030 { 1031 byte[] queryBytes = encoder.getBytes(query); 1032 comm.appendBytes(queryBytes, queryBytes.length, (byte)0); 1033 } 1034 moreResults2=true; //JJ 1999-01-10 1035 comm.sendPacket(); 1036 1037 // XXX Should we check that the change actual was okay 1038 // and throw some sort of exception if it wasn't? 1039 // Get the reply to the change database request. 1040 while (! ((result = processSubPacket()) 1041 instanceof PacketEndTokenResult)) 1042 { 1043 if (result instanceof PacketErrorResult) 1044 { 1045 isOkay = false; 1046 } 1047 // XXX Should really process some more types of packets. 1048 } 1049 } 1050 catch (com.internetcds.jdbc.tds.TdsUnknownPacketSubType e) 1051 { 1052 throw new SQLException("Unknown response. " + e.getMessage()); 1053 } 1054 catch (java.io.IOException e) 1055 { 1056 throw new SQLException("Network problem. " + e.getMessage()); 1057 } 1058 catch (com.internetcds.jdbc.tds.TdsException e) 1059 { 1060 throw new SQLException(e.getMessage()); 1061 } 1062 1063 return isOkay; 1064 } // changeDB() 1065 1066 1067 public void cancel() 1068 throws java.io.IOException, com.internetcds.jdbc.tds.TdsException 1069 { 1070 // XXX How should this be synchronized? What sort of deadlock 1071 // conditions do we need to consider? 1072 1073 cancelController.doCancel(comm); 1074 } 1075 1076 1077 public boolean moreResults() 1078 { 1079 return moreResults2; 1080 } 1081 1082 1083 /*** 1084 * Get the length of the current subpacket. 1085 * <p> 1086 * This will eat two bytes from the input socket. 1087 * 1088 * @return length of the current subpacket. 1089 */ 1090 private int getSubPacketLength() 1091 throws java.io.IOException, com.internetcds.jdbc.tds.TdsException 1092 { 1093 return comm.getTdsShort(); 1094 } 1095 1096 1097 /*** 1098 * This will read a error (or warning) message from the SQLServer and 1099 * create a SqlMessage object from that message. 1100 * <p> 1101 * <b> Warning! </b> This is not synchronized because it assumes 1102 * it will only be called by processSubPacket() which is synchronized. 1103 * 1104 * @param packetSubType type of the current subpacket 1105 * 1106 * @return The message returned by the SQLServer. 1107 * 1108 */ 1109 private PacketMsgResult processMsg(byte packetSubType) 1110 throws java.io.IOException, com.internetcds.jdbc.tds.TdsException 1111 { 1112 SqlMessage msg = new SqlMessage(); 1113 1114 int len = getSubPacketLength(); 1115 1116 msg.number = comm.getTdsInt(); 1117 1118 msg.state = comm.getByte(); 1119 1120 msg.level = comm.getByte(); // ?class? 1121 1122 int msgLen = comm.getTdsShort(); 1123 msg.message = comm.getString(msgLen); 1124 1125 // RMK 2000-06-08: the getWarnings() methods aren't implemented, so we 1126 // need to do something with these. 1127 if (showWarnings && msg.message != null) { 1128 String warn = msg.message.trim(); 1129 if (warn.length() > 0) 1130 System.err.println("Server message: " + warn); 1131 } 1132 1133 int srvNameLen = comm.getByte() & 0xFF; 1134 msg.server = comm.getString(srvNameLen); 1135 1136 if (packetSubType == TDS_MSG_TOKEN || packetSubType==TDS_ERR_TOKEN) 1137 { 1138 // nop 1139 int procNameLen = comm.getByte() & 0xFF; 1140 msg.procName = comm.getString(procNameLen); 1141 } 1142 else 1143 { 1144 throw new TdsConfused("Was expecting a msg or error token. " + 1145 "Found 0x" + 1146 Integer.toHexString(packetSubType & 0xff)); 1147 } 1148 1149 msg.line = comm.getByte(); 1150 1151 // unknonw byte 1152 comm.getByte(); 1153 1154 lastServerMessage = msg; 1155 1156 if (packetSubType == TDS_ERR_TOKEN) 1157 { 1158 return new PacketErrorResult(packetSubType, msg); 1159 } 1160 else 1161 { 1162 return new PacketMsgResult(packetSubType, msg); 1163 } 1164 } 1165 1166 1167 /*** 1168 * Process an env change message (TDS_ENV_CHG_TOKEN) 1169 * <p> 1170 * <b> Warning! </b> This is not synchronized because it assumes 1171 * it will only be called by processSubPacket() which is synchronized. 1172 * 1173 * @exception java.io.IOException 1174 * @exception com.internetcds.jdbc.tds.TdsException 1175 */ 1176 private PacketResult processEnvChange() 1177 throws java.io.IOException, com.internetcds.jdbc.tds.TdsException 1178 { 1179 final byte CHARSET_CHANGE = (byte)3; 1180 1181 int len = getSubPacketLength(); 1182 int type = comm.getByte(); 1183 switch (type) 1184 { 1185 case CHARSET_CHANGE: 1186 { 1187 int clen = comm.getByte()&0xFF; 1188 String charset; 1189 if (tdsVer == TDS70) 1190 { 1191 charset = comm.getString(clen); 1192 comm.skip(len-2-clen*2); 1193 } 1194 else 1195 { 1196 charset = encoder.getString(comm.getBytes(clen)); 1197 comm.skip(len-2-clen); 1198 } 1199 setCharset(charset); 1200 break; 1201 } 1202 default: 1203 { 1204 // XXX Should actually look at the env change 1205 // instead of ignoring it. 1206 comm.skip(len-1); 1207 break; 1208 } 1209 } 1210 1211 return new PacketResult(TDS_ENV_CHG_TOKEN); 1212 } 1213 1214 /*** 1215 * Process an column name subpacket. 1216 * <p> 1217 * <p> 1218 * <b> Warning! </b> This is not synchronized because it assumes 1219 * it will only be called by processSubPacket() which is synchronized. 1220 * 1221 */ 1222 private PacketColumnNamesResult processColumnNames() 1223 throws java.io.IOException, com.internetcds.jdbc.tds.TdsException 1224 { 1225 Columns columns = new Columns(); 1226 1227 int totalLen = comm.getTdsShort(); 1228 1229 int bytesRead = 0; 1230 int i = 0; 1231 while (bytesRead < totalLen) 1232 { 1233 int colNameLen = comm.getByte(); 1234 String colName = encoder.getString(comm.getBytes(colNameLen)); 1235 bytesRead = bytesRead + 1 + colNameLen; 1236 i++; 1237 columns.setName(i, colName); 1238 columns.setLabel(i, colName); 1239 } 1240 1241 return new PacketColumnNamesResult(columns); 1242 } // processColumnNames() 1243 1244 1245 /*** 1246 * Process the columns information subpacket. 1247 * <p> 1248 * <b> Warning! </b> This is not synchronized because it assumes 1249 * it will only be called by processSubPacket() which is synchronized. 1250 * 1251 */ 1252 private PacketColumnInfoResult processColumnInfo() 1253 throws java.io.IOException, com.internetcds.jdbc.tds.TdsException 1254 { 1255 Columns columns = new Columns(); 1256 int precision; 1257 int scale; 1258 1259 int totalLen = comm.getTdsShort(); 1260 1261 1262 int bytesRead = 0; 1263 int numColumns = 0; 1264 while (bytesRead < totalLen) 1265 { 1266 scale = -1; 1267 precision = -1; 1268 1269 int sizeOfColumn = -1; 1270 1271 byte flagData[] = new byte[4]; 1272 for (int i = 0; i < 4; i++) 1273 { 1274 flagData[i] = comm.getByte (); 1275 bytesRead++; 1276 } 1277 boolean nullable = (flagData[2] & 0x01) > 0; 1278 boolean writeable = (flagData[2] & 0x08) > 0; 1279 boolean autoIncrement = (flagData[2] & 0x10) > 0; 1280 1281 1282 // Get the type of column 1283 byte columnType = comm.getByte(); 1284 bytesRead++; 1285 1286 if (columnType == SYBTEXT 1287 || columnType == SYBIMAGE) 1288 { 1289 int i; 1290 int tmpByte; 1291 1292 // XXX Need to find out what these next 4 bytes are 1293 // Could they be the column size? 1294 comm.skip(4); 1295 bytesRead += 4; 1296 1297 int tableNameLen = comm.getTdsShort(); 1298 bytesRead += 2; 1299 String tableName = encoder.getString(comm.getBytes(tableNameLen)); 1300 bytesRead += tableNameLen; 1301 1302 sizeOfColumn = 2<<31 - 1; 1303 } 1304 else if (columnType == SYBDECIMAL 1305 || columnType == SYBNUMERIC) 1306 { 1307 int tmp; 1308 sizeOfColumn = comm.getByte(); 1309 bytesRead++; 1310 precision = comm.getByte(); // Total number of digits 1311 bytesRead++; 1312 scale = comm.getByte(); // # of digits after the decimal point 1313 bytesRead++; 1314 } 1315 else if (isFixedSizeColumn(columnType)) 1316 { 1317 sizeOfColumn = lookupColumnSize(columnType); 1318 } 1319 else 1320 { 1321 sizeOfColumn = ((int) comm.getByte() & 0xff); 1322 bytesRead++; 1323 } 1324 numColumns++; 1325 1326 if (scale != -1) 1327 { 1328 columns.setScale(numColumns, scale); 1329 } 1330 if (precision != -1) 1331 { 1332 columns.setPrecision(numColumns, precision); 1333 } 1334 columns.setType(numColumns, columnType); 1335 columns.setDisplaySize(numColumns, sizeOfColumn); 1336 columns.setNullable(numColumns, (nullable 1337 ? ResultSetMetaData.columnNullable 1338 : ResultSetMetaData.columnNoNulls)); 1339 columns.setAutoIncrement(numColumns, autoIncrement); 1340 columns.setReadOnly(numColumns, !writeable); 1341 } 1342 1343 // Don't know what the rest is except that the 1344 int skipLen = totalLen - bytesRead; 1345 if (skipLen != 0) 1346 { 1347 throw new TdsException( 1348 "skipping " + skipLen + " bytes"); 1349 } 1350 1351 return new PacketColumnInfoResult(columns); 1352 } // processColumnInfo 1353 1354 1355 1356 private PacketTabNameResult processTabName() 1357 throws java.io.IOException, com.internetcds.jdbc.tds.TdsException 1358 { 1359 int totalLen = comm.getTdsShort(); 1360 1361 // RMK 2000-06-11. Not sure why the original code is bothering 1362 // to extract the bytes with such meticulous care if it isn't 1363 // going to use the extracted strings for creating the returned 1364 // object. At any rate, this approach doesn't work under TDS 7.0, 1365 // because (1) the name length is a short, not a byte under 7.0, 1366 // and (2) the name string is nameLen wide characters for 7.0, 1367 // not 8-bit characters. So I'm commenting the wasted effort 1368 // and replacing it with a simple call to TdsComm.skip(). 1369 //int bytesRead = 0; 1370 //int nameLen = 0; 1371 //String tabName = null; 1372 1373 //while(bytesRead < totalLen) 1374 //{ 1375 // nameLen = comm.getByte(); 1376 // bytesRead++; 1377 // tabName = new String(comm.getBytes(nameLen)); 1378 // bytesRead += nameLen; 1379 //} 1380 1381 comm.skip(totalLen); 1382 1383 return new PacketTabNameResult(); 1384 } // processTabName() 1385 1386 1387 1388 /*** 1389 * Process an end subpacket. 1390 * <p> 1391 * This routine assumes that the TDS_END_TOKEN byte has already 1392 * been read. 1393 * 1394 * @return 1395 * 1396 * @exception com.internetcds.jdbc.tds.TdsException 1397 * 1398 * @exception java.io.IOException 1399 * Thrown if some sort of error occured reading bytes from the network. 1400 */ 1401 private PacketEndTokenResult processEndToken( 1402 byte packetType) 1403 throws com.internetcds.jdbc.tds.TdsException, java.io.IOException 1404 { 1405 byte status = comm.getByte(); 1406 comm.skip(3); 1407 int rowCount = comm.getTdsInt(); 1408 1409 if (packetType==TdsDefinitions.TDS_DONEINPROC) 1410 { 1411 throw new TdsException("Internal error. TDS_DONEINPROC " 1412 + " is no longer considered an end token"); 1413 } 1414 1415 PacketEndTokenResult result = new PacketEndTokenResult(packetType, 1416 status, rowCount); 1417 1418 moreResults = result.moreResults(); 1419 1420 1421 // XXX If we executed something that returns multiple result 1422 // sets then we don't want to clear the query in progress flag. 1423 // See the CancelController class for details. 1424 cancelController.finishQuery(result.wasCanceled(), 1425 result.moreResults()); 1426 1427 1428 // XXX Problem handling cancels that were sent after the server 1429 // send the endToken packet 1430 return result; 1431 } 1432 1433 1434 private PacketDoneInProcResult processDoneInProc( 1435 byte packetType) 1436 throws TdsException, java.io.IOException 1437 { 1438 byte status = comm.getByte(); 1439 comm.skip(3); 1440 int rowCount = comm.getTdsInt(); 1441 PacketDoneInProcResult result = new PacketDoneInProcResult(packetType, 1442 status, 1443 rowCount); 1444 if (!result.moreResults()) 1445 { 1446 throw new TdsException("What? No more results with a DONEINPROC!"); 1447 } 1448 1449 if (result.moreResults() && peek()==TdsDefinitions.TDS_DONEINPROC) 1450 { 1451 result = (PacketDoneInProcResult)processSubPacket(); 1452 } 1453 1454 while (result.moreResults() && 1455 (peek()==TdsDefinitions.TDS_PROCID 1456 || peek()==TdsDefinitions.TDS_RET_STAT_TOKEN)) 1457 { 1458 if (peek()==TDS_PROCID) 1459 { 1460 PacketResult tmp = processSubPacket(); 1461 } 1462 else if (peek()==TDS_RET_STAT_TOKEN) 1463 { 1464 PacketRetStatResult tmp = (PacketRetStatResult)processSubPacket(); 1465 result.setRetStat(tmp.getRetStat()); 1466 } 1467 } 1468 // XXX If we executed something that returns multiple result 1469 // sets then we don't want to clear the query in progress flag. 1470 // See the CancelController class for details. 1471 cancelController.finishQuery(result.wasCanceled(), 1472 result.moreResults()); 1473 return result; 1474 } 1475 1476 1477 1478 1479 /*** 1480 * Process a subpacket reply 1481 * <p> 1482 * <b>Note-</b> All subpackets must be processed through here. 1483 * This is the only routine has the proper locking to support 1484 * the cancel method in the Statement class. 1485 * <br> 1486 * 1487 * @return packet subtype the was processed. 1488 */ 1489 PacketResult processSubPacket() 1490 throws TdsUnknownPacketSubType, 1491 java.io.IOException, 1492 com.internetcds.jdbc.tds.TdsException 1493 { 1494 return processSubPacket(null); 1495 } 1496 1497 1498 /*** 1499 * Process a subpacket reply 1500 * <p> 1501 * <b>Note-</b> All subpackets must be processed through here. Only this 1502 * routine has the proper locking to support the cancel method in the 1503 * Statement class. 1504 * <br> 1505 * 1506 * 1507 * @return packet subtype the was processed. 1508 */ 1509 synchronized PacketResult processSubPacket(Context context) 1510 throws TdsUnknownPacketSubType, 1511 java.io.IOException, 1512 com.internetcds.jdbc.tds.TdsException 1513 { 1514 // NOTE!!! Before adding anything to this list you must 1515 // consider the ramifications to the the handling of cancels 1516 // as implemented by the CancelController class. 1517 // 1518 // The CancelController class might implicitly assume it can call 1519 // processSubPacket() whenever it is looking for a cancel 1520 // acknowledgment. It assumes that any results of the call 1521 // can be discarded. 1522 1523 PacketResult result = null; 1524 moreResults = false; 1525 1526 byte packetSubType = comm.getByte(); 1527 Logger.println("processSubPacket: " + 1528 Integer.toHexString(packetSubType & 0xFF) + " " + 1529 "moreResults: " + moreResults()); 1530 1531 switch(packetSubType) 1532 { 1533 case TDS_ENV_CHG_TOKEN: 1534 { 1535 result = processEnvChange(); 1536 break; 1537 } 1538 case TDS_ERR_TOKEN: 1539 case TDS_MSG_TOKEN: 1540 case TDS_MSG50_TOKEN: 1541 { 1542 result = processMsg(packetSubType); 1543 break; 1544 } 1545 case TDS_TEXT_UPD_TOKEN: 1546 { 1547 int len = getSubPacketLength(); 1548 comm.skip(len); 1549 result = new PacketResult(TDS_TEXT_UPD_TOKEN); 1550 break; 1551 } 1552 case TDS_LOGIN_ACK_TOKEN: 1553 { 1554 result = processLoginAck(); 1555 break; 1556 } 1557 case TDS_RET_STAT_TOKEN: 1558 { 1559 result = processRetStat(); 1560 break; 1561 } 1562 case TDS_PROCID: 1563 { 1564 result = processProcId(); 1565 break; 1566 } 1567 case TDS_DONEINPROC: 1568 { 1569 result = processDoneInProc(packetSubType); 1570 break; 1571 } 1572 case TDS_DONEPROC: 1573 case TDS_END_TOKEN: 1574 { 1575 result = processEndToken(packetSubType); 1576 moreResults2 = ((PacketEndTokenResult)result).moreResults(); 1577 break; 1578 } 1579 case TDS_COL_NAME_TOKEN: 1580 { 1581 result = processColumnNames(); 1582 break; 1583 } 1584 case TDS_COL_INFO_TOKEN: 1585 { 1586 result = processColumnInfo(); 1587 break; 1588 } 1589 case TDS_UNKNOWN_0xA5: 1590 case TDS_UNKNOWN_0xA7: 1591 case TDS_UNKNOWN_0xA8: 1592 { 1593 // XXX Need to figure out what this packet is 1594 comm.skip(comm.getTdsShort()); 1595 result = new PacketUnknown(packetSubType); 1596 break; 1597 } 1598 case TDS_TABNAME: 1599 { 1600 result = processTabName(); 1601 break; 1602 } 1603 case TDS_ORDER: 1604 { 1605 int len = comm.getTdsShort(); 1606 comm.skip(len); 1607 1608 result = new PacketColumnOrderResult(); 1609 break; 1610 } 1611 case TDS_CONTROL: 1612 { 1613 int len = comm.getTdsShort(); 1614 comm.skip(len); 1615 // FIXME - I'm just ignoring this 1616 result = new PacketControlResult(); 1617 break; 1618 } 1619 case TDS_ROW_TOKEN: 1620 { 1621 result = getRow(context.getColumnInfo()); 1622 break; 1623 } 1624 case TDS7_RESULT_TOKEN: 1625 { 1626 1627 result = processTds7Result(); 1628 break; 1629 } 1630 default: 1631 { 1632 throw new TdsUnknownPacketSubType(packetSubType); 1633 } 1634 } 1635 return result; 1636 } 1637 1638 1639 /*** 1640 * Find out how many bytes a particular SQLServer data type takes. 1641 * 1642 * @param nativeColumnType 1643 * 1644 * @return number of bytes required by the given type 1645 * 1646 * @exception com.internetcds.jdbc.tds.TdsException 1647 * Thrown if the given type either doesn't exist or is a variable 1648 * sized data type. 1649 */ 1650 private int lookupColumnSize(byte nativeColumnType) 1651 throws com.internetcds.jdbc.tds.TdsException 1652 { 1653 switch(nativeColumnType) 1654 { 1655 case SYBINT1: 1656 { 1657 return 1; 1658 } 1659 case SYBINT2: 1660 { 1661 return 2; 1662 } 1663 case SYBINT4: 1664 { 1665 return 4; 1666 } 1667 case SYBREAL: 1668 { 1669 return 4; 1670 } 1671 case SYBFLT8: 1672 { 1673 return 8; 1674 } 1675 case SYBDATETIME: 1676 { 1677 return 8; 1678 } 1679 case SYBDATETIME4: 1680 { 1681 return 8; 1682 } 1683 case SYBBIT: 1684 { 1685 return 1; 1686 } 1687 case SYBMONEY: 1688 { 1689 return 8; 1690 } 1691 case SYBMONEY4: 1692 case SYBSMALLMONEY: 1693 { 1694 return 4; 1695 } 1696 default: 1697 { 1698 throw new TdsException("Not fixed size column " 1699 + nativeColumnType); 1700 } 1701 } 1702 } // lookupColumnSize() 1703 1704 1705 1706 /*** 1707 * determine if a given datatype is a fixed size 1708 * 1709 * @param nativeColumnType The SQLServer datatype to check 1710 * 1711 * @return <code>true</code> if the datatype is a fixed size, 1712 * <code>false</code> if the datatype is a variable size 1713 * 1714 * @exception com.internetcds.jdbc.tds.TdsException 1715 * If the <code>nativeColumnType</code> is not a knowm datatype. 1716 * 1717 */ 1718 private boolean isFixedSizeColumn(byte nativeColumnType) 1719 throws com.internetcds.jdbc.tds.TdsException 1720 { 1721 switch (nativeColumnType) 1722 { 1723 case SYBINT1: 1724 case SYBINT2: 1725 case SYBINT4: 1726 case SYBFLT8: 1727 case SYBDATETIME: 1728 case SYBBIT: 1729 case SYBMONEY: 1730 case SYBMONEY4: 1731 case SYBSMALLMONEY: 1732 case SYBREAL: 1733 case SYBDATETIME4: 1734 { 1735 return true; 1736 } 1737 case SYBINTN: 1738 case SYBMONEYN: 1739 case SYBVARCHAR: 1740 case SYBNVARCHAR: 1741 case SYBDATETIMN: 1742 case SYBFLTN: 1743 case SYBCHAR: 1744 case SYBNCHAR: 1745 case SYBNTEXT: 1746 case SYBIMAGE: 1747 case SYBVARBINARY: 1748 case SYBBINARY: 1749 case SYBDECIMAL: 1750 case SYBNUMERIC: 1751 case SYBBITN: 1752 { 1753 return false; 1754 } 1755 default: 1756 { 1757 throw new TdsException("Unrecognized column type 0x" 1758 + Integer.toHexString(nativeColumnType)); 1759 } 1760 } 1761 } 1762 1763 1764 private Object readFloatN(int len) 1765 throws TdsException, java.io.IOException 1766 { 1767 Object tmp; 1768 1769 switch (len) 1770 { 1771 case 8: 1772 { 1773 long l = comm.getTdsInt64(); 1774 tmp = new Double(Double.longBitsToDouble(l)); 1775 break; 1776 } 1777 case 4: 1778 { 1779 int i = comm.getTdsInt(); 1780 tmp = new Float(Float.intBitsToFloat(i)); 1781 break; 1782 } 1783 case 0: 1784 { 1785 tmp = null; 1786 break; 1787 } 1788 default: 1789 { 1790 throw new TdsNotImplemented("Don't now how to handle " 1791 + "float with size of " 1792 + len 1793 + "(0x" 1794 + Integer.toHexString(len & 0xff) 1795 + ")"); 1796 } 1797 } 1798 return tmp; 1799 } 1800 1801 1802 private Object getMoneyValue( 1803 int type) 1804 throws java.io.IOException, TdsException 1805 { 1806 int len; 1807 Object result; 1808 1809 if (type == SYBMONEYN) 1810 { 1811 len = comm.getByte(); 1812 } 1813 else 1814 { 1815 len = lookupColumnSize((byte)type); 1816 } 1817 1818 if (len == 0) 1819 { 1820 result = null; 1821 } 1822 else 1823 { 1824 BigInteger x = null; 1825 1826 if (len == 4) 1827 { 1828 x = BigInteger.valueOf(comm.getTdsInt()); 1829 } 1830 else if (len == 8) 1831 { 1832 byte b4 = comm.getByte(); 1833 byte b5 = comm.getByte(); 1834 byte b6 = comm.getByte(); 1835 byte b7 = comm.getByte(); 1836 byte b0 = comm.getByte(); 1837 byte b1 = comm.getByte(); 1838 byte b2 = comm.getByte(); 1839 byte b3 = comm.getByte(); 1840 long l = 1841 (long)(b0&0xff) + ((long)(b1&0xff)<<8) + 1842 ((long)(b2&0xff)<<16) + ((long)(b3&0xff)<<24) + 1843 ((long)(b4&0xff)<<32) + ((long)(b5&0xff)<<40) + 1844 ((long)(b6&0xff)<<48) + ((long)(b7&0xff)<<56); 1845 x = BigInteger.valueOf(l); 1846 } 1847 else 1848 { 1849 throw new TdsConfused("Don't know what to do with len of " 1850 + len); 1851 } 1852 x = x.divide(BigInteger.valueOf(100)); 1853 result = new BigDecimal(x, 2); 1854 } 1855 return result; 1856 } // getMoneyValue 1857 1858 1859 /*** 1860 * Extracts decimal value from the server's results packet. Takes 1861 * advantage of Java's superb handling of large numbers, which does 1862 * all the heavy lifting for us. Format is: 1863 * <UL> 1864 * <LI>Length byte <code>len</code>; count includes sign byte.</LI> 1865 * <LI>Sign byte (0=negative; 1=positive).</LI> 1866 * <LI>Magnitude bytes (array of <code>len</code> - 1 bytes, 1867 * in little-endian order.</LI> 1868 * </UL> 1869 * 1870 * @param scale number of decimal digits after the decimal 1871 * point. 1872 * @return <code>BigDecimal</code> for extracted value 1873 * (or (<code>null</code> if appropriate). 1874 */ 1875 private Object getDecimalValue(int scale) 1876 throws TdsException, java.io.IOException, NumberFormatException 1877 { 1878 int len = comm.getByte() & 0xff; 1879 if (--len < 1) 1880 return null; 1881 1882 // RMK 2000-06-10. Deduced from some testing/packet sniffing. 1883 byte[] bytes = new byte[len]; 1884 int signum = comm.getByte() == 0 ? -1 : 1; 1885 while (len > 0) 1886 bytes[--len] = comm.getByte(); 1887 BigInteger bigInt = new BigInteger(signum, bytes); 1888 return new BigDecimal(bigInt, scale); 1889 } 1890 1891 1892 private Object getDatetimeValue( 1893 int type) 1894 throws java.io.IOException, TdsException 1895 { 1896 // Some useful constants 1897 final long SECONDS_PER_DAY = 24L * 60L * 60L; 1898 final long DAYS_BETWEEN_1900_AND_1970 = 25567L; 1899 1900 int len; 1901 Object result; 1902 1903 if (type == SYBDATETIMN) 1904 { 1905 len = comm.getByte(); 1906 } 1907 else if (type == SYBDATETIME4) 1908 { 1909 len = 4; 1910 } 1911 else 1912 { 1913 len = 8; // XXX shouldn't this be an error? 1914 } 1915 1916 1917 switch (len) 1918 { 1919 case 0: 1920 { 1921 result = null; 1922 break; 1923 } 1924 case 8: 1925 { 1926 // It appears that a datetime is made of of 2 32bit ints 1927 // The first one is the number of days since 1900 1928 // The second integer is the number of seconds*300 1929 // The reason the calculations below are sliced up into 1930 // such small baby steps is to avoid a bug in JDK1.2.2's 1931 // runtime, which got confused by the original complexity. 1932 long tdsDays = (long)comm.getTdsInt(); 1933 long tdsTime = (long)comm.getTdsInt(); 1934 long sqlDays = tdsDays - DAYS_BETWEEN_1900_AND_1970; 1935 long seconds = sqlDays * SECONDS_PER_DAY + tdsTime / 300L; 1936 long micros = ((tdsTime % 300L) * 1000000L) / 300L; 1937 long millis = seconds * 1000L + micros / 1000L - zoneOffset; 1938 1939 // Round up if appropriate. 1940 if (micros % 1000L >= 500L) 1941 millis++; 1942 1943 result = new Timestamp(millis - getDstOffset(millis)); 1944 break; 1945 } 1946 case 4: 1947 { 1948 // Accroding to Transact SQL Reference 1949 // a smalldatetime is two small integers. 1950 // The first is the number of days past January 1, 1900, 1951 // the second smallint is the number of minutes past 1952 // midnight. 1953 1954 long tdsDays = (long)comm.getTdsShort(); 1955 long minutes = (long)comm.getTdsShort(); 1956 long sqlDays = tdsDays - DAYS_BETWEEN_1900_AND_1970; 1957 long seconds = sqlDays * SECONDS_PER_DAY + minutes * 60L; 1958 long millis = seconds * 1000L - zoneOffset; 1959 1960 result = new Timestamp(millis - getDstOffset(millis)); 1961 break; 1962 1963 } 1964 default: 1965 { 1966 result = null; 1967 throw new TdsNotImplemented("Don't now how to handle " 1968 + "date with size of " 1969 + len); 1970 } 1971 } 1972 return result; 1973 } // getDatetimeValue() 1974 1975 1976 /*** 1977 * Determines the number of milliseconds needed to adjust for daylight 1978 * savings time for a given date/time value. Note that there is a problem 1979 * with the way SQL Server sends a DATETIME value, since it is constructed 1980 * to represent the local time for the server. This means that each fall 1981 * there is a window of approximately one hour during which a single value 1982 * can represent two different times. 1983 */ 1984 private long getDstOffset(long time) { 1985 Calendar cal = Calendar.getInstance(); 1986 cal.setTime(new java.util.Date(time)); 1987 return cal.get(Calendar.DST_OFFSET); 1988 } 1989 1990 1991 private Object getIntValue(int type) 1992 throws java.io.IOException, TdsException 1993 { 1994 Object result; 1995 int len; 1996 1997 switch (type) 1998 { 1999 case SYBINTN: 2000 { 2001 len = comm.getByte(); 2002 break; 2003 } 2004 case SYBINT4: 2005 { 2006 len = 4; 2007 break; 2008 } 2009 case SYBINT2: 2010 { 2011 len = 2; 2012 break; 2013 } 2014 case SYBINT1: 2015 { 2016 len = 1; 2017 break; 2018 } 2019 default: 2020 { 2021 result = null; 2022 throw new TdsNotImplemented( 2023 "can't handle integer of type " 2024 + Integer.toHexString(type)); 2025 } 2026 } 2027 2028 switch (len) 2029 { 2030 case 4: {result = new Long(comm.getTdsInt()); break;} 2031 case 2: {result = new Long(comm.getTdsShort()); break;} 2032 case 1: 2033 { 2034 int tmp = toUInt(comm.getByte()); // XXX Are we sure this should be unsigned? 2035 result = new Long(tmp); 2036 break; 2037 } 2038 case 0: 2039 { 2040 result = null; 2041 break; 2042 } 2043 default: 2044 { 2045 result = null; 2046 throw new TdsConfused("Bad SYBINTN length of " + 2047 len); 2048 } 2049 } 2050 return result; 2051 } // getIntValue() 2052 2053 2054 private Object getCharValue(boolean wideChars) 2055 throws TdsException, java.io.IOException 2056 { 2057 Object result; 2058 int len = tdsVer == Tds.TDS70 ? comm.getTdsShort() : comm.getByte() & 0xFF; 2059 2060 if (len == 0 || tdsVer == Tds.TDS70 && len == 0xFFFF) 2061 { 2062 result = null; 2063 } 2064 else if (len > 0) 2065 { 2066 if (wideChars) 2067 result = comm.getString(len / 2); 2068 else 2069 result = encoder.getString(comm.getBytes(len)); 2070 2071 if (result.equals(" ")) 2072 { 2073 // In SQL trailing spaces are stripped from strings 2074 // MS SQLServer denotes a zero length string 2075 // as a single space. 2076 result = ""; 2077 } 2078 } 2079 else 2080 { 2081 throw new TdsConfused("String with length<0"); 2082 } 2083 return result; 2084 } // getCharValue() 2085 2086 private Object getTextValue(boolean wideChars) 2087 throws TdsException, java.io.IOException 2088 { 2089 String result; 2090 2091 byte hasValue = comm.getByte(); 2092 2093 if (hasValue==0) 2094 { 2095 result = null; 2096 } 2097 else 2098 { 2099 // XXX Have no idea what these 24 bytes are 2100 // 2000-06-06 RMK They are the TEXTPTR (16 bytes) and the TIMESTAMP. 2101 comm.skip(24); 2102 2103 int len = comm.getTdsInt(); 2104 2105 // RMK 2000-06-11 2106 // The logic immediately below does not agree with test t0031, 2107 // so I'm commenting it out. On the other hand, it's a bit 2108 // puzzling that a column defined as TEXT NOT NULL needs the 2109 // hasValue byte read just above, but apparently it does. 2110 //if (len == 0) 2111 //{ 2112 // result = null; 2113 //} 2114 //else 2115 if (len >= 0) 2116 { 2117 if (wideChars) { 2118 result = comm.getString(len / 2); 2119 } else { 2120 result = encoder.getString(comm.getBytes(len)); 2121 } 2122 2123 if (" ".equals(result)) 2124 { 2125 // In SQL trailing spaces are stripped from strings 2126 // MS SQLServer denotes a zero length string 2127 // as a single space. 2128 result = ""; 2129 } 2130 } 2131 else 2132 { 2133 throw new TdsConfused("String with length<0"); 2134 } 2135 } 2136 return result; 2137 } // getTextValue() 2138 2139 private Object getImageValue() throws TdsException, java.io.IOException 2140 { 2141 byte[] result; 2142 2143 byte hasValue = comm.getByte(); 2144 2145 if (hasValue==0) 2146 { 2147 result = null; 2148 } 2149 else 2150 { 2151 // XXX Have no idea what these 24 bytes are 2152 // 2000-06-06 RMK They are the TEXTPTR (16 bytes) and the TIMESTAMP. 2153 comm.skip(24); 2154 2155 int len = comm.getTdsInt(); 2156 2157 // RMK 2000-06-11 2158 // The logic immediately below does not agree with test t0031, 2159 // so I'm commenting it out. On the other hand, it's a bit 2160 // puzzling that a column defined as TEXT NOT NULL needs the 2161 // hasValue byte read just above, but apparently it does. 2162 //if (len == 0) 2163 //{ 2164 // result = null; 2165 //} 2166 //else 2167 if (len >= 0) 2168 { 2169 result = comm.getBytes(len); 2170 } 2171 else 2172 { 2173 throw new TdsConfused("String with length<0"); 2174 } 2175 } 2176 return result; 2177 } // getImageValue() 2178 2179 /*** 2180 * get one result row from the TDS stream 2181 * <p> 2182 * This will read a full row from the TDS stream and store it in 2183 * a PacketRowResult object. 2184 * 2185 */ 2186 synchronized private PacketRowResult getRow(Columns columnsInfo) 2187 throws TdsException, java.io.IOException 2188 { 2189 PacketRowResult result = null; 2190 2191 int i; 2192 2193 result = new PacketRowResult(columnsInfo.getColumnCount()); 2194 2195 for(i=1; i<=columnsInfo.getColumnCount(); i++) 2196 { 2197 Object element; 2198 int colType = columnsInfo.getType(i); 2199 2200 Logger.println("colno=" + i + 2201 " type=" + colType + 2202 " offset=" + Integer.toHexString(comm.inBufferIndex)); 2203 switch (colType) 2204 { 2205 case SYBINTN: 2206 case SYBINT1: 2207 case SYBINT2: 2208 case SYBINT4: 2209 { 2210 element = getIntValue(colType); 2211 break; 2212 } 2213 case SYBIMAGE: 2214 { 2215 element = getImageValue(); 2216 break; 2217 } 2218 case SYBTEXT: 2219 { 2220 element = getTextValue(false); 2221 break; 2222 } 2223 case SYBNTEXT: 2224 { 2225 element = getTextValue(true); 2226 break; 2227 } 2228 case SYBCHAR: 2229 case SYBVARCHAR: 2230 { 2231 element = getCharValue(false); 2232 break; 2233 } 2234 case SYBNCHAR: 2235 case SYBNVARCHAR: 2236 { 2237 element = getCharValue(true); 2238 break; 2239 } 2240 case SYBREAL: 2241 { 2242 element = readFloatN(4); 2243 break; 2244 } 2245 case SYBFLT8: 2246 { 2247 element = readFloatN(8); 2248 break; 2249 } 2250 case SYBFLTN: 2251 { 2252 int len; 2253 2254 len = comm.getByte(); 2255 2256 element = readFloatN(len); 2257 break; 2258 } 2259 case SYBSMALLMONEY: 2260 case SYBMONEY: 2261 case SYBMONEYN: 2262 { 2263 element = getMoneyValue(colType); 2264 break; 2265 } 2266 case SYBNUMERIC: 2267 case SYBDECIMAL: 2268 { 2269 element = getDecimalValue(columnsInfo.getScale(i)); 2270 break; 2271 } 2272 case SYBDATETIME4: 2273 case SYBDATETIMN: 2274 case SYBDATETIME: 2275 { 2276 element = getDatetimeValue(colType); 2277 break; 2278 } 2279 case SYBVARBINARY: 2280 case SYBBINARY: 2281 { 2282 int len = tdsVer == Tds.TDS70 2283 ? comm.getTdsShort() 2284 : (comm.getByte() & 0xff); 2285 if (tdsVer == Tds.TDS70 && len == 0xffff) 2286 element = null; 2287 else 2288 element = comm.getBytes(len); 2289 break; 2290 } 2291 case SYBBITN: 2292 case SYBBIT: 2293 { 2294 if (colType == SYBBITN && comm.getByte() == 0) 2295 element = null; 2296 else 2297 element = new Boolean((comm.getByte()!=0) ? true : false); 2298 break; 2299 } 2300 default: 2301 { 2302 element = null; 2303 throw new TdsNotImplemented("Don't now how to handle " + 2304 "column type 0x" + 2305 Integer.toHexString(colType)); 2306 } 2307 } 2308 result.setElementAt(element, i); 2309 } 2310 2311 return result; 2312 } // getRow() 2313 2314 2315 private boolean createStoredProcedureNameTable() 2316 { 2317 boolean result = false; 2318 String sql = null; 2319 2320 try 2321 { 2322 java.sql.Statement stmt = connection.createStatement(); 2323 2324 2325 // ignore any of the exceptions thrown because they either 2326 // don't matter or they will make themselves known when we try 2327 // to use the name generator stored procedure. 2328 try 2329 { 2330 sql = "" 2331 + "create table " + procNameTableName 2332 + "( " 2333 + " id NUMERIC(10, 0) IDENTITY, " 2334 + " session int not null, " 2335 + " name char(29) not null " 2336 + ") "; 2337 stmt.executeUpdate(sql); 2338 } 2339 catch(java.sql.SQLException e) 2340 { 2341 // don't care 2342 } 2343 2344 try 2345 { 2346 sql = "" 2347 + "create procedure " + procNameGeneratorName + " " 2348 + "as " 2349 + "begin tran " 2350 + "insert into " + procNameTableName + " " 2351 + " (session, name) " 2352 + " values " 2353 + " (@@spid, '') " 2354 + " " 2355 + "update " + procNameTableName + " " 2356 + " set name=('" + user + ".jdbctmpsp' + " 2357 + " convert(varchar, @@IDENTITY)) " 2358 + " where id = @@IDENTITY " 2359 + " " 2360 + "select name from " + procNameTableName + " " 2361 + " where id=@@IDENTITY " 2362 + " " 2363 + "commit tran " 2364 + ""; 2365 2366 stmt.execute(sql); 2367 stmt.execute("sp_procxmode " + 2368 procNameGeneratorName + 2369 ", 'anymode' "); 2370 } 2371 catch(java.sql.SQLException e) 2372 { 2373 // don't care 2374 } 2375 2376 stmt = null; 2377 } 2378 catch(java.sql.SQLException e) 2379 { 2380 // don't care 2381 } 2382 return result; 2383 } 2384 2385 private String generateUniqueProcName() 2386 throws java.sql.SQLException 2387 { 2388 java.sql.Statement stmt = connection.createStatement(); 2389 2390 boolean wasRs; 2391 2392 wasRs = stmt.execute("exec " + procNameGeneratorName); 2393 if (!wasRs) 2394 { 2395 throw new java.sql.SQLException( 2396 "Confused. Was expecting a result set."); 2397 } 2398 2399 java.sql.ResultSet rs; 2400 rs = stmt.getResultSet(); 2401 if (!rs.next()) 2402 { 2403 throw new java.sql.SQLException("Couldn't get stored proc name"); 2404 } 2405 return rs.getString(1); 2406 } 2407 2408 2409 /*** 2410 * Create a new and unique name for a store procedure. 2411 * 2412 * This routine will return a unique name for a stored procedure 2413 * that will be associated with a PreparedStatement(). 2414 * <p> 2415 * Since SQLServer supports temporary procedure names we can just 2416 * use UniqueId.getUniqueId() to generate a unique (for the connection) 2417 * name. 2418 * <p> 2419 * Sybase does not support temporary procedure names so we will have 2420 * to have a per user table devoted to storing user specific stored 2421 * procedures. The table name will be of the form 2422 * database.user.jdbc_temp_stored_proc_names. The table will be defined 2423 * as 2424 * <code> 2425 * CREATE TABLE database.user.jdbc_temp_stored_proc_names ( 2426 * id NUMERIC(10, 0) IDENTITY; 2427 * session int not null; 2428 * name char(29) 2429 * ) 2430 * <code> 2431 * This routine will use that table to track names that are being 2432 * used. 2433 */ 2434 public String getUniqueProcedureName() 2435 throws java.sql.SQLException 2436 { 2437 String result = null; 2438 2439 if (serverType == SYBASE) 2440 { 2441 if (null == procNameTableName) 2442 { 2443 procNameTableName = database + "." + user 2444 + ".jdbc_temp_stored_proc_names"; 2445 procNameGeneratorName = user + ".jdbc_gen_temp_sp_names"; 2446 } 2447 2448 // 2449 // Attempt to create the table for the stored procedure names 2450 // If it already exists we'll get an error, but we don't care. 2451 // Also create a stored procedure for generating the unique 2452 // names. 2453 // 2454 haveProcNameTable = createStoredProcedureNameTable(); 2455 2456 result = generateUniqueProcName(); 2457 } 2458 else 2459 { 2460 result = "#jdbc#" + UniqueId.getUniqueId(); 2461 } 2462 return result; 2463 } // getUniqueProcedureName() 2464 2465 2466 /*** 2467 * 2468 */ 2469 synchronized public PacketResult submitProcedure(String sql, 2470 SQLWarningChain chain) 2471 throws SQLException 2472 { 2473 2474 PacketResult result = null; 2475 PacketResult tmp = null; 2476 boolean okay = true; 2477 byte tmpByte; 2478 SQLException exception = null; 2479 2480 try 2481 { 2482 executeQuery(sql, null, 0); 2483 2484 tmpByte = (byte)(comm.peek() & 0xff); 2485 2486 while (! ((tmp = processSubPacket()) 2487 instanceof PacketEndTokenResult)) 2488 { 2489 if (tmp instanceof PacketErrorResult) 2490 { 2491 result = tmp; 2492 okay = false; 2493 // XXX I'm sure we need to do more here. 2494 2495 // how about throwing an Exception? --SB 2496 exception = ((PacketErrorResult)tmp).getMsg().toSQLException(); 2497 } 2498 else if (tmp instanceof PacketMsgResult) 2499 { 2500 chain.addOrReturn((PacketMsgResult)tmp); 2501 } 2502 else 2503 { 2504 throw new SQLException( 2505 "Confused. Was expecting the " 2506 + "end of result, found a " 2507 + tmp.getClass().getName()); 2508 } 2509 } 2510 if (result == null) 2511 { 2512 result = tmp; 2513 } 2514 } 2515 catch(java.io.IOException e) 2516 { 2517 throw new SQLException("Network error" + e.getMessage()); 2518 } 2519 catch (com.internetcds.jdbc.tds.TdsUnknownPacketSubType e) 2520 { 2521 throw new SQLException(e.getMessage()); 2522 } 2523 catch(com.internetcds.jdbc.tds.TdsException e) 2524 { 2525 throw new SQLException(e.getMessage()); 2526 } 2527 2528 if (!okay) 2529 { 2530 throw exception; 2531 } 2532 return result; 2533 } 2534 2535 2536 /*** 2537 * Execute a stored procedure on the SQLServer 2538 * <p> 2539 * 2540 * @param procedure the stored procedure to execute. 2541 * @param parameterList the parameter to pass to the stored procedure 2542 * 2543 * @exception java.sql.SQLException 2544 * @exception com.internetcds.jdbc.tds.TdsException 2545 */ 2546 synchronized public void executeProcedure( 2547 String procedureName, 2548 ParameterListItem[] formalParameterList, 2549 ParameterListItem[] actualParameterList, 2550 java.sql.Statement stmt, 2551 int timeout) 2552 throws java.sql.SQLException, com.internetcds.jdbc.tds.TdsException 2553 { 2554 2555 // A stored procedure has a packet type of 0x03 in the header packet. 2556 // for non-image date the packets consists of 2557 // offset length desc. 2558 // 0 1 The length of the name of the stored proc 2559 // 1 N1 The name of the stored proc 2560 // N1 + 1 2 unknown (filled with zeros?) 2561 // N1 + 3 2 unknown prefix for param 1 (zero filled?) 2562 // N1 + 5 1 datatype for param 1 2563 // N1 + 6 1 max length of param 1 2564 // N1 + 7 N2 parameter 1 data 2565 // ... 2566 // 2567 // For image data (datatype 0x22) the packet consists of 2568 // 0 1 The length of the name of the stored proc 2569 // 1 N1 The name of the stored proc 2570 // N1 + 1 2 unknown (filled with zeros?) 2571 // N1 + 3 2 unknown prefix for param 1 (zero filled?) 2572 // N1 + 5 1 datatype for param 1 2573 // N1 + 6 4 length of param 1 2574 // N1 + 10 4 length of param 1 (duplicated?) 2575 // N1 + 7 N2 parameter 1 data 2576 // ... 2577 2578 int i; 2579 2580 try 2581 { 2582 // mark that we are performing a query 2583 cancelController.setQueryInProgressFlag(); 2584 2585 // Start sending the procedure execute packet. 2586 comm.startPacket(TdsComm.PROC); 2587 2588 if (tdsVer == Tds.TDS70) { 2589 comm.appendTdsShort((short)(procedureName.length())); 2590 comm.appendChars(procedureName); 2591 } 2592 else { 2593 byte[] nameBytes = encoder.getBytes(procedureName); 2594 comm.appendByte((byte)nameBytes.length); 2595 comm.appendBytes(nameBytes, 2596 nameBytes.length, 2597 (byte)0); 2598 } 2599 comm.appendByte((byte)0); 2600 comm.appendByte((byte)0); 2601 // Now handle the parameters 2602 for(i=0; i<formalParameterList.length; i++) 2603 { 2604 byte nativeType = cvtJdbcTypeToNativeType(formalParameterList[i].type); 2605 2606 comm.appendByte((byte)0); 2607 comm.appendByte((byte)0); 2608 2609 switch(nativeType) 2610 { 2611 case SYBCHAR: 2612 case SYBVARCHAR: 2613 { 2614 String val = (String)actualParameterList[i].value; 2615 int len = val != null ? val.length() : 0; 2616 int max = formalParameterList[i].maxLength; 2617 //Sinisa 2618 //using actualParameters caused problems 2619 // if (actualParameterList[i].formalType.startsWith("n")) { 2620 if (formalParameterList[i].formalType.startsWith("n")) { 2621 /* 2622 * This is a Unicode column, save to assume TDS 7.0 2623 */ 2624 if (max > 4000) { 2625 comm.appendByte(SYBNTEXT); 2626 comm.appendTdsInt(max * 2); 2627 if (val == null) 2628 comm.appendTdsInt(0xFFFFFFFF); 2629 else { 2630 comm.appendTdsInt(len * 2); 2631 comm.appendChars(val); 2632 } 2633 } 2634 else { 2635 comm.appendByte((byte)(SYBNVARCHAR | 0x80)); 2636 comm.appendTdsShort((short)(max * 2)); 2637 if (val == null) 2638 comm.appendTdsShort((short)0xFFFF); 2639 else { 2640 comm.appendTdsShort((short)(len * 2)); 2641 comm.appendChars(val); 2642 } 2643 } 2644 2645 } else { 2646 /* 2647 * Either VARCHAR or TEXT, TEXT can not happen 2648 * with TDS 7.0 as we would always use NTEXT there 2649 */ 2650 if (tdsVer != TDS70 && max > 255) { 2651 // TEXT 2652 comm.appendByte((byte)SYBTEXT); 2653 sendSybImage(encoder.getBytes((String)actualParameterList[i] 2654 .value)); 2655 } else { 2656 // VARCHAR 2657 sendSybChar(((String)actualParameterList[i].value), 2658 formalParameterList[i].maxLength); 2659 } 2660 } 2661 break; 2662 2663 } 2664 2665 case SYBINT4: 2666 case SYBINTN: 2667 { 2668 if (nativeType==SYBINTN) 2669 { 2670 comm.appendByte(nativeType); 2671 // set the maximum length of the field, 2672 comm.appendByte((byte)4); 2673 2674 // set the actual length, and the data 2675 if (actualParameterList[i].value == null) 2676 { 2677 comm.appendByte((byte)0); 2678 // comm.appendTdsInt((byte)0); 2679 } 2680 else 2681 { 2682 comm.appendByte((byte)4); 2683 comm.appendTdsInt(((Number)(actualParameterList[i].value)).intValue()); 2684 } 2685 } 2686 else if (actualParameterList[i].value == null) 2687 { 2688 comm.appendByte(SYBINTN); 2689 comm.appendByte((byte)4); 2690 comm.appendByte((byte)0); 2691 } 2692 else 2693 { 2694 comm.appendByte(nativeType); 2695 comm.appendTdsInt(((Number)(actualParameterList[i].value)).intValue()); 2696 } 2697 break; 2698 } 2699 case SYBFLT8: 2700 { 2701 //sinisa 2702 //Add possibility with NULL value as a SYBFLT8 parameter 2703 if(actualParameterList[i].value!=null) { 2704 Number n = (Number)(actualParameterList[i].value); 2705 Double d = new Double(n.doubleValue()); 2706 comm.appendByte((byte)nativeType); 2707 comm.appendFlt8(d); 2708 } 2709 //sinisa 2710 //If parameter value is NULL send INTEGER value for NULL value to database 2711 else { 2712 comm.appendByte(SYBINTN); 2713 comm.appendByte((byte)4); 2714 comm.appendByte((byte)0); 2715 } 2716 break; 2717 } 2718 case SYBDATETIMN: 2719 { 2720 comm.appendByte((byte)nativeType); 2721 2722 comm.appendByte((byte)8); 2723 if (actualParameterList[i].value == null) 2724 { 2725 comm.appendByte((byte)0); 2726 } 2727 else 2728 { 2729 Timestamp value; 2730 if (actualParameterList[i].value instanceof java.sql.Timestamp) 2731 { 2732 value = (Timestamp)actualParameterList[i].value; 2733 } 2734 else 2735 { 2736 value = new Timestamp(((java.util.Date)actualParameterList[i].value).getTime()); 2737 } 2738 2739 2740 comm.appendByte((byte)8); 2741 2742 final int secondsPerDay = 24 * 60 * 60; 2743 final int msPerDay = secondsPerDay * 1000; 2744 final int nsPerMs = 1000 * 1000; 2745 // epochsDifference is the number of days between unix 2746 // epoch (1970 based) and the sybase epoch (1900 based) 2747 final int epochsDifference = 25567; 2748 2749 long nanoseconds = value.getNanos(); 2750 2751 // ms is the number of milliseconds into unix epoch 2752 2753 long ms = ((value.getTime() + (nanoseconds / nsPerMs)) 2754 + zoneOffset); 2755 ms -= getDstOffset(ms); 2756 long msIntoCurrentDay = ms % msPerDay; 2757 2758 long daysIntoUnixEpoch = (ms-msIntoCurrentDay)/msPerDay; 2759 2760 int jiffies = (int)((msIntoCurrentDay * 300) / 1000); 2761 int daysIntoSybaseEpoch = (int)daysIntoUnixEpoch 2762 + epochsDifference; 2763 2764 comm.appendTdsInt(daysIntoSybaseEpoch); 2765 comm.appendTdsInt(jiffies); 2766 } 2767 break; 2768 } 2769 case SYBIMAGE: 2770 { 2771 comm.appendByte((byte)nativeType); 2772 2773 sendSybImage((byte[])actualParameterList[i].value); 2774 break; 2775 } 2776 case SYBTEXT: 2777 { 2778 comm.appendByte((byte)SYBTEXT); 2779 sendSybImage(encoder.getBytes((String)actualParameterList[i]. 2780 value)); 2781 break; 2782 } 2783 //Sinisa 2784 //Add implementation of SYBBIT native type 2785 2786 case SYBBIT: 2787 { 2788 if (actualParameterList[i].value == null) 2789 { 2790 comm.appendByte(SYBINTN); 2791 comm.appendByte((byte)1); 2792 comm.appendByte((byte)0); 2793 } 2794 else 2795 { 2796 comm.appendByte(nativeType); 2797 if(((Byte)(actualParameterList[i].value)).intValue()==1) 2798 comm.appendByte((byte)1); 2799 else 2800 comm.appendByte((byte)0); 2801 } 2802 break; 2803 2804 // comm.appendByte((byte)nativeType); 2805 //sendSybImage(byte[])actualParameterList[i].value); 2806 // sendSybChar(((String)actualParameterList[i].value),1); 2807 // break; 2808 2809 2810 } 2811 case SYBVOID: 2812 case SYBVARBINARY: 2813 //Sinisa 2814 // case SYBVARCHAR: 2815 case SYBBINARY: 2816 case SYBINT1: 2817 //case SYBBIT: 2818 case SYBINT2: 2819 case SYBDATETIME4: 2820 case SYBREAL: 2821 case SYBMONEY: 2822 case SYBDATETIME: 2823 case SYBDECIMAL: 2824 case SYBNUMERIC: 2825 case SYBFLTN: 2826 case SYBMONEYN: 2827 case SYBMONEY4: 2828 default: 2829 { 2830 throw new SQLException("Not implemented for nativeType 0x" 2831 + Integer.toHexString(nativeType)); 2832 } 2833 } 2834 } 2835 //sinisa 2836 // moreResults2=true; 2837 comm.sendPacket(); 2838 waitForDataOrTimeout(stmt, timeout); 2839 } 2840 catch(java.io.IOException e) 2841 { 2842 throw new SQLException("Network error- " + e.getMessage()); 2843 } 2844 } /* executeProcedure() */ 2845 2846 private void sendSybImage( 2847 byte[] value) 2848 throws java.io.IOException 2849 { 2850 int i; 2851 int length = (value==null ? 0 : value.length); 2852 2853 // send the lenght of this piece of data 2854 comm.appendTdsInt(length); 2855 2856 // send the length of this piece of data again 2857 comm.appendTdsInt(length); 2858 2859 // send the data 2860 for(i=0; i<length; i++) 2861 { 2862 comm.appendByte(value[i]); 2863 } 2864 } 2865 2866 private void sendSybChar( 2867 String value, 2868 int maxLength) 2869 throws java.io.IOException 2870 { 2871 2872 byte[] converted; 2873 if (value == null) { 2874 converted = new byte[0]; 2875 } else { 2876 converted = encoder.getBytes(value); 2877 } 2878 2879 if (converted.length > 255 && tdsVer != TDS70) 2880 { 2881 throw new java.io.IOException("String too long"); 2882 } 2883 2884 // set the type of the column 2885 // set the maximum length of the field 2886 // set the actual lenght of the field. 2887 if (converted.length > 256) { 2888 comm.appendByte((byte) (SYBVARCHAR | 0x80)); 2889 comm.appendTdsShort((short)(maxLength)); 2890 comm.appendTdsShort((short)(converted.length)); 2891 } else { 2892 comm.appendByte(SYBVARCHAR); 2893 comm.appendByte((byte)(maxLength)); 2894 comm.appendByte((byte)(converted.length)); 2895 } 2896 2897 comm.appendBytes(converted); 2898 } 2899 2900 /*** 2901 * Process a login ack supacket 2902 */ 2903 private PacketResult processLoginAck() 2904 throws com.internetcds.jdbc.tds.TdsException, java.io.IOException 2905 { 2906 int len = getSubPacketLength(); 2907 int bytesRead = 0; 2908 2909 if (tdsVer == Tds.TDS70) 2910 { 2911 comm.skip(5); 2912 int nameLen = comm.getByte(); 2913 databaseProductName = comm.getString(nameLen); 2914 databaseProductVersion = ("" + comm.getByte() + "." 2915 + comm.getByte() + "." 2916 + ((256*comm.getByte())+ comm.getByte())); 2917 } 2918 else 2919 { 2920 comm.skip(5); 2921 short nameLen = comm.getByte(); 2922 databaseProductName = comm.getString(nameLen); 2923 comm.skip(1); 2924 databaseProductVersion = ("" + comm.getByte() + "." + comm.getByte()); 2925 comm.skip(1); 2926 } 2927 2928 if (databaseProductName.length()>1 2929 && -1 != databaseProductName.indexOf('\0')) 2930 { 2931 int last = databaseProductName.indexOf('\0'); 2932 databaseProductName = databaseProductName.substring(0, last); 2933 } 2934 2935 return new PacketResult(TDS_LOGIN_ACK_TOKEN); 2936 } 2937 2938 2939 /*** 2940 * Process an proc id subpacket. 2941 * <p> 2942 * This routine assumes that the TDS_PROCID byte has already 2943 * been read. 2944 * 2945 * @exception com.internetcds.jdbc.tds.TdsException 2946 * 2947 * @exception java.io.IOException 2948 * Thrown if some sort of error occured reading bytes from the network. 2949 */ 2950 private PacketResult processProcId() 2951 throws java.io.IOException, com.internetcds.jdbc.tds.TdsException 2952 { 2953 // XXX Try to find out what meaning this subpacket has. 2954 int i; 2955 byte tmp; 2956 2957 for(i=0; i<8; i++) 2958 { 2959 tmp = comm.getByte(); 2960 } 2961 return new PacketResult(TDS_PROCID); 2962 } 2963 /*** 2964 * Process a TDS_RET_STAT_TOKEN subpacket. 2965 * <p> 2966 * This routine assumes that the TDS_RET_STAT_TOKEN 2967 * byte has already been read. 2968 * 2969 * @exception com.internetcds.jdbc.tds.TdsException 2970 * 2971 * @exception java.io.IOException 2972 * Thrown if some sort of error occured reading bytes from the network. 2973 */ 2974 private PacketRetStatResult processRetStat() 2975 throws java.io.IOException, com.internetcds.jdbc.tds.TdsException 2976 { 2977 // XXX Not completely sure of this. 2978 return new PacketRetStatResult(comm.getTdsInt()); 2979 } 2980 2981 /*** 2982 * Processes a TDS 7.0-style result packet, extracting column information 2983 * for the result set. 2984 * 2985 * Added 2000-06-05. 2986 */ 2987 private PacketResult processTds7Result() 2988 throws java.io.IOException, com.internetcds.jdbc.tds.TdsException 2989 { 2990 int numColumns = comm.getTdsShort(); 2991 Columns columns = new Columns(); 2992 2993 //try { 2994 //throw new Exception(); 2995 //} 2996 //catch (Exception e) { 2997 //e.printStackTrace(); 2998 //} 2999 for (int colNum = 1; colNum <= numColumns; ++colNum) { 3000 3001 /* 3002 * The freetds C code didn't know what to do with these four 3003 * bytes, but initial inspection appears to tentatively confirm 3004 * that they serve the same purpose as the flag bytes read by the 3005 * Java code for 4.2 column information. 3006 */ 3007 byte flagData[] = new byte[4]; 3008 for (int i = 0; i < 4; i++) 3009 flagData[i] = comm.getByte(); 3010 boolean nullable = (flagData[2] & 0x01) > 0; 3011 boolean writeable = (flagData[2] & 0x08) > 0; 3012 boolean autoIncrement = (flagData[2] & 0x10) > 0; 3013 3014 /* 3015 * Get the type of column. Large types have 2-byte size fields and 3016 * type codes OR'd with 0x80. Except SYBNCHAR, whose type code 3017 * is already above 0x80. 3018 */ 3019 int columnType = comm.getByte() & 0xFF; 3020 if (columnType == 0xEF) 3021 columnType = SYBNCHAR; 3022 int xColType = -1; 3023 if (isLargeType(columnType)) { 3024 xColType = columnType; 3025 if (columnType != SYBNCHAR) 3026 columnType -= 128; 3027 } 3028 // Determine the column size. 3029 int colSize; 3030 if (isBlobType(columnType)) { 3031 3032 // Text and image columns have 4-byte size fields. 3033 colSize = comm.getTdsInt(); 3034 3035 // Swallow table name. 3036 comm.getString(comm.getTdsShort()); 3037 } 3038 3039 // Fixed types have no size field in the packet. 3040 else if (isFixedSizeColumn((byte)columnType)) 3041 colSize = lookupColumnSize((byte)columnType); 3042 3043 else if (isLargeType(xColType)) 3044 colSize = comm.getTdsShort(); 3045 3046 else 3047 colSize = comm.getByte(); 3048 3049 // Get precision, scale for decimal types. 3050 int precision = -1; 3051 int scale = -1; 3052 if (columnType == SYBDECIMAL || columnType == SYBNUMERIC) { 3053 precision = comm.getByte(); 3054 scale = comm.getByte(); 3055 } 3056 3057 /* 3058 * NB: under 7.0 lengths are number of characters, not number of 3059 * bytes. The getString() method handles this. 3060 */ 3061 int colNameLen = comm.getByte(); 3062 String columnName = comm.getString(colNameLen); 3063 3064 // Populate the Column object. 3065 columns.setType(colNum, columnType); 3066 columns.setName(colNum, columnName); 3067 columns.setLabel(colNum, columnName); 3068 columns.setDisplaySize(colNum, colSize); 3069 columns.setNullable(colNum, (nullable 3070 ? ResultSetMetaData.columnNullable 3071 : ResultSetMetaData.columnNoNulls)); 3072 columns.setAutoIncrement(colNum, autoIncrement); 3073 columns.setReadOnly(colNum, !writeable); 3074 if (precision != -1) 3075 columns.setPrecision(colNum, precision); 3076 if (scale != -1) 3077 columns.setScale(colNum, scale); 3078 } 3079 return new PacketColumnNamesResult(columns); 3080 3081 } // processTds7Result() 3082 3083 /*** 3084 * Reports whether the type is for a large object. Name is a bit of a 3085 * misnomer, since it returns true for large text types, not just binary 3086 * objects (took it over from the freetds C code). 3087 */ 3088 private static boolean isBlobType(int type) { 3089 return type == SYBTEXT || type == SYBIMAGE || type == SYBNTEXT; 3090 } 3091 3092 /*** 3093 * Reports whether the type uses a 2-byte size value. 3094 */ 3095 private static boolean isLargeType(int type) { 3096 return type == SYBNCHAR || type > 128; 3097 } 3098 3099 private void waitForDataOrTimeout( 3100 java.sql.Statement stmt, 3101 int timeout) 3102 throws java.io.IOException, com.internetcds.jdbc.tds.TdsException 3103 { 3104 3105 // XXX How should this be syncrhonized? 3106 if (timeout==0 || stmt==null) 3107 { 3108 comm.peek(); 3109 } 3110 else 3111 { 3112 // start the timeout thread 3113 TimeoutHandler t = new TimeoutHandler(stmt, timeout); 3114 3115 3116 t.start(); 3117 3118 // wait until there is at least one byte of data 3119 comm.peek(); 3120 3121 // kill the timeout thread 3122 t.stop(); 3123 t = null; 3124 } 3125 } 3126 3127 3128 /*** 3129 * send a query to the SQLServer for execution. 3130 * <p> 3131 * 3132 * @param sql sql statement to execute. 3133 * @param stmt 3134 * @param timeout 3135 * 3136 * @exception com.internetcds.jdbc.tds.TdsException 3137 * @exception java.io.IOException 3138 */ 3139 synchronized public void executeQuery( 3140 String sql, 3141 java.sql.Statement stmt, 3142 int timeout) 3143 throws java.io.IOException, java.sql.SQLException, TdsException 3144 { 3145 { 3146 cancelController.setQueryInProgressFlag(); 3147 comm.startPacket(TdsComm.QUERY); 3148 3149 if (tdsVer == Tds.TDS70) 3150 { 3151 comm.appendChars(sql); 3152 } 3153 else 3154 { 3155 byte[] sqlBytes = encoder.getBytes(sql); 3156 comm.appendBytes(sqlBytes, sqlBytes.length, (byte)0); 3157 } 3158 moreResults2=true; //JJ 1999-01-10 3159 comm.sendPacket(); 3160 3161 waitForDataOrTimeout(stmt, timeout); 3162 } 3163 } 3164 3165 3166 /*** 3167 * skip over and discard any remaining data from a result set. 3168 * 3169 * @exception com.internetcds.jdbc.tds.TdsException 3170 * @exception java.io.IOException 3171 */ 3172 synchronized public void discardResultSet(Columns columnsInfo) 3173 throws java.io.IOException, com.internetcds.jdbc.tds.TdsException 3174 { 3175 while(isResultRow()) 3176 { 3177 if (columnsInfo == null) 3178 { 3179 throw new com.internetcds.jdbc.tds.TdsConfused(); 3180 } 3181 comm.skip(1); 3182 getRow(columnsInfo); 3183 } 3184 3185 if(comm.peek() == TDS_DONEINPROC) 3186 { 3187 PacketResult tmp = processSubPacket(); 3188 } 3189 3190 // XXX Is there ever going to be a situation where the 3191 // TDS_DONEINPROC is the real end of data? If so then the 3192 // next section of code will hang forever waiting for more data 3193 // from the socket. 3194 if (isEndOfResults()) 3195 { 3196 processSubPacket(); 3197 } 3198 3199 // RMK 2000-06-08 Don't choke on additional result sets. 3200 else if (!isResultSet()) 3201 { 3202 throw new TdsConfused("Was expecting an end of results token. " 3203 + "Found a 0x" 3204 + Integer.toHexString(comm.peek() & 0xff)); 3205 } 3206 } 3207 3208 3209 synchronized public byte peek() 3210 throws java.io.IOException, com.internetcds.jdbc.tds.TdsException 3211 { 3212 return comm.peek(); 3213 } // peek() 3214 3215 3216 /*** 3217 * Determine if the next subpacket is a result set. 3218 * <p> 3219 * This does not eat any input. 3220 * 3221 * @return true if the next piece of data to read is a result set. 3222 * 3223 * @exception com.internetcds.jdbc.tds.TdsException 3224 * @exception java.io.IOException 3225 */ 3226 synchronized public boolean isResultSet() 3227 throws com.internetcds.jdbc.tds.TdsException, java.io.IOException 3228 { 3229 byte type = comm.peek(); 3230 3231 /* 3232 * XXX to support 5.0 we need to expand our view of what a result 3233 * set is. 3234 */ 3235 return type==TDS_COL_NAME_TOKEN || type == TDS7_RESULT_TOKEN; 3236 } 3237 3238 /*** 3239 * Determine if the next subpacket is a ret stat 3240 * <p> 3241 * This does not eat any input. 3242 * 3243 * @return true if the next piece of data to read is a result row. 3244 * 3245 * @exception com.internetcds.jdbc.tds.TdsException 3246 * @exception java.io.IOException 3247 */ 3248 synchronized public boolean isRetStat() 3249 throws com.internetcds.jdbc.tds.TdsException, java.io.IOException 3250 { 3251 byte type = comm.peek(); 3252 3253 return type==TDS_RET_STAT_TOKEN; 3254 } 3255 3256 /*** 3257 * Determine if the next subpacket is a result row. 3258 * <p> 3259 * This does not eat any input. 3260 * 3261 * @return true if the next piece of data to read is a result row. 3262 * 3263 * @exception com.internetcds.jdbc.tds.TdsException 3264 * @exception java.io.IOException 3265 */ 3266 synchronized public boolean isResultRow() 3267 throws com.internetcds.jdbc.tds.TdsException, java.io.IOException 3268 { 3269 byte type = comm.peek(); 3270 3271 return type==TDS_ROW_TOKEN; 3272 } 3273 3274 3275 /*** 3276 * Determine if the next subpacket is an end of result set marker. 3277 * <p> 3278 * This does not eat any input. 3279 * 3280 * @return true if the next piece of data to read is end of result set 3281 * marker. 3282 * 3283 * @exception com.internetcds.jdbc.tds.TdsException 3284 * @exception java.io.IOException 3285 */ 3286 synchronized public boolean isEndOfResults() 3287 throws com.internetcds.jdbc.tds.TdsException, java.io.IOException 3288 { 3289 byte type = comm.peek(); 3290 3291 return type==TDS_END_TOKEN || type==TDS_DONEPROC; 3292 } 3293 3294 /*** 3295 * Determine if the next subpacket is a DONEINPROC marker 3296 * <p> 3297 * This does not eat any input. 3298 * 3299 * @return 3300 * 3301 * @exception com.internetcds.jdbc.tds.TdsException 3302 * @exception java.io.IOException 3303 */ 3304 synchronized public boolean isDoneInProc() 3305 throws com.internetcds.jdbc.tds.TdsException, java.io.IOException 3306 { 3307 byte type = comm.peek(); 3308 3309 return type==TDS_DONEINPROC; 3310 } 3311 3312 /*** 3313 * Determine if the next subpacket is a message packet 3314 * <p> 3315 * This does not eat any input. 3316 * 3317 * @return true if the next piece of data to read is message 3318 * 3319 * @exception com.internetcds.jdbc.tds.TdsException 3320 * @exception java.io.IOException 3321 */ 3322 synchronized public boolean isMessagePacket() 3323 throws com.internetcds.jdbc.tds.TdsException, java.io.IOException 3324 { 3325 byte type = comm.peek(); 3326 return type==TDS_MSG_TOKEN; 3327 } 3328 3329 /*** 3330 * Determine if the next subpacket is a text update packet 3331 * <p> 3332 * This does not eat any input. 3333 * 3334 * @return true if the next piece of data to read is text update 3335 * 3336 * @exception com.internetcds.jdbc.tds.TdsException 3337 * @exception java.io.IOException 3338 */ 3339 synchronized public boolean isTextUpdate() 3340 throws com.internetcds.jdbc.tds.TdsException, java.io.IOException 3341 { 3342 byte type = comm.peek(); 3343 return type==TDS_TEXT_UPD_TOKEN; 3344 } 3345 3346 /*** 3347 * Determine if the next subpacket is an error packet 3348 * <p> 3349 * This does not eat any input. 3350 * 3351 * @return true if the next piece of data to read is an error 3352 * 3353 * @exception com.internetcds.jdbc.tds.TdsException 3354 * @exception java.io.IOException 3355 */ 3356 synchronized public boolean isErrorPacket() 3357 throws com.internetcds.jdbc.tds.TdsException, java.io.IOException 3358 { 3359 byte type = comm.peek(); 3360 return type==TDS_ERR_TOKEN; 3361 } 3362 //sinisa 3363 /*** 3364 * Determine if the next subpacket is an return status packet 3365 * <p> 3366 * This does not eat any input. 3367 * 3368 * @return true if the next piece of data to read is an return status 3369 * 3370 * @exception com.internetcds.jdbc.tds.TdsException 3371 * @exception java.io.IOException 3372 */ 3373 synchronized public boolean isReturnStatus() 3374 throws com.internetcds.jdbc.tds.TdsException, java.io.IOException 3375 { 3376 byte type = comm.peek(); 3377 return type==TDS_RET_STAT_TOKEN; 3378 } 3379 3380 /*** 3381 * Determine if the next subpacket is an procid subpacket 3382 * <p> 3383 * This does not eat any input. 3384 * 3385 * @return true if the next piece of data to read is end of result set 3386 * marker. 3387 * 3388 * @exception com.internetcds.jdbc.tds.TdsException 3389 * @exception java.io.IOException 3390 */ 3391 synchronized public boolean isProcId() 3392 throws com.internetcds.jdbc.tds.TdsException, java.io.IOException 3393 { 3394 3395 byte type = comm.peek(); 3396 return type==TDS_PROCID; 3397 } 3398 3399 /*** 3400 * Accessor method to determine the TDS level used. 3401 * 3402 * @return TDS42, TDS50, or TDS70. 3403 */ 3404 int getTdsVer() { return tdsVer; } 3405 3406 /*** 3407 * Return the name that this database server program calls itself. 3408 */ 3409 String getDatabaseProductName() 3410 { 3411 return databaseProductName; 3412 } 3413 3414 /*** 3415 * Return the name that this database server program calls itself. 3416 */ 3417 String getDatabaseProductVersion() 3418 { 3419 return databaseProductVersion; 3420 } 3421 }

This page automatically generated by Maven