KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > internetcds > jdbc > tds > Tds


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 JavaDoc;
39 import java.util.Vector JavaDoc;
40 import java.lang.Thread JavaDoc;
41 import java.util.StringTokenizer JavaDoc;
42 import java.sql.*;
43 import com.internetcds.jdbc.tds.TdsComm;
44 import com.internetcds.util.Logger;
45 import java.math.BigInteger JavaDoc;
46 import java.math.BigDecimal JavaDoc;
47 import java.util.Calendar JavaDoc;
48 import java.util.Properties JavaDoc;
49 import java.util.TimeZone JavaDoc;
50 import java.util.Locale JavaDoc;
51
52
53
54 /**
55  * Cancel the current SQL if the timeout expires.
56  *
57  * @version $Id: Tds.java,v 1.1 2006/06/23 10:39:30 sinisa Exp $
58  * @author Craig Spannring
59  */

60 class TimeoutHandler extends Thread JavaDoc
61 {
62    public static final String JavaDoc cvsVersion = "$Id: Tds.java,v 1.1 2006/06/23 10:39:30 sinisa Exp $";
63
64    java.sql.Statement JavaDoc stmt;
65    int timeout;
66
67
68    public TimeoutHandler(
69       java.sql.Statement JavaDoc 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 JavaDoc e)
89       {
90          // nop
91
}
92    }
93 }
94
95
96
97 /**
98  * Implement the TDS protocol.
99  *
100  * @version $Id: Tds.java,v 1.1 2006/06/23 10:39:30 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 JavaDoc cvsVersion = "$Id: Tds.java,v 1.1 2006/06/23 10:39:30 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 JavaDoc sock = null;
121    TdsComm comm = null;
122
123    String JavaDoc databaseProductName;
124    String JavaDoc databaseProductVersion;
125
126    java.sql.Connection JavaDoc connection;
127    String JavaDoc 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 JavaDoc database;
131    String JavaDoc user;
132    String JavaDoc password;
133    String JavaDoc appName;
134    String JavaDoc serverName;
135    String JavaDoc progName;
136    byte progMajorVersion;
137    byte progMinorVersion;
138
139    boolean haveProcNameTable = false;
140    String JavaDoc procNameGeneratorName = null;
141    String JavaDoc procNameTableName = null;
142
143    String JavaDoc initialSettings = null;
144
145    private Properties JavaDoc initialProps = null;
146    private EncodingHelper encoder = null;
147    private String JavaDoc 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 JavaDoc connection_,
172       Properties JavaDoc props_,
173       String JavaDoc initialSettings)
174       throws java.io.IOException JavaDoc, java.net.UnknownHostException JavaDoc,
175       java.sql.SQLException JavaDoc, com.internetcds.jdbc.tds.TdsException
176    {
177       connection = (java.sql.Connection JavaDoc)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 JavaDoc 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 JavaDoc(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 JavaDoc charset)
235    {
236       try
237       {
238          Logger.println("Trying to change charset to " + charset);
239       }
240       catch(java.io.IOException JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc toNativeSql(String JavaDoc 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 JavaDoc getClientName()
488    {
489       // This method is thread safe.
490
String JavaDoc tmp;
491       try
492       {
493          tmp = java.net.InetAddress.getLocalHost().getHostName();
494       }
495       catch(java.net.UnknownHostException JavaDoc e)
496       {
497          tmp = "";
498       }
499       StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(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 JavaDoc,
610       TdsUnknownPacketSubType, java.io.IOException JavaDoc,
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 JavaDoc {
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 JavaDoc libName = "DB-Library";
816       byte pad = (byte)0;
817       byte[] empty = new byte[0];
818       String JavaDoc 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 JavaDoc 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 JavaDoc tds7CryptPass(String JavaDoc 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 JavaDoc(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 JavaDoc database,
927       String JavaDoc settings)
928       throws java.sql.SQLException JavaDoc
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 JavaDoc 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 JavaDoc 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 JavaDoc database)
991       throws java.sql.SQLException JavaDoc
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 JavaDoc 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 JavaDoc 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 JavaDoc, 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 JavaDoc, 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 JavaDoc, 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 JavaDoc 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 JavaDoc, 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 JavaDoc 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 JavaDoc, 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 JavaDoc 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 JavaDoc, 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 JavaDoc 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 JavaDoc, 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 JavaDoc
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 JavaDoc
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 JavaDoc,
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 JavaDoc,
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 JavaDoc readFloatN(int len)
1765      throws TdsException, java.io.IOException JavaDoc
1766   {
1767      Object JavaDoc tmp;
1768
1769      switch (len)
1770      {
1771         case 8:
1772         {
1773            long l = comm.getTdsInt64();
1774            tmp = new Double JavaDoc(Double.longBitsToDouble(l));
1775            break;
1776         }
1777         case 4:
1778         {
1779            int i = comm.getTdsInt();
1780            tmp = new Float JavaDoc(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 JavaDoc getMoneyValue(
1803      int type)
1804      throws java.io.IOException JavaDoc, TdsException
1805   {
1806      int len;
1807      Object JavaDoc 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 JavaDoc 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 JavaDoc(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 JavaDoc getDecimalValue(int scale)
1876      throws TdsException, java.io.IOException JavaDoc, NumberFormatException JavaDoc
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 JavaDoc bigInt = new BigInteger JavaDoc(signum, bytes);
1888      return new BigDecimal JavaDoc(bigInt, scale);
1889   }
1890
1891      
1892   private Object JavaDoc getDatetimeValue(
1893      int type)
1894      throws java.io.IOException JavaDoc, 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 JavaDoc 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 JavaDoc cal = Calendar.getInstance();
1986      cal.setTime(new java.util.Date JavaDoc(time));
1987      return cal.get(Calendar.DST_OFFSET);
1988   }
1989
1990
1991   private Object JavaDoc getIntValue(int type)
1992      throws java.io.IOException JavaDoc, TdsException
1993   {
1994      Object JavaDoc 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 JavaDoc(comm.getTdsInt()); break;}
2031         case 2: {result = new Long JavaDoc(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 JavaDoc(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 JavaDoc getCharValue(boolean wideChars)
2055      throws TdsException, java.io.IOException JavaDoc
2056   {
2057      Object JavaDoc 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 JavaDoc getTextValue(boolean wideChars)
2087      throws TdsException, java.io.IOException JavaDoc
2088   {
2089      String JavaDoc 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 JavaDoc getImageValue() throws TdsException, java.io.IOException JavaDoc
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 JavaDoc
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 JavaDoc 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 JavaDoc((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 JavaDoc sql = null;
2319
2320      try
2321      {
2322         java.sql.Statement JavaDoc 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 JavaDoc 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 JavaDoc e)
2372         {
2373            // don't care
2374
}
2375         
2376         stmt = null;
2377      }
2378      catch(java.sql.SQLException JavaDoc e)
2379      {
2380         // don't care
2381
}
2382      return result;
2383   }
2384
2385   private String JavaDoc generateUniqueProcName()
2386      throws java.sql.SQLException JavaDoc
2387   {
2388      java.sql.Statement JavaDoc stmt = connection.createStatement();
2389
2390      boolean wasRs;
2391
2392      wasRs = stmt.execute("exec " + procNameGeneratorName);
2393      if (!wasRs)
2394      {
2395         throw new java.sql.SQLException JavaDoc(
2396            "Confused. Was expecting a result set.");
2397      }
2398
2399      java.sql.ResultSet JavaDoc rs;
2400      rs = stmt.getResultSet();
2401      if (!rs.next())
2402      {
2403         throw new java.sql.SQLException JavaDoc("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 JavaDoc getUniqueProcedureName()
2435      throws java.sql.SQLException JavaDoc
2436   {
2437      String JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc procedureName,
2548      ParameterListItem[] formalParameterList,
2549      ParameterListItem[] actualParameterList,
2550      java.sql.Statement JavaDoc stmt,
2551      int timeout)
2552      throws java.sql.SQLException JavaDoc, 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 JavaDoc val = (String JavaDoc)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 JavaDoc)actualParameterList[i]
2654                                                         .value));
2655                       } else {
2656                           // VARCHAR
2657
sendSybChar(((String JavaDoc)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 JavaDoc)(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 JavaDoc)(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 JavaDoc n = (Number JavaDoc)(actualParameterList[i].value);
2705                  Double JavaDoc d = new Double JavaDoc(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 JavaDoc)
2731                     {
2732                        value = (Timestamp)actualParameterList[i].value;
2733                     }
2734                     else
2735                     {
2736                        value = new Timestamp(((java.util.Date JavaDoc)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 JavaDoc)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 JavaDoc)(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 JavaDoc 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 JavaDoc
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 JavaDoc value,
2868      int maxLength)
2869      throws java.io.IOException JavaDoc
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 JavaDoc("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 JavaDoc
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 JavaDoc, 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 JavaDoc, 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 JavaDoc, 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 JavaDoc 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 JavaDoc stmt,
3101      int timeout)
3102      throws java.io.IOException JavaDoc, 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 JavaDoc sql,
3141      java.sql.Statement JavaDoc stmt,
3142      int timeout)
3143      throws java.io.IOException JavaDoc, java.sql.SQLException JavaDoc, 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 JavaDoc, 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 JavaDoc, 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 JavaDoc
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 JavaDoc
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 JavaDoc
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 JavaDoc
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 JavaDoc
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 JavaDoc
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 JavaDoc
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 JavaDoc
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 JavaDoc
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 JavaDoc
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 JavaDoc getDatabaseProductName()
3410   {
3411      return databaseProductName;
3412   }
3413
3414   /**
3415    * Return the name that this database server program calls itself.
3416    */

3417   String JavaDoc getDatabaseProductVersion()
3418   {
3419      return databaseProductVersion;
3420   }
3421}
3422
Popular Tags