KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > mysql > jdbc > MysqlIO


1 /*
2    Copyright (C) 2002 MySQL AB
3
4       This program is free software; you can redistribute it and/or modify
5       it under the terms of the GNU General Public License as published by
6       the Free Software Foundation; either version 2 of the License, or
7       (at your option) any later version.
8
9       This program is distributed in the hope that it will be useful,
10       but WITHOUT ANY WARRANTY; without even the implied warranty of
11       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12       GNU General Public License for more details.
13
14       You should have received a copy of the GNU General Public License
15       along with this program; if not, write to the Free Software
16       Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
18  */

19 package com.mysql.jdbc;
20
21 import java.io.BufferedInputStream JavaDoc;
22 import java.io.BufferedOutputStream JavaDoc;
23 import java.io.ByteArrayOutputStream JavaDoc;
24 import java.io.EOFException JavaDoc;
25 import java.io.FileInputStream JavaDoc;
26 import java.io.IOException JavaDoc;
27 import java.io.InputStream JavaDoc;
28 import java.io.OutputStreamWriter JavaDoc;
29
30 import java.lang.ref.SoftReference JavaDoc;
31
32 import java.net.Socket JavaDoc;
33
34 import java.security.NoSuchAlgorithmException JavaDoc;
35
36 import java.sql.SQLException JavaDoc;
37 import java.sql.SQLWarning JavaDoc;
38
39 import java.util.ArrayList JavaDoc;
40 import java.util.Properties JavaDoc;
41 import java.util.zip.Deflater JavaDoc;
42 import java.util.zip.Inflater JavaDoc;
43
44
45 /**
46  * This class is used by Connection for communicating with the MySQL server.
47  *
48  * @author Mark Matthews
49  * @version $Id: MysqlIO.java,v 1.32.2.46 2004/02/06 00:54:42 mmatthew Exp $
50  *
51  * @see java.sql.Connection
52  */

53 public class MysqlIO {
54     static final int NULL_LENGTH = ~0;
55     static final int COMP_HEADER_LENGTH = 3;
56     static final int MIN_COMPRESS_LEN = 50;
57     static final int HEADER_LENGTH = 4;
58     private static int maxBufferSize = 65535;
59     private static final int CLIENT_COMPRESS = 32; /* Can use compression
60     protcol */

61     private static final int CLIENT_CONNECT_WITH_DB = 8;
62     private static final int CLIENT_FOUND_ROWS = 2;
63     private static final int CLIENT_IGNORE_SPACE = 256; /* Ignore spaces
64     before '(' */

65     private static final int CLIENT_LOCAL_FILES = 128; /* Can use LOAD DATA
66     LOCAL */

67
68     /* Found instead of
69        affected rows */

70     private static final int CLIENT_LONG_FLAG = 4; /* Get all column flags */
71     private static final int CLIENT_LONG_PASSWORD = 1; /* new more secure
72     passwords */

73     private static final int CLIENT_PROTOCOL_41 = 512; // for > 4.1.1
74
private static final int CLIENT_INTERACTIVE = 1024;
75     private static final int CLIENT_SSL = 2048;
76     private static final int CLIENT_RESERVED = 16384; // for 4.1.0 only
77
private static final int CLIENT_SECURE_CONNECTION = 32768;
78     private static final String JavaDoc FALSE_SCRAMBLE = "xxxxxxxx";
79
80     /**
81      * We store the platform 'encoding' here, only used to avoid munging
82      * filenames for LOAD DATA LOCAL INFILE...
83      */

84     private static String JavaDoc jvmPlatformCharset = null;
85
86     static {
87         OutputStreamWriter JavaDoc outWriter = null;
88
89         //
90
// Use the I/O system to get the encoding (if possible), to avoid
91
// security restrictions on System.getProperty("file.encoding") in
92
// applets (why is that restricted?)
93
//
94
try {
95             outWriter = new OutputStreamWriter JavaDoc(new ByteArrayOutputStream JavaDoc());
96             jvmPlatformCharset = outWriter.getEncoding();
97         } finally {
98             try {
99                 outWriter.close();
100             } catch (IOException JavaDoc ioEx) {
101                 // ignore
102
}
103         }
104     }
105
106     //
107
// Use this when reading in rows to avoid thousands of new()
108
// calls, because the byte arrays just get copied out of the
109
// packet anyway
110
//
111
private Buffer reusablePacket = null;
112     private Buffer sendPacket = null;
113     private Buffer sharedSendPacket = null;
114
115     /** Data to the server */
116
117     //private DataOutputStream _Mysql_Output = null;
118
private BufferedOutputStream JavaDoc mysqlOutput = null;
119     private com.mysql.jdbc.Connection connection;
120     private Deflater JavaDoc deflater = null;
121     private Inflater JavaDoc inflater = null;
122
123     /** Buffered data from the server */
124
125     //private BufferedInputStream _Mysql_Buf_Input = null;
126

127     /** Buffered data to the server */
128
129     //private BufferedOutputStream _Mysql_Buf_Output = null;
130

131     /** Data from the server */
132
133     //private DataInputStream _Mysql_Input = null;
134
private InputStream JavaDoc mysqlInput = null;
135     private RowData streamingData = null;
136
137     //
138
// For SQL Warnings
139
//
140
private SQLWarning JavaDoc warningChain = null;
141
142     /** The connection to the server */
143     private Socket JavaDoc mysqlConnection = null;
144     private SocketFactory socketFactory = null;
145
146     //
147
// Packet used for 'LOAD DATA LOCAL INFILE'
148
//
149
// We use a SoftReference, so that we don't penalize intermittent
150
// use of this feature
151
//
152
private SoftReference JavaDoc loadFileBufRef;
153
154     //
155
// Used to send large packets to the server versions 4+
156
// We use a SoftReference, so that we don't penalize intermittent
157
// use of this feature
158
//
159
private SoftReference JavaDoc splitBufRef;
160     private String JavaDoc host = null;
161     private String JavaDoc seed;
162     private String JavaDoc serverVersion = null;
163     private String JavaDoc socketFactoryClassName = null;
164     private byte[] packetHeaderBuf = new byte[4];
165     private boolean clearStreamBeforeEachQuery = false;
166     private boolean colDecimalNeedsBump = false; // do we need to increment the colDecimal flag?
167
private boolean has41NewNewProt = false;
168
169     /** Does the server support long column info? */
170     private boolean hasLongColumnInfo = false;
171     private boolean isInteractiveClient = false;
172
173     /**
174      * Does the character set of this connection match the character set of the
175      * platform
176      */

177     private boolean platformDbCharsetMatches = true;
178     private boolean profileSql = false;
179
180     /** Should we use 4.1 protocol extensions? */
181     private boolean use41Extensions = false;
182     private boolean useCompression = false;
183     private boolean useNewLargePackets = false;
184     private boolean useNewUpdateCounts = false; // should we use the new larger update counts?
185
private byte packetSequence = 0;
186     private byte protocolVersion = 0;
187     private int clientParam = 0;
188
189     // changed once we've connected.
190
private int maxAllowedPacket = 1024 * 1024;
191     private int maxThreeBytes = 255 * 255 * 255;
192     private int port = 3306;
193     private int serverMajorVersion = 0;
194     private int serverMinorVersion = 0;
195     private int serverSubMinorVersion = 0;
196
197     /**
198      * Constructor: Connect to the MySQL server and setup a stream connection.
199      *
200      * @param host the hostname to connect to
201      * @param port the port number that the server is listening on
202      * @param socketFactoryClassName the socket factory to use
203      * @param props the Properties from DriverManager.getConnection()
204      * @param conn the Connection that is creating us
205      * @param socketTimeout the timeout to set for the socket (0 means no
206      * timeout)
207      *
208      * @throws IOException if an IOException occurs during connect.
209      * @throws java.sql.SQLException if a database access error occurs.
210      */

211     protected MysqlIO(String JavaDoc host, int port, String JavaDoc socketFactoryClassName,
212         Properties JavaDoc props, com.mysql.jdbc.Connection conn, int socketTimeout)
213         throws IOException JavaDoc, java.sql.SQLException JavaDoc {
214         this.connection = conn;
215         this.reusablePacket = new Buffer(this.connection.getNetBufferLength());
216         this.port = port;
217         this.host = host;
218         this.socketFactoryClassName = socketFactoryClassName;
219         this.socketFactory = createSocketFactory();
220         this.mysqlConnection = socketFactory.connect(this.host, props);
221         this.clearStreamBeforeEachQuery = this.connection.alwaysClearStream();
222
223         if (socketTimeout != 0) {
224             try {
225                 this.mysqlConnection.setSoTimeout(socketTimeout);
226             } catch (Exception JavaDoc ex) {
227                 /* Ignore if the platform does not support it */
228             }
229         }
230
231         this.mysqlConnection = this.socketFactory.beforeHandshake();
232
233         if (!this.connection.isUsingUnbufferedInput()) {
234             this.mysqlInput = new BufferedInputStream JavaDoc(this.mysqlConnection
235                     .getInputStream(), 16384);
236         } else {
237             this.mysqlInput = this.mysqlConnection.getInputStream();
238         }
239
240         this.mysqlOutput = new BufferedOutputStream JavaDoc(this.mysqlConnection
241                 .getOutputStream(), 16384);
242         this.isInteractiveClient = this.connection.isInteractiveClient();
243     }
244
245     /**
246      * Should the driver generate SQL statement profiles?
247      *
248      * @param flag should the driver enable profiling?
249      */

250     protected void setProfileSql(boolean flag) {
251         this.profileSql = flag;
252     }
253
254     /**
255      * Build a result set. Delegates to buildResultSetWithRows() to build a
256      * JDBC-version-specific ResultSet, given rows as byte data, and field
257      * information.
258      *
259      * @param columnCount the number of columns in the result set
260      * @param maxRows the maximum number of rows to read (-1 means all rows)
261      * @param resultSetType the type of result set (CONCUR_UPDATABLE or
262      * READ_ONLY)
263      * @param streamResults should the result set be read all at once, or
264      * streamed?
265      * @param catalog the database name in use when the result set was created
266      *
267      * @return a result set
268      *
269      * @throws Exception if a database access error occurs
270      */

271     protected ResultSet getResultSet(long columnCount, int maxRows,
272         int resultSetType, boolean streamResults, String JavaDoc catalog)
273         throws Exception JavaDoc {
274         Buffer packet; // The packet from the server
275
Field[] fields = new Field[(int) columnCount];
276
277         // Read in the column information
278
for (int i = 0; i < columnCount; i++) {
279             packet = readPacket();
280             fields[i] = unpackField(packet, false);
281         }
282
283         packet = reuseAndReadPacket(this.reusablePacket);
284
285         RowData rowData = null;
286
287         if (!streamResults) {
288             ArrayList JavaDoc rows = new ArrayList JavaDoc();
289
290             // Now read the data
291
byte[][] rowBytes = nextRow((int) columnCount);
292             int rowCount = 0;
293
294             if (rowBytes != null) {
295                 rows.add(rowBytes);
296                 rowCount = 1;
297             }
298
299             while ((rowBytes != null) && (rowCount < maxRows)) {
300                 rowBytes = nextRow((int) columnCount);
301
302                 if (rowBytes != null) {
303                     rows.add(rowBytes);
304                     rowCount++;
305                 } else {
306                     if (Driver.TRACE) {
307                         Debug.msg(this, "* NULL Row *");
308                     }
309                 }
310             }
311
312             //
313
// Clear any outstanding data left on the wire
314
// when we've artifically limited the number of
315
// rows we retrieve (fix for BUG#1695)
316
//
317
if (rowCount <= maxRows) {
318                 clearInputStream();
319             }
320
321             if (Driver.TRACE) {
322                 Debug.msg(this,
323                     "* Fetched " + rows.size() + " rows from server *");
324             }
325
326             rowData = new RowDataStatic(rows);
327             reclaimLargeReusablePacket();
328         } else {
329             rowData = new RowDataDynamic(this, (int) columnCount);
330             this.streamingData = rowData;
331         }
332
333         return buildResultSetWithRows(catalog, fields, rowData, resultSetType);
334     }
335
336     /**
337      * Forcibly closes the underlying socket to MySQL.
338      */

339     protected final void forceClose() {
340         try {
341             if (this.mysqlInput != null) {
342                 this.mysqlInput.close();
343             }
344         } catch (IOException JavaDoc ioEx) {
345             // we can't do anything constructive about this
346
// Let the JVM clean it up later
347
this.mysqlInput = null;
348         }
349
350         try {
351             if (this.mysqlOutput != null) {
352                 this.mysqlOutput.close();
353             }
354         } catch (IOException JavaDoc ioEx) {
355             // we can't do anything constructive about this
356
// Let the JVM clean it up later
357
this.mysqlOutput = null;
358         }
359
360         try {
361             if (this.mysqlConnection != null) {
362                 this.mysqlConnection.close();
363             }
364         } catch (IOException JavaDoc ioEx) {
365             // we can't do anything constructive about this
366
// Let the JVM clean it up later
367
this.mysqlConnection = null;
368         }
369     }
370
371     /**
372      * Does the server send back extra column info?
373      *
374      * @return true if so
375      */

376     protected boolean hasLongColumnInfo() {
377         return this.hasLongColumnInfo;
378     }
379
380     /**
381      * Unpacks the Field information from the given packet. Understands pre 4.1
382      * and post 4.1 server version field packet structures.
383      *
384      * @param packet the packet containing the field information
385      * @param extractDefaultValues should default values be extracted?
386      *
387      * @return the unpacked field
388      */

389     protected final Field unpackField(Buffer packet,
390         boolean extractDefaultValues) {
391         if (this.use41Extensions) {
392             // we only store the position of the string and
393
// materialize only if needed...
394
if (this.has41NewNewProt) {
395                 int catalogNameStart = packet.getPosition() + 1;
396                 int catalogNameLength = packet.fastSkipLenString();
397             }
398
399             int databaseNameStart = packet.getPosition() + 1;
400             int databaseNameLength = packet.fastSkipLenString();
401
402             int tableNameStart = packet.getPosition() + 1;
403             int tableNameLength = packet.fastSkipLenString();
404
405             // orgTableName is never used so skip
406
int originalTableNameStart = packet.getPosition() + 1;
407             int originalTableNameLength = packet.fastSkipLenString();
408
409             // we only store the position again...
410
int nameStart = packet.getPosition() + 1;
411             int nameLength = packet.fastSkipLenString();
412
413             // orgColName is not required so skip...
414
int originalColumnNameStart = packet.getPosition() + 1;
415             int originalColumnNameLength = packet.fastSkipLenString();
416
417             packet.readByte();
418
419             int charSetNumber = packet.readInt();
420
421             int colLength = 0;
422
423             if (this.has41NewNewProt) {
424                 // fixme
425
colLength = (int) packet.readLong();
426             } else {
427                 colLength = packet.readLongInt();
428             }
429
430             int colType = packet.readByte() & 0xff;
431
432             short colFlag = 0;
433
434             if (this.hasLongColumnInfo) {
435                 colFlag = (short) (packet.readInt());
436             } else {
437                 colFlag = (short) (packet.readByte() & 0xff);
438             }
439
440             int colDecimals = packet.readByte() & 0xff;
441
442             int defaultValueStart = -1;
443             int defaultValueLength = -1;
444
445             if (extractDefaultValues) {
446                 defaultValueStart = packet.getPosition() + 1;
447                 defaultValueLength = packet.fastSkipLenString();
448             }
449
450             Field field = new Field(this.connection, packet.getByteBuffer(),
451                     databaseNameStart, databaseNameLength, tableNameStart,
452                     tableNameLength, originalTableNameStart,
453                     originalTableNameLength, nameStart, nameLength,
454                     originalColumnNameStart, originalColumnNameLength,
455                     colLength, colType, colFlag, colDecimals,
456                     defaultValueStart, defaultValueLength, charSetNumber);
457
458             return field;
459         } else {
460             int tableNameStart = packet.getPosition() + 1;
461             int tableNameLength = packet.fastSkipLenString();
462             int nameStart = packet.getPosition() + 1;
463             int nameLength = packet.fastSkipLenString();
464             int colLength = packet.readnBytes();
465             int colType = packet.readnBytes();
466             packet.readByte(); // We know it's currently 2
467

468             short colFlag = 0;
469
470             if (this.hasLongColumnInfo) {
471                 colFlag = (short) (packet.readInt());
472             } else {
473                 colFlag = (short) (packet.readByte() & 0xff);
474             }
475
476             int colDecimals = (packet.readByte() & 0xff);
477
478             if (this.colDecimalNeedsBump) {
479                 colDecimals++;
480             }
481
482             Field field = new Field(this.connection, packet.getBufferSource(),
483                     nameStart, nameLength, tableNameStart, tableNameLength,
484                     colLength, colType, colFlag, colDecimals);
485
486             return field;
487         }
488     }
489
490     /**
491      * Determines if the database charset is the same as the platform charset
492      */

493     protected void checkForCharsetMismatch() {
494         if (this.connection.useUnicode()
495                 && (this.connection.getEncoding() != null)) {
496             String JavaDoc encodingToCheck = jvmPlatformCharset;
497
498             if (encodingToCheck == null) {
499                 encodingToCheck = System.getProperty("file.encoding");
500             }
501
502             if (encodingToCheck == null) {
503                 this.platformDbCharsetMatches = false;
504             } else {
505                 this.platformDbCharsetMatches = encodingToCheck.equals(this.connection
506                         .getEncoding());
507             }
508         }
509     }
510
511     static int getMaxBuf() {
512         return maxBufferSize;
513     }
514
515     /**
516      * Get the major version of the MySQL server we are talking to.
517      *
518      * @return DOCUMENT ME!
519      */

520     final int getServerMajorVersion() {
521         return this.serverMajorVersion;
522     }
523
524     /**
525      * Get the minor version of the MySQL server we are talking to.
526      *
527      * @return DOCUMENT ME!
528      */

529     final int getServerMinorVersion() {
530         return this.serverMinorVersion;
531     }
532
533     /**
534      * Get the sub-minor version of the MySQL server we are talking to.
535      *
536      * @return DOCUMENT ME!
537      */

538     final int getServerSubMinorVersion() {
539         return this.serverSubMinorVersion;
540     }
541
542     /**
543      * Get the version string of the server we are talking to
544      *
545      * @return DOCUMENT ME!
546      */

547     String JavaDoc getServerVersion() {
548         return this.serverVersion;
549     }
550
551     /**
552      * Initialize communications with the MySQL server. Handles logging on, and
553      * handling initial connection errors.
554      *
555      * @param user DOCUMENT ME!
556      * @param password DOCUMENT ME!
557      * @param database DOCUMENT ME!
558      *
559      * @throws java.sql.SQLException DOCUMENT ME!
560      * @throws SQLException DOCUMENT ME!
561      */

562     void doHandshake(String JavaDoc user, String JavaDoc password, String JavaDoc database)
563         throws java.sql.SQLException JavaDoc {
564         // Read the first packet
565
Buffer buf = readPacket();
566
567         // Get the protocol version
568
this.protocolVersion = buf.readByte();
569
570         if (this.protocolVersion == -1) {
571             try {
572                 this.mysqlConnection.close();
573             } catch (Exception JavaDoc e) {
574                 ; // ignore
575
}
576
577             int errno = 2000;
578
579             errno = buf.readInt();
580
581             String JavaDoc serverErrorMessage = buf.readString();
582
583             StringBuffer JavaDoc errorBuf = new StringBuffer JavaDoc(" message from server: \"");
584             errorBuf.append(serverErrorMessage);
585             errorBuf.append("\"");
586
587             String JavaDoc xOpen = SQLError.mysqlToXOpen(errno);
588
589             throw new SQLException JavaDoc(SQLError.get(xOpen) + ", "
590                 + errorBuf.toString(), xOpen, errno);
591         }
592
593         this.serverVersion = buf.readString();
594
595         // Parse the server version into major/minor/subminor
596
int point = this.serverVersion.indexOf(".");
597
598         if (point != -1) {
599             try {
600                 int n = Integer.parseInt(this.serverVersion.substring(0, point));
601                 this.serverMajorVersion = n;
602             } catch (NumberFormatException JavaDoc NFE1) {
603                 ;
604             }
605
606             String JavaDoc remaining = this.serverVersion.substring(point + 1,
607                     this.serverVersion.length());
608             point = remaining.indexOf(".");
609
610             if (point != -1) {
611                 try {
612                     int n = Integer.parseInt(remaining.substring(0, point));
613                     this.serverMinorVersion = n;
614                 } catch (NumberFormatException JavaDoc nfe) {
615                     ;
616                 }
617
618                 remaining = remaining.substring(point + 1, remaining.length());
619
620                 int pos = 0;
621
622                 while (pos < remaining.length()) {
623                     if ((remaining.charAt(pos) < '0')
624                             || (remaining.charAt(pos) > '9')) {
625                         break;
626                     }
627
628                     pos++;
629                 }
630
631                 try {
632                     int n = Integer.parseInt(remaining.substring(0, pos));
633                     this.serverSubMinorVersion = n;
634                 } catch (NumberFormatException JavaDoc nfe) {
635                     ;
636                 }
637             }
638         }
639
640         if (versionMeetsMinimum(4, 0, 8)) {
641             this.maxThreeBytes = (256 * 256 * 256) - 1;
642             this.useNewLargePackets = true;
643         } else {
644             this.maxThreeBytes = 255 * 255 * 255;
645             this.useNewLargePackets = false;
646         }
647
648         this.colDecimalNeedsBump = versionMeetsMinimum(3, 23, 0);
649         this.colDecimalNeedsBump = !versionMeetsMinimum(3, 23, 15); // guess? Not noted in changelog
650
this.useNewUpdateCounts = versionMeetsMinimum(3, 22, 5);
651
652         long threadId = buf.readLong();
653         seed = buf.readString();
654
655         if (Driver.TRACE) {
656             Debug.msg(this, "Protocol Version: " + (int) this.protocolVersion);
657             Debug.msg(this, "Server Version: " + this.serverVersion);
658             Debug.msg(this, "Thread ID: " + threadId);
659             Debug.msg(this, "Crypt Seed: " + seed);
660         }
661
662         int serverCapabilities = 0;
663
664         if (buf.getPosition() < buf.getBufLength()) {
665             serverCapabilities = buf.readInt();
666         }
667
668         if (versionMeetsMinimum(4, 1, 1)) {
669             int position = buf.getPosition();
670
671             /* New protocol with 16 bytes to describe server characteristics */
672             int serverLanguage = buf.readInt(); // 2 bytes
673
buf.readInt();
674             buf.setPosition(position + 16);
675
676             String JavaDoc seedPart2 = buf.readString();
677             StringBuffer JavaDoc newSeed = new StringBuffer JavaDoc(20);
678             newSeed.append(seed);
679             newSeed.append(seedPart2);
680             this.seed = newSeed.toString();
681         }
682
683         if (((serverCapabilities & CLIENT_COMPRESS) != 0)
684                 && this.connection.useCompression()) {
685             clientParam |= CLIENT_COMPRESS;
686         }
687
688         if ((database != null) && (database.length() > 0)) {
689             clientParam |= CLIENT_CONNECT_WITH_DB;
690         }
691
692         if (((serverCapabilities & CLIENT_SSL) == 0)
693                 && this.connection.useSSL()) {
694             this.connection.setUseSSL(false);
695         }
696
697         if ((serverCapabilities & CLIENT_LONG_FLAG) != 0) {
698             // We understand other column flags, as well
699
clientParam |= CLIENT_LONG_FLAG;
700             this.hasLongColumnInfo = true;
701         }
702
703         // return FOUND rows
704
clientParam |= CLIENT_FOUND_ROWS;
705
706         if (this.connection.allowLoadLocalInfile()) {
707             clientParam |= CLIENT_LOCAL_FILES;
708         }
709
710         if (isInteractiveClient) {
711             clientParam |= CLIENT_INTERACTIVE;
712         }
713
714         // Authenticate
715
if (this.protocolVersion > 9) {
716             clientParam |= CLIENT_LONG_PASSWORD; // for long passwords
717
} else {
718             clientParam &= ~CLIENT_LONG_PASSWORD;
719         }
720
721         //
722
// 4.1 has some differences in the protocol
723
//
724
if (versionMeetsMinimum(4, 1, 0)) {
725             if (versionMeetsMinimum(4, 1, 1)) {
726                 clientParam |= CLIENT_PROTOCOL_41;
727                 this.has41NewNewProt = true;
728             } else {
729                 clientParam |= CLIENT_RESERVED;
730                 this.has41NewNewProt = false;
731             }
732
733             this.use41Extensions = true;
734         }
735
736         int passwordLength = 16;
737         int userLength = 0;
738         int databaseLength = 0;
739
740         if (user != null) {
741             userLength = user.length();
742         }
743
744         if (database != null) {
745             databaseLength = database.length();
746         }
747
748         int packLength = (userLength + passwordLength + databaseLength) + 7
749             + HEADER_LENGTH;
750         Buffer packet = null;
751
752         if (!connection.useSSL()) {
753             if ((serverCapabilities & CLIENT_SECURE_CONNECTION) != 0) {
754                 clientParam |= CLIENT_SECURE_CONNECTION;
755
756                 if (versionMeetsMinimum(4, 1, 1)) {
757                     secureAuth411(packLength, serverCapabilities, clientParam,
758                         user, password, database);
759                 } else {
760                     secureAuth(packLength, serverCapabilities, clientParam,
761                         user, password, database);
762                 }
763             } else {
764                 packet = new Buffer(packLength);
765
766                 if ((clientParam & CLIENT_RESERVED) != 0) {
767                     if (versionMeetsMinimum(4, 1, 1)) {
768                         packet.writeLong(clientParam);
769                         packet.writeLong(this.maxThreeBytes);
770
771                         // charset, JDBC will connect as 'latin1',
772
// and use 'SET NAMES' to change to the desired
773
// charset after the connection is established.
774
packet.writeByte((byte) 8);
775
776                         // Set of bytes reserved for future use.
777
packet.writeBytesNoNull(new byte[23]);
778                     } else {
779                         packet.writeLong(clientParam);
780                         packet.writeLong(this.maxThreeBytes);
781                     }
782                 } else {
783                     packet.writeInt((int) clientParam);
784                     packet.writeLongInt(this.maxThreeBytes);
785                 }
786
787                 // User/Password data
788
packet.writeString(user);
789
790                 if (this.protocolVersion > 9) {
791                     packet.writeString(Util.newCrypt(password, this.seed));
792                 } else {
793                     packet.writeString(Util.oldCrypt(password, this.seed));
794                 }
795
796                 if (((serverCapabilities & CLIENT_CONNECT_WITH_DB) != 0)
797                         && (database != null) && (database.length() > 0)) {
798                     packet.writeString(database);
799                 }
800
801                 send(packet);
802             }
803         } else {
804             boolean doSecureAuth = false;
805
806             if ((serverCapabilities & CLIENT_SECURE_CONNECTION) != 0) {
807                 clientParam |= CLIENT_SECURE_CONNECTION;
808                 doSecureAuth = true;
809             }
810
811             clientParam |= CLIENT_SSL;
812             packet = new Buffer(packLength);
813
814             if ((clientParam & CLIENT_RESERVED) != 0) {
815                 packet.writeLong(clientParam);
816             } else {
817                 packet.writeInt((int) clientParam);
818             }
819
820             send(packet);
821
822             javax.net.ssl.SSLSocketFactory sslFact = (javax.net.ssl.SSLSocketFactory) javax.net.ssl.SSLSocketFactory
823                 .getDefault();
824
825             try {
826                 this.mysqlConnection = sslFact.createSocket(this.mysqlConnection,
827                         this.host, this.port, true);
828
829                 // need to force TLSv1, or else JSSE tries to do a SSLv2 handshake
830
// which MySQL doesn't understand
831
((javax.net.ssl.SSLSocket) this.mysqlConnection)
832                 .setEnabledProtocols(new String JavaDoc[] { "TLSv1" });
833                 ((javax.net.ssl.SSLSocket) this.mysqlConnection).startHandshake();
834                 this.mysqlInput = new BufferedInputStream JavaDoc(this.mysqlConnection
835                         .getInputStream(), 16384);
836                 this.mysqlOutput = new BufferedOutputStream JavaDoc(this.mysqlConnection
837                         .getOutputStream(), 16384);
838                 this.mysqlOutput.flush();
839             } catch (IOException JavaDoc ioEx) {
840                 StringBuffer JavaDoc message = new StringBuffer JavaDoc(SQLError.get(
841                             SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE));
842                 message.append(": ");
843                 message.append(ioEx.getClass().getName());
844                 message.append(", underlying cause: ");
845                 message.append(ioEx.getMessage());
846
847                 if (!this.connection.useParanoidErrorMessages()) {
848                     message.append(Util.stackTraceToString(ioEx));
849                 }
850
851                 throw new java.sql.SQLException JavaDoc(message.toString(),
852                     SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE, 0);
853             }
854
855             packet.clear();
856
857             if (doSecureAuth) {
858                 if (versionMeetsMinimum(4, 1, 1)) {
859                     secureAuth411(packLength, serverCapabilities, clientParam,
860                         user, password, database);
861                 } else {
862                     secureAuth(packLength, serverCapabilities, clientParam,
863                         user, password, database);
864                 }
865             } else {
866                 if ((clientParam & CLIENT_RESERVED) != 0) {
867                     packet.writeLong(clientParam);
868                     packet.writeLong(this.maxThreeBytes);
869                 } else {
870                     packet.writeInt((int) clientParam);
871                     packet.writeLongInt(this.maxThreeBytes);
872                 }
873
874                 // User/Password data
875
packet.writeString(user);
876
877                 if (this.protocolVersion > 9) {
878                     packet.writeString(Util.newCrypt(password, seed));
879                 } else {
880                     packet.writeString(Util.oldCrypt(password, seed));
881                 }
882
883                 if (((serverCapabilities & CLIENT_CONNECT_WITH_DB) != 0)
884                         && (database != null) && (database.length() > 0)) {
885                     packet.writeString(database);
886                 }
887
888                 send(packet);
889             }
890         }
891
892         // Check for errors, not for 4.1.1 or newer,
893
// as the new auth protocol doesn't work that way
894
// (see secureAuth411() for more details...)
895
if (!versionMeetsMinimum(4, 1, 1)) {
896             checkErrorPacket();
897         }
898
899         //
900
// Can't enable compression until after handshake
901
//
902
if (((serverCapabilities & CLIENT_COMPRESS) != 0)
903                 && this.connection.useCompression()) {
904             // The following matches with ZLIB's
905
// compress()
906
this.deflater = new Deflater JavaDoc();
907             this.useCompression = true;
908             this.mysqlInput = new CompressedInputStream(this.mysqlInput);
909         }
910
911         if (((serverCapabilities & CLIENT_CONNECT_WITH_DB) == 0)
912                 && (database != null) && (database.length() > 0)) {
913             try {
914                 sendCommand(MysqlDefs.INIT_DB, database, null);
915             } catch (Exception JavaDoc ex) {
916                 throw new SQLException JavaDoc(ex.toString(),
917                     SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE);
918             }
919         }
920     }
921
922     /**
923      * Retrieve one row from the MySQL server. Note: this method is not
924      * thread-safe, but it is only called from methods that are guarded by
925      * synchronizing on this object.
926      *
927      * @param columnCount DOCUMENT ME!
928      *
929      * @return DOCUMENT ME!
930      *
931      * @throws Exception DOCUMENT ME!
932      */

933     final byte[][] nextRow(int columnCount) throws Exception JavaDoc {
934         // Get the next incoming packet, re-using the packet because
935
// all the data we need gets copied out of it.
936
Buffer rowPacket = checkErrorPacket();
937
938         //
939
// Didn't read an error, so re-position to beginning
940
// of packet in order to read result set data
941
//
942
int offset = 0;
943
944         //if (rowPacket.wasMultiPacket()) {
945
// if (this.useNewLargePackets) {
946
// offset = HEADER_LENGTH;
947
// } else {
948
// offset = HEADER_LENGTH + 1;
949
// }
950
//}
951
rowPacket.setPosition(rowPacket.getPosition() - 1);
952
953         byte[][] rowData = new byte[columnCount][];
954
955         if (!rowPacket.isLastDataPacket()) {
956             for (int i = 0; i < columnCount; i++) {
957                 rowData[i] = rowPacket.readLenByteArray(offset);
958
959                 if (Driver.TRACE) {
960                     if (rowData[i] == null) {
961                         Debug.msg(this, "Field value: NULL");
962                     } else {
963                         Debug.msg(this, "Field value: " + rowData[i].toString());
964                     }
965                 }
966             }
967
968             return rowData;
969         }
970
971         return null;
972     }
973
974     /**
975      * Log-off of the MySQL server and close the socket.
976      *
977      * @throws SQLException DOCUMENT ME!
978      */

979     final void quit() throws SQLException JavaDoc {
980         Buffer packet = new Buffer(6);
981         this.packetSequence = -1;
982         packet.writeByte((byte) MysqlDefs.QUIT);
983         send(packet);
984         forceClose();
985     }
986
987     /**
988      * Returns the packet used for sending data (used by PreparedStatement)
989      * Guarded by external synchronization on a mutex.
990      *
991      * @return A packet to send data with
992      */

993     Buffer getSharedSendPacket() {
994         if (this.sharedSendPacket == null) {
995             this.sharedSendPacket = new Buffer(this.connection
996                     .getNetBufferLength());
997         }
998
999         return this.sharedSendPacket;
1000    }
1001
1002    void closeStreamer(RowData streamer) throws SQLException JavaDoc {
1003        if (this.streamingData == null) {
1004            throw new SQLException JavaDoc("Attempt to close streaming result set "
1005                + streamer
1006                + " when no streaming result set was registered. This is an internal error.");
1007        }
1008
1009        if (streamer != this.streamingData) {
1010            throw new SQLException JavaDoc("Attempt to close streaming result set "
1011                + streamer + " that was not registered."
1012                + " Only one streaming result set may be open and in use per-connection. Ensure that you have called .close() on "
1013                + " any active result sets before attempting more queries.");
1014        }
1015
1016        this.streamingData = null;
1017    }
1018
1019    /**
1020     * Sets the buffer size to max-buf
1021     */

1022    void resetMaxBuf() {
1023        this.maxAllowedPacket = this.connection.getMaxAllowedPacket();
1024    }
1025
1026    /**
1027     * Send a command to the MySQL server If data is to be sent with command,
1028     * it should be put in ExtraData Raw packets can be sent by setting
1029     * QueryPacket to something other than null.
1030     *
1031     * @param command DOCUMENT ME!
1032     * @param extraData DOCUMENT ME!
1033     * @param queryPacket DOCUMENT ME!
1034     *
1035     * @return DOCUMENT ME!
1036     *
1037     * @throws Exception DOCUMENT ME!
1038     * @throws java.sql.SQLException DOCUMENT ME!
1039     */

1040    final Buffer sendCommand(int command, String JavaDoc extraData, Buffer queryPacket)
1041        throws Exception JavaDoc {
1042        checkForOutstandingStreamingData();
1043
1044        try {
1045            if (this.clearStreamBeforeEachQuery) {
1046                clearInputStream();
1047            }
1048
1049            //
1050
// PreparedStatements construct their own packets,
1051
// for efficiency's sake.
1052
//
1053
// If this is a generic query, we need to re-use
1054
// the sending packet.
1055
//
1056
if (queryPacket == null) {
1057                int packLength = HEADER_LENGTH + COMP_HEADER_LENGTH + 1
1058                    + ((extraData != null) ? extraData.length() : 0) + 2;
1059
1060                if (this.sendPacket == null) {
1061                    this.sendPacket = new Buffer(packLength);
1062                }
1063
1064                this.packetSequence = -1;
1065                this.sendPacket.clear();
1066
1067                // Offset different for compression
1068
if (this.useCompression) {
1069                    this.sendPacket.setPosition(this.sendPacket.getPosition()
1070                        + COMP_HEADER_LENGTH);
1071                }
1072
1073                this.sendPacket.writeByte((byte) command);
1074
1075                if ((command == MysqlDefs.INIT_DB)
1076                        || (command == MysqlDefs.CREATE_DB)
1077                        || (command == MysqlDefs.DROP_DB)
1078                        || (command == MysqlDefs.QUERY)) {
1079                    this.sendPacket.writeStringNoNull(extraData);
1080                } else if (command == MysqlDefs.PROCESS_KILL) {
1081                    long id = new Long JavaDoc(extraData).longValue();
1082                    this.sendPacket.writeLong(id);
1083                } else if ((command == MysqlDefs.RELOAD)
1084                        && (this.protocolVersion > 9)) {
1085                    Debug.msg(this, "Reload");
1086
1087                    //Packet.writeByte(reloadParam);
1088
}
1089
1090                send(this.sendPacket);
1091            } else {
1092                this.packetSequence = -1;
1093                send(queryPacket); // packet passed by PreparedStatement
1094
}
1095        } catch (SQLException JavaDoc sqlEx) {
1096            // don't wrap SQLExceptions
1097
throw sqlEx;
1098        } catch (Exception JavaDoc ex) {
1099            String JavaDoc underlyingMessage = ex.getMessage();
1100
1101            throw new java.sql.SQLException JavaDoc(SQLError.get(
1102                    SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE) + ": "
1103                + ex.getClass().getName() + ", "
1104                + ((underlyingMessage != null) ? underlyingMessage
1105                                               : "no message given by JVM"),
1106                SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE, 0);
1107        }
1108
1109        return checkErrorPacket(command);
1110    }
1111
1112    /**
1113     * Send a query specified in the String "Query" to the MySQL server. This
1114     * method uses the specified character encoding to get the bytes from the
1115     * query string.
1116     *
1117     * @param query DOCUMENT ME!
1118     * @param maxRows DOCUMENT ME!
1119     * @param characterEncoding DOCUMENT ME!
1120     * @param conn DOCUMENT ME!
1121     * @param resultSetType DOCUMENT ME!
1122     * @param streamResults DOCUMENT ME!
1123     * @param catalog DOCUMENT ME!
1124     *
1125     * @return DOCUMENT ME!
1126     *
1127     * @throws Exception DOCUMENT ME!
1128     */

1129    final ResultSet sqlQuery(String JavaDoc query, int maxRows,
1130        String JavaDoc characterEncoding, Connection conn, int resultSetType,
1131        boolean streamResults, String JavaDoc catalog) throws Exception JavaDoc {
1132        // We don't know exactly how many bytes we're going to get
1133
// from the query. Since we're dealing with Unicode, the
1134
// max is 2, so pad it (2 * query) + space for headers
1135
int packLength = HEADER_LENGTH + 1 + (query.length() * 2) + 2;
1136
1137        if (this.sendPacket == null) {
1138            this.sendPacket = new Buffer(packLength);
1139        } else {
1140            this.sendPacket.clear();
1141        }
1142
1143        this.sendPacket.writeByte((byte) MysqlDefs.QUERY);
1144
1145        if (characterEncoding != null) {
1146            SingleByteCharsetConverter converter = this.connection
1147                .getCharsetConverter(characterEncoding);
1148
1149            if (this.platformDbCharsetMatches) {
1150                this.sendPacket.writeStringNoNull(query, characterEncoding,
1151                    converter);
1152            } else {
1153                if (StringUtils.startsWithIgnoreCaseAndWs(query, "LOAD DATA")) {
1154                    this.sendPacket.writeBytesNoNull(query.getBytes());
1155                } else {
1156                    this.sendPacket.writeStringNoNull(query, characterEncoding,
1157                        converter);
1158                }
1159            }
1160        } else {
1161            this.sendPacket.writeStringNoNull(query);
1162        }
1163
1164        return sqlQueryDirect(this.sendPacket, maxRows, conn, resultSetType,
1165            streamResults, catalog);
1166    }
1167
1168    /**
1169     * Send a query stored in a packet directly to the server.
1170     *
1171     * @param queryPacket DOCUMENT ME!
1172     * @param maxRows DOCUMENT ME!
1173     * @param conn DOCUMENT ME!
1174     * @param resultSetType DOCUMENT ME!
1175     * @param streamResults DOCUMENT ME!
1176     * @param catalog DOCUMENT ME!
1177     *
1178     * @return DOCUMENT ME!
1179     *
1180     * @throws Exception DOCUMENT ME!
1181     */

1182    final ResultSet sqlQueryDirect(Buffer queryPacket, int maxRows,
1183        Connection conn, int resultSetType, boolean streamResults,
1184        String JavaDoc catalog) throws Exception JavaDoc {
1185        StringBuffer JavaDoc profileMsgBuf = null; // used if profiling
1186
long queryStartTime = 0;
1187
1188        if (this.profileSql) {
1189            profileMsgBuf = new StringBuffer JavaDoc();
1190            queryStartTime = System.currentTimeMillis();
1191
1192            byte[] queryBuf = queryPacket.getByteBuffer();
1193
1194            // Extract the actual query from the network packet
1195
String JavaDoc query = new String JavaDoc(queryBuf, 5,
1196                    (queryPacket.getPosition() - 5));
1197            profileMsgBuf.append("Query\t\"");
1198            profileMsgBuf.append(query);
1199            profileMsgBuf.append("\"\texecution time:\t");
1200        }
1201
1202        // Send query command and sql query string
1203
Buffer resultPacket = sendCommand(MysqlDefs.QUERY, null, queryPacket);
1204
1205        if (this.profileSql) {
1206            long executionTime = System.currentTimeMillis() - queryStartTime;
1207            profileMsgBuf.append(executionTime);
1208            profileMsgBuf.append("\t");
1209        }
1210
1211        resultPacket.setPosition(resultPacket.getPosition() - 1);
1212
1213        long columnCount = resultPacket.readFieldLength();
1214
1215        if (Driver.TRACE) {
1216            Debug.msg(this, "Column count: " + columnCount);
1217        }
1218
1219        if (columnCount == 0) {
1220            if (this.profileSql) {
1221                System.err.println(profileMsgBuf.toString());
1222            }
1223
1224            return buildResultSetWithUpdates(resultPacket);
1225        } else if (columnCount == Buffer.NULL_LENGTH) {
1226            String JavaDoc charEncoding = null;
1227
1228            if (this.connection.useUnicode()) {
1229                charEncoding = this.connection.getEncoding();
1230            }
1231
1232            String JavaDoc fileName = null;
1233
1234            if (this.platformDbCharsetMatches) {
1235                fileName = ((charEncoding != null)
1236                    ? resultPacket.readString(charEncoding)
1237                    : resultPacket.readString());
1238            } else {
1239                fileName = resultPacket.readString();
1240            }
1241
1242            return sendFileToServer(fileName);
1243        } else {
1244            long fetchStartTime = 0;
1245
1246            if (this.profileSql) {
1247                fetchStartTime = System.currentTimeMillis();
1248            }
1249
1250            com.mysql.jdbc.ResultSet results = getResultSet(columnCount,
1251                    maxRows, resultSetType, streamResults, catalog);
1252
1253            if (this.profileSql) {
1254                long fetchElapsedTime = System.currentTimeMillis()
1255                    - fetchStartTime;
1256                profileMsgBuf.append("result set fetch time:\t");
1257                profileMsgBuf.append(fetchElapsedTime);
1258                System.err.println(profileMsgBuf.toString());
1259            }
1260
1261            return results;
1262        }
1263    }
1264
1265    /**
1266     * Returns the host this IO is connected to
1267     *
1268     * @return DOCUMENT ME!
1269     */

1270    String JavaDoc getHost() {
1271        return this.host;
1272    }
1273
1274    /**
1275     * Does the version of the MySQL server we are connected to meet the given
1276     * minimums?
1277     *
1278     * @param major DOCUMENT ME!
1279     * @param minor DOCUMENT ME!
1280     * @param subminor DOCUMENT ME!
1281     *
1282     * @return DOCUMENT ME!
1283     */

1284    boolean versionMeetsMinimum(int major, int minor, int subminor) {
1285        if (getServerMajorVersion() >= major) {
1286            if (getServerMajorVersion() == major) {
1287                if (getServerMinorVersion() >= minor) {
1288                    if (getServerMinorVersion() == minor) {
1289                        return (getServerSubMinorVersion() >= subminor);
1290                    } else {
1291                        // newer than major.minor
1292
return true;
1293                    }
1294                } else {
1295                    // older than major.minor
1296
return false;
1297                }
1298            } else {
1299                // newer than major
1300
return true;
1301            }
1302        } else {
1303            return false;
1304        }
1305    }
1306
1307    private final int readFully(InputStream JavaDoc in, byte[] b, int off, int len)
1308        throws IOException JavaDoc {
1309        if (len < 0) {
1310            throw new IndexOutOfBoundsException JavaDoc();
1311        }
1312
1313        int n = 0;
1314
1315        while (n < len) {
1316            int count = in.read(b, off + n, len - n);
1317
1318            if (count < 0) {
1319                throw new EOFException JavaDoc();
1320            }
1321
1322            n += count;
1323        }
1324
1325        return n;
1326    }
1327
1328    /**
1329     * Read one packet from the MySQL server
1330     *
1331     * @return DOCUMENT ME!
1332     *
1333     * @throws SQLException DOCUMENT ME!
1334     * @throws java.sql.SQLException DOCUMENT ME!
1335     */

1336    private final Buffer readPacket() throws SQLException JavaDoc {
1337        try {
1338            int lengthRead = readFully(mysqlInput, this.packetHeaderBuf, 0, 4);
1339
1340            if (lengthRead < 4) {
1341                forceClose();
1342                throw new IOException JavaDoc("Unexpected end of input stream");
1343            }
1344
1345            int packetLength = ((int) (this.packetHeaderBuf[0] & 0xff))
1346                + (((int) (this.packetHeaderBuf[1] & 0xff)) << 8)
1347                + (((int) (this.packetHeaderBuf[2] & 0xff)) << 16);
1348
1349            byte multiPacketSeq = this.packetHeaderBuf[3];
1350
1351            // Read data
1352
byte[] buffer = new byte[packetLength + 1];
1353            readFully(this.mysqlInput, buffer, 0, packetLength);
1354            buffer[packetLength] = 0;
1355
1356            Buffer packet = new Buffer(buffer);
1357
1358            return packet;
1359        } catch (IOException JavaDoc ioEx) {
1360            StringBuffer JavaDoc message = new StringBuffer JavaDoc(SQLError.get(
1361                        SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE));
1362            message.append(": ");
1363            message.append(ioEx.getClass().getName());
1364            message.append(", underlying cause: ");
1365            message.append(ioEx.getMessage());
1366
1367            if (!this.connection.useParanoidErrorMessages()) {
1368                message.append(Util.stackTraceToString(ioEx));
1369            }
1370
1371            throw new java.sql.SQLException JavaDoc(message.toString(),
1372                SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE, 0);
1373        }
1374    }
1375
1376    private com.mysql.jdbc.ResultSet buildResultSetWithRows(String JavaDoc catalog,
1377        com.mysql.jdbc.Field[] fields, RowData rows, int resultSetConcurrency)
1378        throws SQLException JavaDoc {
1379        switch (resultSetConcurrency) {
1380        case java.sql.ResultSet.CONCUR_READ_ONLY:
1381            return new com.mysql.jdbc.ResultSet(catalog, fields, rows,
1382                this.connection);
1383
1384        case java.sql.ResultSet.CONCUR_UPDATABLE:
1385            return new com.mysql.jdbc.UpdatableResultSet(catalog, fields, rows,
1386                this.connection);
1387
1388        default:
1389            return new com.mysql.jdbc.ResultSet(catalog, fields, rows,
1390                this.connection);
1391        }
1392    }
1393
1394    private com.mysql.jdbc.ResultSet buildResultSetWithUpdates(
1395        Buffer resultPacket) throws SQLException JavaDoc {
1396        long updateCount = -1;
1397        long updateID = -1;
1398        String JavaDoc info = null;
1399
1400        try {
1401            if (this.useNewUpdateCounts) {
1402                updateCount = resultPacket.newReadLength();
1403                updateID = resultPacket.newReadLength();
1404            } else {
1405                updateCount = (long) resultPacket.readLength();
1406                updateID = (long) resultPacket.readLength();
1407            }
1408
1409            if (this.connection.isReadInfoMsgEnabled()) {
1410                if (this.use41Extensions) {
1411                    int serverStatus = resultPacket.readInt();
1412                    int warningCount = resultPacket.readInt();
1413
1414                    resultPacket.readByte(); // advance pointer
1415
}
1416
1417                info = resultPacket.readString();
1418            }
1419        } catch (Exception JavaDoc ex) {
1420            throw new java.sql.SQLException JavaDoc(SQLError.get(
1421                    SQLError.SQL_STATE_GENERAL_ERROR) + ": "
1422                + ex.getClass().getName(), SQLError.SQL_STATE_GENERAL_ERROR, -1);
1423        }
1424
1425        if (Driver.TRACE) {
1426            Debug.msg(this, "Update Count = " + updateCount);
1427        }
1428
1429        ResultSet updateRs = new ResultSet(updateCount, updateID);
1430
1431        if (info != null) {
1432            updateRs.setServerInfo(info);
1433        }
1434
1435        return updateRs;
1436    }
1437
1438    /**
1439     * Don't hold on to overly-large packets
1440     */

1441    private void reclaimLargeReusablePacket() {
1442        if ((this.reusablePacket != null)
1443                && (this.reusablePacket.getBufLength() > 1048576)) {
1444            this.reusablePacket = new Buffer(this.connection.getNetBufferLength());
1445        }
1446    }
1447
1448    /**
1449     * Re-use a packet to read from the MySQL server
1450     *
1451     * @param reuse DOCUMENT ME!
1452     *
1453     * @return DOCUMENT ME!
1454     *
1455     * @throws SQLException DOCUMENT ME!
1456     * @throws SQLException DOCUMENT ME!
1457     */

1458    private final Buffer reuseAndReadPacket(Buffer reuse)
1459        throws SQLException JavaDoc {
1460        try {
1461            reuse.setWasMultiPacket(false);
1462
1463            int lengthRead = readFully(mysqlInput, this.packetHeaderBuf, 0, 4);
1464
1465            if (lengthRead < 4) {
1466                forceClose();
1467                throw new IOException JavaDoc("Unexpected end of input stream");
1468            }
1469
1470            int packetLength = ((int) (this.packetHeaderBuf[0] & 0xff))
1471                + (((int) (this.packetHeaderBuf[1] & 0xff)) << 8)
1472                + (((int) (this.packetHeaderBuf[2] & 0xff)) << 16);
1473
1474            byte multiPacketSeq = this.packetHeaderBuf[3];
1475
1476            //byte multiPacketSeq = (byte) this.mysqlInput.read();
1477
// Set the Buffer to it's original state
1478
reuse.setPosition(0);
1479            reuse.setSendLength(0);
1480
1481            // Do we need to re-alloc the byte buffer?
1482
//
1483
// Note: We actually check the length of the buffer,
1484
// rather than getBufLength(), because getBufLength() is not
1485
// necesarily the actual length of the byte array
1486
// used as the buffer
1487
if (reuse.getByteBuffer().length <= packetLength) {
1488                reuse.setByteBuffer(new byte[packetLength + 1]);
1489            }
1490
1491            // Set the new length
1492
reuse.setBufLength(packetLength);
1493
1494            // Read the data from the server
1495
readFully(this.mysqlInput, reuse.getByteBuffer(), 0, packetLength);
1496
1497            boolean isMultiPacket = false;
1498
1499            if (packetLength == maxThreeBytes) {
1500                reuse.setPosition((int) maxThreeBytes);
1501
1502                int packetEndPoint = packetLength;
1503
1504                // it's multi-packet
1505
isMultiPacket = true;
1506
1507                lengthRead = readFully(mysqlInput, this.packetHeaderBuf, 0, 4);
1508
1509                if (lengthRead < 4) {
1510                    forceClose();
1511                    throw new IOException JavaDoc("Unexpected end of input stream");
1512                }
1513
1514                packetLength = ((int) (this.packetHeaderBuf[0] & 0xff))
1515                    + (((int) (this.packetHeaderBuf[1] & 0xff)) << 8)
1516                    + (((int) (this.packetHeaderBuf[2] & 0xff)) << 16);
1517
1518                Buffer multiPacket = new Buffer(packetLength);
1519                boolean firstMultiPkt = true;
1520
1521                while (true) {
1522                    if (!firstMultiPkt) {
1523                        lengthRead = readFully(mysqlInput,
1524                                this.packetHeaderBuf, 0, 4);
1525
1526                        if (lengthRead < 4) {
1527                            forceClose();
1528                            throw new IOException JavaDoc(
1529                                "Unexpected end of input stream");
1530                        }
1531
1532                        packetLength = ((int) (this.packetHeaderBuf[0] & 0xff))
1533                            + (((int) (this.packetHeaderBuf[1] & 0xff)) << 8)
1534                            + (((int) (this.packetHeaderBuf[2] & 0xff)) << 16);
1535                    } else {
1536                        firstMultiPkt = false;
1537                    }
1538
1539                    if (!this.useNewLargePackets && (packetLength == 1)) {
1540                        clearInputStream();
1541
1542                        break;
1543                    } else if (packetLength < this.maxThreeBytes) {
1544                        byte newPacketSeq = this.packetHeaderBuf[3];
1545
1546                        if (newPacketSeq != (multiPacketSeq + 1)) {
1547                            throw new IOException JavaDoc(
1548                                "Packets received out of order");
1549                        }
1550
1551                        multiPacketSeq = newPacketSeq;
1552
1553                        // Set the Buffer to it's original state
1554
multiPacket.setPosition(0);
1555                        multiPacket.setSendLength(0);
1556
1557                        // Set the new length
1558
multiPacket.setBufLength(packetLength);
1559
1560                        // Read the data from the server
1561
byte[] byteBuf = multiPacket.getByteBuffer();
1562                        int lengthToWrite = packetLength;
1563
1564                        int bytesRead = readFully(this.mysqlInput, byteBuf, 0,
1565                                packetLength);
1566
1567                        if (bytesRead != lengthToWrite) {
1568                            throw new SQLException JavaDoc(
1569                                "Short read from server, expected "
1570                                + lengthToWrite + " bytes, received only "
1571                                + bytesRead + ".",
1572                                SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE);
1573                        }
1574
1575                        reuse.writeBytesNoNull(byteBuf, 0, lengthToWrite);
1576
1577                        packetEndPoint += lengthToWrite;
1578
1579                        break; // end of multipacket sequence
1580
}
1581
1582                    byte newPacketSeq = this.packetHeaderBuf[3];
1583
1584                    if (newPacketSeq != (multiPacketSeq + 1)) {
1585                        throw new IOException JavaDoc("Packets received out of order");
1586                    }
1587
1588                    multiPacketSeq = newPacketSeq;
1589
1590                    // Set the Buffer to it's original state
1591
multiPacket.setPosition(0);
1592                    multiPacket.setSendLength(0);
1593
1594                    // Set the new length
1595
multiPacket.setBufLength(packetLength);
1596
1597                    // Read the data from the server
1598
byte[] byteBuf = multiPacket.getByteBuffer();
1599                    int lengthToWrite = packetLength;
1600
1601                    int bytesRead = readFully(this.mysqlInput, byteBuf, 0,
1602                            packetLength);
1603
1604                    if (bytesRead != lengthToWrite) {
1605                        throw new SQLException JavaDoc(
1606                            "Short read from server, expected " + lengthToWrite
1607                            + " bytes, received only " + bytesRead + ".",
1608                            SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE);
1609                    }
1610
1611                    reuse.writeBytesNoNull(byteBuf, 0, lengthToWrite);
1612
1613                    packetEndPoint += lengthToWrite;
1614                }
1615
1616                //reuse.writeByte((byte) 0);
1617
reuse.setPosition(0);
1618                reuse.setWasMultiPacket(true);
1619            }
1620
1621            if (!isMultiPacket) {
1622                reuse.getByteBuffer()[packetLength] = 0; // Null-termination
1623
}
1624
1625            return reuse;
1626        } catch (IOException JavaDoc ioEx) {
1627            StringBuffer JavaDoc message = new StringBuffer JavaDoc(SQLError.get(
1628                        SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE));
1629            message.append(": ");
1630            message.append(ioEx.getClass().getName());
1631            message.append(", underlying cause: ");
1632            message.append(ioEx.getMessage());
1633
1634            if (!this.connection.useParanoidErrorMessages()) {
1635                message.append(Util.stackTraceToString(ioEx));
1636            }
1637
1638            throw new java.sql.SQLException JavaDoc(message.toString(),
1639                SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE, 0);
1640        }
1641    }
1642
1643    /**
1644     * Send a packet to the MySQL server
1645     *
1646     * @param packet DOCUMENT ME!
1647     *
1648     * @throws SQLException DOCUMENT ME!
1649     */

1650    private final void send(Buffer packet) throws SQLException JavaDoc {
1651        int l = packet.getPosition();
1652        send(packet, l);
1653
1654        //
1655
// Don't hold on to large packets
1656
//
1657
if (packet == this.sharedSendPacket) {
1658            reclaimLargeSharedSendPacket();
1659        }
1660    }
1661
1662    private final void send(Buffer packet, int packetLen)
1663        throws SQLException JavaDoc {
1664        try {
1665            if (packetLen > this.maxAllowedPacket) {
1666                throw new PacketTooBigException(packetLen, this.maxAllowedPacket);
1667            }
1668
1669            if ((serverMajorVersion >= 4) && (packetLen >= maxThreeBytes)) {
1670                sendSplitPackets(packet);
1671            } else {
1672                this.packetSequence++;
1673
1674                Buffer packetToSend = packet;
1675
1676                packetToSend.setPosition(0);
1677
1678                if (this.useCompression) {
1679                    packetToSend = compressPacket(packet, 0, packetLen,
1680                            HEADER_LENGTH);
1681                    packetLen = packetToSend.getPosition();
1682                } else {
1683                    packetToSend.writeLongInt(packetLen - HEADER_LENGTH);
1684                    packetToSend.writeByte(this.packetSequence);
1685                }
1686
1687                this.mysqlOutput.write(packetToSend.getByteBuffer(), 0,
1688                    packetLen);
1689                this.mysqlOutput.flush();
1690            }
1691
1692            //
1693
// Don't hold on to large packets
1694
//
1695
if (packet == this.sharedSendPacket) {
1696                reclaimLargeSharedSendPacket();
1697            }
1698        } catch (IOException JavaDoc ioEx) {
1699            StringBuffer JavaDoc message = new StringBuffer JavaDoc(SQLError.get(
1700                        SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE));
1701            message.append(": ");
1702            message.append(ioEx.getClass().getName());
1703            message.append(", underlying cause: ");
1704            message.append(ioEx.getMessage());
1705
1706            if (!this.connection.useParanoidErrorMessages()) {
1707                message.append(Util.stackTraceToString(ioEx));
1708            }
1709
1710            throw new java.sql.SQLException JavaDoc(message.toString(),
1711                SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE, 0);
1712        }
1713    }
1714
1715    /**
1716     * Reads and sends a file to the server for LOAD DATA LOCAL INFILE
1717     *
1718     * @param fileName the file name to send.
1719     *
1720     * @return DOCUMENT ME!
1721     *
1722     * @throws SQLException DOCUMENT ME!
1723     */

1724    private final ResultSet sendFileToServer(String JavaDoc fileName)
1725        throws SQLException JavaDoc {
1726        Buffer filePacket = (loadFileBufRef == null) ? null
1727                                                     : (Buffer) (loadFileBufRef
1728            .get());
1729
1730        int packetLength = Math.min(this.connection.getMaxAllowedPacket()
1731                - (HEADER_LENGTH * 3),
1732                alignPacketSize(this.connection.getMaxAllowedPacket() - 16, 4096)
1733                - (HEADER_LENGTH * 3));
1734
1735        //
1736
// This packet may be _way_ too large to actually allocate,
1737
// unforunately, LOAD DATA LOCAL INFILE requires this setup...
1738
//
1739
try {
1740            if (filePacket == null) {
1741                filePacket = new Buffer((int) (packetLength + HEADER_LENGTH));
1742                loadFileBufRef = new SoftReference JavaDoc(filePacket);
1743            }
1744        } catch (OutOfMemoryError JavaDoc oom) {
1745            // Attempt to do this, but it might not work...
1746
// The server is expecting at least one packet, so we
1747
// send an empty 'EOF' packet...
1748
this.reusablePacket.clear();
1749            send(this.reusablePacket);
1750
1751            throw new SQLException JavaDoc("Unable to allocate packet of size '"
1752                + (packetLength + HEADER_LENGTH)
1753                + "' for LOAD DATA LOCAL INFILE. Either increase heap space available to your JVM, or adjust the MySQL server variable 'max_allowed_packet'",
1754                SQLError.SQL_STATE_MEMORY_ALLOCATION_FAILURE);
1755        }
1756
1757        filePacket.clear();
1758        send(filePacket, 0);
1759
1760        byte[] fileBuf = new byte[packetLength];
1761
1762        BufferedInputStream JavaDoc fileIn = null;
1763
1764        try {
1765            fileIn = new BufferedInputStream JavaDoc(new FileInputStream JavaDoc(fileName));
1766
1767            int bytesRead = 0;
1768
1769            while ((bytesRead = fileIn.read(fileBuf)) != -1) {
1770                filePacket.clear();
1771                filePacket.writeBytesNoNull(fileBuf, 0, bytesRead);
1772                send(filePacket);
1773            }
1774        } catch (IOException JavaDoc ioEx) {
1775            StringBuffer JavaDoc messageBuf = new StringBuffer JavaDoc("Unable to open file ");
1776
1777            if (!this.connection.useParanoidErrorMessages()) {
1778                messageBuf.append("'");
1779
1780                if (fileName != null) {
1781                    messageBuf.append(fileName);
1782                }
1783
1784                messageBuf.append("'");
1785            }
1786
1787            messageBuf.append("for 'LOAD DATA LOCAL INFILE' command.");
1788
1789            if (!this.connection.useParanoidErrorMessages()) {
1790                messageBuf.append("Due to underlying IOException: ");
1791                messageBuf.append(Util.stackTraceToString(ioEx));
1792            }
1793
1794            throw new SQLException JavaDoc(messageBuf.toString(),
1795                SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
1796        } finally {
1797            if (fileIn != null) {
1798                try {
1799                    fileIn.close();
1800                } catch (Exception JavaDoc ex) {
1801                    throw new SQLException JavaDoc("Unable to close local file during LOAD DATA LOCAL INFILE command",
1802                        SQLError.SQL_STATE_GENERAL_ERROR);
1803                }
1804
1805                fileIn = null;
1806            } else {
1807                // file open failed, but server needs one packet
1808
filePacket.clear();
1809                send(filePacket);
1810            }
1811        }
1812
1813        // send empty packet to mark EOF
1814
filePacket.clear();
1815        send(filePacket);
1816
1817        Buffer resultPacket = checkErrorPacket();
1818
1819        return buildResultSetWithUpdates(resultPacket);
1820    }
1821
1822    /**
1823     * Checks for errors in the reply packet, and if none, returns the reply
1824     * packet, ready for reading
1825     *
1826     * @return DOCUMENT ME!
1827     *
1828     * @throws SQLException DOCUMENT ME!
1829     */

1830    private Buffer checkErrorPacket() throws SQLException JavaDoc {
1831        return checkErrorPacket(-1);
1832    }
1833
1834    /**
1835     * Checks for errors in the reply packet, and if none, returns the reply
1836     * packet, ready for reading
1837     *
1838     * @param command the command being issued (if used)
1839     *
1840     * @return DOCUMENT ME!
1841     *
1842     * @throws SQLException if an error packet was received
1843     * @throws java.sql.SQLException DOCUMENT ME!
1844     */

1845    private Buffer checkErrorPacket(int command) throws SQLException JavaDoc {
1846        int statusCode = 0;
1847        Buffer resultPacket = null;
1848
1849        try {
1850            // Check return value, if we get a java.io.EOFException,
1851
// the server has gone away. We'll pass it on up the
1852
// exception chain and let someone higher up decide
1853
// what to do (barf, reconnect, etc).
1854
resultPacket = reuseAndReadPacket(this.reusablePacket);
1855            statusCode = resultPacket.readByte();
1856        } catch (SQLException JavaDoc sqlEx) {
1857            // don't wrap SQLExceptions
1858
throw sqlEx;
1859        } catch (Exception JavaDoc fallThru) {
1860            String JavaDoc underlyingMessage = fallThru.getMessage();
1861
1862            throw new java.sql.SQLException JavaDoc(SQLError.get(
1863                    SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE) + ": "
1864                + fallThru.getClass().getName() + ", "
1865                + ((underlyingMessage != null) ? underlyingMessage
1866                                               : "no message given by JVM"),
1867                SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE, 0);
1868        }
1869
1870        // Error handling
1871
if (statusCode == (byte) 0xff) {
1872            String JavaDoc serverErrorMessage;
1873            int errno = 2000;
1874
1875            if (this.protocolVersion > 9) {
1876                errno = resultPacket.readInt();
1877
1878                String JavaDoc xOpen = null;
1879
1880                serverErrorMessage = resultPacket.readString();
1881
1882                if (serverErrorMessage.startsWith("#")) {
1883                    // we have an SQLState
1884
if (serverErrorMessage.length() > 6) {
1885                        xOpen = serverErrorMessage.substring(1, 6);
1886                        serverErrorMessage = serverErrorMessage.substring(6);
1887
1888                        if (xOpen.equals("HY000")) {
1889                            xOpen = SQLError.mysqlToXOpen(errno);
1890                        }
1891                    } else {
1892                        xOpen = SQLError.mysqlToXOpen(errno);
1893                    }
1894                } else {
1895                    xOpen = SQLError.mysqlToXOpen(errno);
1896                }
1897
1898                clearInputStream();
1899
1900                StringBuffer JavaDoc errorBuf = new StringBuffer JavaDoc(
1901                        " message from server: \"");
1902                errorBuf.append(serverErrorMessage);
1903                errorBuf.append("\"");
1904
1905                throw new SQLException JavaDoc(SQLError.get(xOpen) + ", "
1906                    + errorBuf.toString(), xOpen, errno);
1907            } else {
1908                serverErrorMessage = resultPacket.readString();
1909                clearInputStream();
1910
1911                if (serverErrorMessage.indexOf("Unknown column") != -1) {
1912                    throw new java.sql.SQLException JavaDoc(SQLError.get(
1913                            SQLError.SQL_STATE_COLUMN_NOT_FOUND) + ", "
1914                        + serverErrorMessage,
1915                        SQLError.SQL_STATE_COLUMN_NOT_FOUND, -1);
1916                } else {
1917                    StringBuffer JavaDoc errorBuf = new StringBuffer JavaDoc(
1918                            " message from server: \"");
1919                    errorBuf.append(serverErrorMessage);
1920                    errorBuf.append("\"");
1921
1922                    throw new java.sql.SQLException JavaDoc(SQLError.get(
1923                            SQLError.SQL_STATE_GENERAL_ERROR) + ", "
1924                        + errorBuf.toString(),
1925                        SQLError.SQL_STATE_GENERAL_ERROR, -1);
1926                }
1927            }
1928        }
1929
1930        return resultPacket;
1931    }
1932
1933    /**
1934     * Sends a large packet to the server as a series of smaller packets
1935     *
1936     * @param packet DOCUMENT ME!
1937     *
1938     * @throws SQLException DOCUMENT ME!
1939     * @throws SQLException DOCUMENT ME!
1940     */

1941    private final void sendSplitPackets(Buffer packet)
1942        throws SQLException JavaDoc {
1943        try {
1944            //
1945
// Big packets are handled by splitting them in packets of MAX_THREE_BYTES
1946
// length. The last packet is always a packet that is < MAX_THREE_BYTES.
1947
// (The last packet may even have a length of 0)
1948
//
1949
//
1950
// NB: Guarded by execSQL. If the driver changes architecture, this
1951
// will need to be synchronized in some other way
1952
//
1953
Buffer headerPacket = (splitBufRef == null) ? null
1954                                                        : (Buffer) (splitBufRef
1955                .get());
1956
1957            //
1958
// Store this packet in a soft reference...It can be re-used if not GC'd (so clients
1959
// that use it frequently won't have to re-alloc the 16M buffer), but we don't
1960
// penalize infrequent users of large packets by keeping 16M allocated all of the time
1961
//
1962
if (headerPacket == null) {
1963                headerPacket = new Buffer((int) (maxThreeBytes + HEADER_LENGTH));
1964                splitBufRef = new SoftReference JavaDoc(headerPacket);
1965            }
1966
1967            int len = packet.getPosition();
1968            int splitSize = (int) maxThreeBytes;
1969            int originalPacketPos = HEADER_LENGTH;
1970            byte[] origPacketBytes = packet.getByteBuffer();
1971            byte[] headerPacketBytes = headerPacket.getByteBuffer();
1972
1973            if (Driver.DEBUG) {
1974                System.out.println("\n\nSending split packets for packet of "
1975                    + len + " bytes:\n");
1976            }
1977
1978            while (len >= maxThreeBytes) {
1979                headerPacket.setPosition(0);
1980                headerPacket.writeLongInt(splitSize);
1981                this.packetSequence++;
1982                headerPacket.writeByte(this.packetSequence);
1983                System.arraycopy(origPacketBytes, originalPacketPos,
1984                    headerPacketBytes, 4, splitSize);
1985                this.mysqlOutput.write(headerPacketBytes, 0,
1986                    splitSize + HEADER_LENGTH);
1987                this.mysqlOutput.flush();
1988
1989                if (Driver.DEBUG) {
1990                    System.out.print(" total packet length (header & data) "
1991                        + (splitSize + HEADER_LENGTH) + "\nheader: ");
1992                    headerPacket.dumpHeader();
1993                    System.out.println();
1994                    System.out.print("last eight bytes: ");
1995                    headerPacket.dumpNBytes(((splitSize + HEADER_LENGTH) - 8), 8);
1996                    System.out.println();
1997                }
1998
1999                originalPacketPos += splitSize;
2000                len -= splitSize;
2001            }
2002
2003            //
2004
// Write last packet
2005
//
2006
headerPacket.clear();
2007            headerPacket.setPosition(0);
2008            headerPacket.writeLongInt(len - HEADER_LENGTH);
2009            this.packetSequence++;
2010            headerPacket.writeByte(this.packetSequence);
2011
2012            if (len != 0) {
2013                System.arraycopy(origPacketBytes, originalPacketPos,
2014                    headerPacketBytes, 4, len - HEADER_LENGTH);
2015            }
2016
2017            this.mysqlOutput.write(headerPacket.getByteBuffer(), 0, len);
2018            this.mysqlOutput.flush();
2019
2020            if (Driver.DEBUG) {
2021                System.out.print(" total packet length (header & data) " + len
2022                    + ",\nheader: ");
2023                headerPacket.dumpHeader();
2024                System.out.println();
2025                System.out.print("last packet bytes: ");
2026                headerPacket.dumpNBytes(0, len);
2027                System.out.println();
2028            }
2029        } catch (IOException JavaDoc ioEx) {
2030            StringBuffer JavaDoc message = new StringBuffer JavaDoc(SQLError.get(
2031                        SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE));
2032            message.append(": ");
2033            message.append(ioEx.getClass().getName());
2034            message.append(", underlying cause: ");
2035            message.append(ioEx.getMessage());
2036
2037            if (!this.connection.useParanoidErrorMessages()) {
2038                message.append(Util.stackTraceToString(ioEx));
2039            }
2040
2041            throw new java.sql.SQLException JavaDoc(message.toString(),
2042                SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE, 0);
2043        }
2044    }
2045
2046    private int alignPacketSize(int a, int l) {
2047        return ((((a) + (l)) - 1) & ~((l) - 1));
2048    }
2049
2050    private void checkForOutstandingStreamingData() throws SQLException JavaDoc {
2051        if (this.streamingData != null) {
2052            if (!this.connection.getClobberStreamingResults()) {
2053                throw new SQLException JavaDoc("Streaming result set "
2054                    + this.streamingData + " is still active."
2055                    + " Only one streaming result set may be open and in use per-connection. Ensure that you have called .close() on "
2056                    + " any active result sets before attempting more queries.");
2057            } else {
2058                // Close the result set
2059
this.streamingData.getOwner().realClose(false);
2060
2061                // clear any pending data....
2062
clearInputStream();
2063            }
2064        }
2065    }
2066
2067    private void clearInputStream() throws SQLException JavaDoc {
2068        try {
2069            int len = this.mysqlInput.available();
2070
2071            while (len > 0) {
2072                this.mysqlInput.skip(len);
2073                len = this.mysqlInput.available();
2074            }
2075        } catch (IOException JavaDoc ioEx) {
2076            throw new SQLException JavaDoc("I/O error while clearing input stream of old results",
2077                SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE);
2078        }
2079    }
2080
2081    private Buffer compressPacket(Buffer packet, int offset, int packetLen,
2082        int headerLength) throws SQLException JavaDoc {
2083        packet.writeLongInt(packetLen - headerLength);
2084        packet.writeByte((byte) 0); // wrapped packet has 0 packet seq.
2085

2086        int lengthToWrite = 0;
2087        int compressedLength = 0;
2088        byte[] bytesToCompress = packet.getByteBuffer();
2089        byte[] compressedBytes = null;
2090        int offsetWrite = 0;
2091
2092        if (true /*packetLen < MIN_COMPRESS_LEN*/) {
2093            lengthToWrite = packetLen;
2094            compressedBytes = packet.getByteBuffer();
2095            compressedLength = 0;
2096            offsetWrite = offset;
2097        } else {
2098            compressedBytes = new byte[bytesToCompress.length * 2];
2099
2100            this.deflater.reset();
2101            this.deflater.setInput(bytesToCompress, offset, packetLen);
2102            this.deflater.finish();
2103
2104            int compLen = this.deflater.deflate(compressedBytes);
2105
2106            if (compLen > packetLen) {
2107                lengthToWrite = packetLen;
2108                compressedBytes = packet.getByteBuffer();
2109                compressedLength = 0;
2110                offsetWrite = offset;
2111            } else {
2112                lengthToWrite = compLen;
2113                headerLength += COMP_HEADER_LENGTH;
2114                compressedLength = packetLen;
2115            }
2116        }
2117
2118        Buffer compressedPacket = new Buffer(packetLen + headerLength);
2119
2120        compressedPacket.setPosition(0);
2121        compressedPacket.writeLongInt(lengthToWrite);
2122        compressedPacket.writeByte(this.packetSequence);
2123        compressedPacket.writeLongInt(compressedLength);
2124        compressedPacket.writeBytesNoNull(compressedBytes, offsetWrite,
2125            lengthToWrite);
2126
2127        return compressedPacket;
2128    }
2129
2130    private SocketFactory createSocketFactory() throws SQLException JavaDoc {
2131        try {
2132            if (socketFactoryClassName == null) {
2133                throw new SQLException JavaDoc("No name specified for socket factory",
2134                    SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE);
2135            }
2136
2137            return (SocketFactory) (Class.forName(socketFactoryClassName)
2138                                         .newInstance());
2139        } catch (Exception JavaDoc ex) {
2140            throw new SQLException JavaDoc("Could not create socket factory '"
2141                + socketFactoryClassName + "' due to underlying exception: "
2142                + ex.toString(),
2143                SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE);
2144        }
2145    }
2146
2147    /**
2148     * Ensures that we don't hold on to overly-large send packets
2149     */

2150    private void reclaimLargeSharedSendPacket() {
2151        if ((this.sharedSendPacket != null)
2152                && (this.sharedSendPacket.getBufLength() > 1048576)) {
2153            this.sharedSendPacket = new Buffer(this.connection
2154                    .getNetBufferLength());
2155        }
2156    }
2157
2158    /**
2159     * Secure authentication for 4.1 and newer servers.
2160     *
2161     * @param packLength
2162     * @param serverCapabilities
2163     * @param clientParam
2164     * @param user
2165     * @param password
2166     * @param database DOCUMENT ME!
2167     *
2168     * @throws SQLException
2169     */

2170    private void secureAuth(int packLength, int serverCapabilities,
2171        long clientParam, String JavaDoc user, String JavaDoc password, String JavaDoc database)
2172        throws SQLException JavaDoc {
2173        // Passwords can be 16 chars long
2174
Buffer packet = new Buffer(packLength);
2175
2176        if (this.use41Extensions) {
2177            if (versionMeetsMinimum(4, 1, 1)) {
2178                packet.writeLong(clientParam);
2179                packet.writeLong(this.maxThreeBytes);
2180
2181                // charset, JDBC will connect as 'latin1',
2182
// and use 'SET NAMES' to change to the desired
2183
// charset after the connection is established.
2184
packet.writeByte((byte) 8);
2185
2186                // Set of bytes reserved for future use.
2187
packet.writeBytesNoNull(new byte[23]);
2188            } else {
2189                packet.writeLong(clientParam);
2190                packet.writeLong(this.maxThreeBytes);
2191            }
2192        } else {
2193            packet.writeInt((int) clientParam);
2194            packet.writeLongInt(this.maxThreeBytes);
2195        }
2196
2197        // User/Password data
2198
packet.writeString(user);
2199
2200        if (password.length() != 0) {
2201            /* Prepare false scramble */
2202            packet.writeString(FALSE_SCRAMBLE);
2203        } else {
2204            /* For empty password*/
2205            packet.writeString("");
2206        }
2207
2208        if (((serverCapabilities & CLIENT_CONNECT_WITH_DB) != 0)
2209                && (database != null) && (database.length() > 0)) {
2210            packet.writeString(database);
2211        }
2212
2213        send(packet);
2214
2215        //
2216
// Don't continue stages if password is empty
2217
//
2218
if (password.length() > 0) {
2219            Buffer b = readPacket();
2220
2221            b.setPosition(0);
2222
2223            byte[] replyAsBytes = b.getByteBuffer();
2224
2225            if ((replyAsBytes.length == 25) && (replyAsBytes[0] != 0)) {
2226                // Old passwords will have '*' at the first byte of hash */
2227
if (replyAsBytes[0] != '*') {
2228                    try {
2229                        /* Build full password hash as it is required to decode scramble */
2230                        byte[] buff = Security.passwordHashStage1(password);
2231
2232                        /* Store copy as we'll need it later */
2233                        byte[] passwordHash = new byte[buff.length];
2234                        System.arraycopy(buff, 0, passwordHash, 0, buff.length);
2235
2236                        /* Finally hash complete password using hash we got from server */
2237                        passwordHash = Security.passwordHashStage2(passwordHash,
2238                                replyAsBytes);
2239
2240                        byte[] packetDataAfterSalt = new byte[replyAsBytes.length
2241                            - 5];
2242
2243                        System.arraycopy(replyAsBytes, 4, packetDataAfterSalt,
2244                            0, replyAsBytes.length - 5);
2245
2246                        byte[] mysqlScrambleBuff = new byte[20];
2247
2248                        /* Decypt and store scramble 4 = hash for stage2 */
2249                        Security.passwordCrypt(packetDataAfterSalt,
2250                            mysqlScrambleBuff, passwordHash, 20);
2251
2252                        /* Encode scramble with password. Recycle buffer */
2253                        Security.passwordCrypt(mysqlScrambleBuff, buff, buff, 20);
2254
2255                        Buffer packet2 = new Buffer(25);
2256                        packet2.writeBytesNoNull(buff);
2257
2258                        this.packetSequence++;
2259
2260                        send(packet2, 24);
2261                    } catch (NoSuchAlgorithmException JavaDoc nse) {
2262                        throw new SQLException JavaDoc(
2263                            "Failed to create message digest 'SHA-1' for authentication. "
2264                            + " You must use a JDK that supports JCE to be able to use secure connection authentication",
2265                            SQLError.SQL_STATE_GENERAL_ERROR);
2266                    }
2267                } else {
2268                    try {
2269                        /* Create password to decode scramble */
2270                        byte[] passwordHash = Security.createKeyFromOldPassword(password);
2271
2272                        /* Decypt and store scramble 4 = hash for stage2 */
2273                        byte[] netReadPos4 = new byte[replyAsBytes.length - 5];
2274
2275                        System.arraycopy(replyAsBytes, 4, netReadPos4, 0,
2276                            replyAsBytes.length - 5);
2277
2278                        byte[] mysqlScrambleBuff = new byte[20];
2279
2280                        /* Decypt and store scramble 4 = hash for stage2 */
2281                        Security.passwordCrypt(netReadPos4, mysqlScrambleBuff,
2282                            passwordHash, 20);
2283
2284                        /* Finally scramble decoded scramble with password */
2285                        String JavaDoc scrambledPassword = Util.scramble(new String JavaDoc(
2286                                    mysqlScrambleBuff), password);
2287
2288                        Buffer packet2 = new Buffer(packLength);
2289
2290                        packet2.writeString(scrambledPassword);
2291                        this.packetSequence++;
2292
2293                        send(packet2, 24);
2294                    } catch (NoSuchAlgorithmException JavaDoc nse) {
2295                        throw new SQLException JavaDoc(
2296                            "Failed to create message digest 'SHA-1' for authentication. "
2297                            + " You must use a JDK that supports JCE to be able to use secure connection authentication",
2298                            SQLError.SQL_STATE_GENERAL_ERROR);
2299                    }
2300                }
2301            }
2302        }
2303    }
2304
2305    /**
2306     * Secure authentication for 4.1.1 and newer servers.
2307     *
2308     * @param packLength
2309     * @param serverCapabilities DOCUMENT ME!
2310     * @param clientParam
2311     * @param user
2312     * @param password
2313     * @param database DOCUMENT ME!
2314     *
2315     * @throws SQLException
2316     */

2317    private void secureAuth411(int packLength, int serverCapabilities,
2318        long clientParam, String JavaDoc user, String JavaDoc password, String JavaDoc database)
2319        throws SQLException JavaDoc {
2320        // SERVER: public_seed=create_random_string()
2321
// send(public_seed)
2322
//
2323
// CLIENT: recv(public_seed)
2324
// hash_stage1=sha1("password")
2325
// hash_stage2=sha1(hash_stage1)
2326
// reply=xor(hash_stage1, sha1(public_seed,hash_stage2)
2327
//
2328
// // this three steps are done in scramble()
2329
//
2330
// send(reply)
2331
//
2332
//
2333
// SERVER: recv(reply)
2334
// hash_stage1=xor(reply, sha1(public_seed,hash_stage2))
2335
// candidate_hash2=sha1(hash_stage1)
2336
// check(candidate_hash2==hash_stage2)
2337
// Passwords can be 16 chars long
2338
Buffer packet = new Buffer(packLength);
2339
2340        if (this.use41Extensions) {
2341            if (versionMeetsMinimum(4, 1, 1)) {
2342                packet.writeLong(this.clientParam);
2343                packet.writeLong(this.maxThreeBytes);
2344
2345                // charset, JDBC will connect as 'latin1',
2346
// and use 'SET NAMES' to change to the desired
2347
// charset after the connection is established.
2348
packet.writeByte((byte) 8);
2349
2350                // Set of bytes reserved for future use.
2351
packet.writeBytesNoNull(new byte[23]);
2352            } else {
2353                packet.writeLong(this.clientParam);
2354                packet.writeLong(this.maxThreeBytes);
2355            }
2356        } else {
2357            packet.writeInt((int) this.clientParam);
2358            packet.writeLongInt(this.maxThreeBytes);
2359        }
2360
2361        // User/Password data
2362
packet.writeString(user);
2363
2364        if (password.length() != 0) {
2365            packet.writeByte((byte) 0x14);
2366
2367            try {
2368                packet.writeBytesNoNull(Security.scramble411(password, this.seed));
2369            } catch (NoSuchAlgorithmException JavaDoc nse) {
2370                throw new SQLException JavaDoc(
2371                    "Failed to create message digest 'SHA-1' for authentication. "
2372                    + " You must use a JDK that supports JCE to be able to use secure connection authentication",
2373                    SQLError.SQL_STATE_GENERAL_ERROR);
2374            }
2375        } else {
2376            /* For empty password*/
2377            packet.writeByte((byte) 0);
2378        }
2379
2380        if (((serverCapabilities & CLIENT_CONNECT_WITH_DB) != 0)
2381                && (database != null) && (database.length() > 0)) {
2382            packet.writeString(database);
2383        }
2384
2385        send(packet);
2386
2387        byte savePacketSequence = this.packetSequence++;
2388
2389        Buffer reply = checkErrorPacket();
2390
2391        if (reply.isLastDataPacket()) {
2392            /*
2393              By sending this very specific reply server asks us to send scrambled
2394              password in old format. The reply contains scramble_323.
2395            */

2396            this.packetSequence = ++savePacketSequence;
2397            packet.clear();
2398
2399            String JavaDoc seed323 = this.seed.substring(0, 8);
2400            packet.writeString(Util.newCrypt(password, seed323));
2401            send(packet);
2402
2403            /* Read what server thinks about out new auth message report */
2404            checkErrorPacket();
2405        }
2406    }
2407}
2408
Popular Tags