Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/mysql/jdbc/MysqlIO.java


1   /*
2    Copyright (C) 2002-2004 MySQL AB
3   
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of version 2 of the GNU General Public License as
6    published by the Free Software Foundation.
7    
8   
9    There are special exceptions to the terms and conditions of the GPL 
10   as it is applied to this software. View the full text of the 
11   exception exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
12   software distribution.
13  
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18  
19   You should have received a copy of the GNU General Public License
20   along with this program; if not, write to the Free Software
21   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  
23   */
24  package com.mysql.jdbc;
25  
26  import java.io.BufferedInputStream;
27  import java.io.BufferedOutputStream;
28  import java.io.ByteArrayOutputStream;
29  import java.io.EOFException;
30  import java.io.FileInputStream;
31  import java.io.IOException;
32  import java.io.InputStream;
33  import java.io.OutputStreamWriter;
34  import java.lang.ref.SoftReference;
35  import java.net.Socket;
36  import java.security.NoSuchAlgorithmException;
37  import java.sql.SQLException;
38  import java.sql.SQLWarning;
39  import java.util.ArrayList;
40  import java.util.Properties;
41  import java.util.zip.Deflater;
42  import java.util.zip.Inflater;
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.61 2004/08/27 21:50:12 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 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 jvmPlatformCharset = null;
85  
86      static {
87          OutputStreamWriter 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(new ByteArrayOutputStream());
96              jvmPlatformCharset = outWriter.getEncoding();
97          } finally {
98              try {
99                if (outWriter != null) {
100                 outWriter.close();
101               }
102             } catch (IOException ioEx) {
103                 // ignore
104             }
105         }
106     }
107 
108     //
109     // Use this when reading in rows to avoid thousands of new()
110     // calls, because the byte arrays just get copied out of the
111     // packet anyway
112     //
113     private Buffer reusablePacket = null;
114     private Buffer sendPacket = null;
115     private Buffer sharedSendPacket = null;
116 
117     /** Data to the server */
118 
119     //private DataOutputStream     _Mysql_Output             = null;
120     private BufferedOutputStream mysqlOutput = null;
121     private com.mysql.jdbc.Connection connection;
122     private Deflater deflater = null;
123     private Inflater inflater = null;
124 
125     /** Buffered data from the server */
126 
127     //private BufferedInputStream  _Mysql_Buf_Input          = null;
128 
129     /** Buffered data to the server */
130 
131     //private BufferedOutputStream _Mysql_Buf_Output         = null;
132 
133     /** Data from the server */
134 
135     //private DataInputStream      _Mysql_Input              = null;
136     private InputStream mysqlInput = null;
137     private RowData streamingData = null;
138 
139     //
140     // For SQL Warnings
141     //
142     private SQLWarning warningChain = null;
143 
144     /** The connection to the server */
145     private Socket mysqlConnection = null;
146     private SocketFactory socketFactory = null;
147 
148     //
149     // Packet used for 'LOAD DATA LOCAL INFILE'
150     //
151     // We use a SoftReference, so that we don't penalize intermittent
152     // use of this feature
153     //
154     private SoftReference loadFileBufRef;
155 
156     //
157     // Used to send large packets to the server versions 4+
158     // We use a SoftReference, so that we don't penalize intermittent
159     // use of this feature
160     //
161     private SoftReference splitBufRef;
162     private String host = null;
163     private String seed;
164     private String serverVersion = null;
165     private String socketFactoryClassName = null;
166     private byte[] packetHeaderBuf = new byte[4];
167     private boolean clearStreamBeforeEachQuery = false;
168     private boolean colDecimalNeedsBump = false; // do we need to increment the colDecimal flag?
169     private boolean has41NewNewProt = false;
170 
171     /** Does the server support long column info? */
172     private boolean hasLongColumnInfo = false;
173     private boolean isInteractiveClient = false;
174 
175     /**
176      * Does the character set of this connection match the character set of the
177      * platform
178      */
179     private boolean platformDbCharsetMatches = true;
180     private boolean profileSql = false;
181 
182     /** Should we use 4.1 protocol extensions? */
183     private boolean use41Extensions = false;
184     private boolean useCompression = false;
185     private boolean useNewLargePackets = false;
186     private boolean useNewUpdateCounts = false; // should we use the new larger update counts?
187     private byte packetSequence = 0;
188     private byte protocolVersion = 0;
189     private int clientParam = 0;
190 
191     // changed once we've connected.
192     private int maxAllowedPacket = 1024 * 1024;
193     private int maxThreeBytes = 255 * 255 * 255;
194     private int port = 3306;
195     private int serverCapabilities;
196     private int serverMajorVersion = 0;
197     private int serverMinorVersion = 0;
198     private int serverSubMinorVersion = 0;
199   protected int serverCharsetIndex;
200   private static final int MAX_QUERY_LENGTH_TO_LOG =  4 * 1024; // 4K
201 
202     /**
203      * Constructor:  Connect to the MySQL server and setup a stream connection.
204      *
205      * @param host the hostname to connect to
206      * @param port the port number that the server is listening on
207      * @param socketFactoryClassName the socket factory to use
208      * @param props the Properties from DriverManager.getConnection()
209      * @param conn the Connection that is creating us
210      * @param socketTimeout the timeout to set for the socket (0 means no
211      *        timeout)
212      *
213      * @throws IOException if an IOException occurs during connect.
214      * @throws java.sql.SQLException if a database access error occurs.
215      */
216     protected MysqlIO(String host, int port, String socketFactoryClassName,
217         Properties props, com.mysql.jdbc.Connection conn, int socketTimeout)
218         throws IOException, java.sql.SQLException {
219         this.connection = conn;
220         this.reusablePacket = new Buffer(this.connection.getNetBufferLength());
221         this.port = port;
222         this.host = host;
223         this.socketFactoryClassName = socketFactoryClassName;
224         this.socketFactory = createSocketFactory();
225         this.mysqlConnection = socketFactory.connect(this.host, this.port, props);
226         this.clearStreamBeforeEachQuery = this.connection.alwaysClearStream();
227 
228         if (socketTimeout != 0) {
229             try {
230                 this.mysqlConnection.setSoTimeout(socketTimeout);
231             } catch (Exception ex) {
232                 /* Ignore if the platform does not support it */
233             }
234         }
235 
236         this.mysqlConnection = this.socketFactory.beforeHandshake();
237 
238         if (!this.connection.isUsingUnbufferedInput()) {
239             this.mysqlInput = new BufferedInputStream(this.mysqlConnection
240                     .getInputStream(), 16384);
241         } else {
242             this.mysqlInput = this.mysqlConnection.getInputStream();
243         }
244 
245         this.mysqlOutput = new BufferedOutputStream(this.mysqlConnection
246                 .getOutputStream(), 16384);
247         this.isInteractiveClient = this.connection.isInteractiveClient();
248     }
249 
250     /**
251      * Should the driver generate SQL statement profiles?
252      *
253      * @param flag should the driver enable profiling?
254      */
255     protected void setProfileSql(boolean flag) {
256         this.profileSql = flag;
257     }
258 
259     /**
260      * Build a result set. Delegates to buildResultSetWithRows() to build a
261      * JDBC-version-specific ResultSet, given rows as byte data, and field
262      * information.
263      *
264      * @param columnCount the number of columns in the result set
265      * @param maxRows the maximum number of rows to read (-1 means all rows)
266      * @param resultSetType the type of result set (CONCUR_UPDATABLE or
267      *        READ_ONLY)
268      * @param streamResults should the result set be read all at once, or
269      *        streamed?
270      * @param catalog the database name in use when the result set was created
271      *
272      * @return a result set
273      *
274      * @throws Exception if a database access error occurs
275      */
276     protected ResultSet getResultSet(long columnCount, int maxRows,
277         int resultSetType, boolean streamResults, String catalog)
278         throws Exception {
279         Buffer packet; // The packet from the server
280         Field[] fields = new Field[(int) columnCount];
281 
282         // Read in the column information
283         for (int i = 0; i < columnCount; i++) {
284             packet = readPacket();
285             fields[i] = unpackField(packet, false);
286         }
287 
288         packet = reuseAndReadPacket(this.reusablePacket);
289 
290         RowData rowData = null;
291 
292         if (!streamResults) {
293             ArrayList rows = new ArrayList();
294 
295             // Now read the data
296             byte[][] rowBytes = nextRow((int) columnCount);
297             int rowCount = 0;
298 
299             if (rowBytes != null) {
300                 rows.add(rowBytes);
301                 rowCount = 1;
302             }
303 
304             while ((rowBytes != null) && (rowCount < maxRows)) {
305                 rowBytes = nextRow((int) columnCount);
306 
307                 if (rowBytes != null) {
308                     rows.add(rowBytes);
309                     rowCount++;
310                 } else {
311                     if (Driver.TRACE) {
312                         Debug.msg(this, "* NULL Row *");
313                     }
314                 }
315             }
316 
317             //
318             // Clear any outstanding data left on the wire
319             // when we've artifically limited the number of 
320             // rows we retrieve (fix for BUG#1695)
321             //
322             if (rowCount <= maxRows) {
323                 clearInputStream();
324             }
325 
326             if (Driver.TRACE) {
327                 Debug.msg(this,
328                     "* Fetched " + rows.size() + " rows from server *");
329             }
330 
331             rowData = new RowDataStatic(rows);
332             reclaimLargeReusablePacket();
333         } else {
334             rowData = new RowDataDynamic(this, (int) columnCount);
335             this.streamingData = rowData;
336         }
337 
338         return buildResultSetWithRows(catalog, fields, rowData, resultSetType);
339     }
340 
341     /**
342      * Forcibly closes the underlying socket to MySQL.
343      */
344     protected final void forceClose() {
345         try {
346             if (this.mysqlInput != null) {
347                 this.mysqlInput.close();
348             }
349         } catch (IOException ioEx) {
350             // we can't do anything constructive about this
351             // Let the JVM clean it up later
352             this.mysqlInput = null;
353         }
354 
355         try {
356             if (this.mysqlOutput != null) {
357                 this.mysqlOutput.close();
358             }
359         } catch (IOException ioEx) {
360             // we can't do anything constructive about this
361             // Let the JVM clean it up later
362             this.mysqlOutput = null;
363         }
364 
365         try {
366             if (this.mysqlConnection != null) {
367                 this.mysqlConnection.close();
368             }
369         } catch (IOException ioEx) {
370             // we can't do anything constructive about this
371             // Let the JVM clean it up later
372             this.mysqlConnection = null;
373         }
374     }
375 
376     /**
377      * Re-authenticates as the given user and password
378      *
379      * @param userName DOCUMENT ME!
380      * @param password DOCUMENT ME!
381      *
382      * @throws SQLException DOCUMENT ME!
383      */
384     protected void changeUser(String userName, String password, String database)
385         throws SQLException {
386         this.packetSequence = -1;
387 
388         int passwordLength = 16;
389         int userLength = 0;
390 
391         if (userName != null) {
392             userLength = userName.length();
393         }
394 
395         int packLength = (userLength + passwordLength) + 7 + HEADER_LENGTH;
396 
397         if ((this.serverCapabilities & CLIENT_SECURE_CONNECTION) != 0) {
398             Buffer changeUserPacket = new Buffer(packLength + 1);
399             changeUserPacket.writeByte((byte) MysqlDefs.COM_CHANGE_USER);
400 
401             if (versionMeetsMinimum(4, 1, 1)) {
402                 secureAuth411(changeUserPacket, packLength, userName, password,
403                     database, false);
404             } else {
405                 secureAuth(changeUserPacket, packLength, userName, password,
406                     database, false);
407             }
408         } else {
409             // Passwords can be 16 chars long
410             Buffer packet = new Buffer(packLength + 1);
411             packet.writeByte((byte) MysqlDefs.COM_CHANGE_USER);
412 
413             // User/Password data
414             packet.writeString(userName);
415 
416             if (this.protocolVersion > 9) {
417                 packet.writeString(Util.newCrypt(password, this.seed));
418             } else {
419                 packet.writeString(Util.oldCrypt(password, this.seed));
420             }
421 
422             if (((serverCapabilities & CLIENT_CONNECT_WITH_DB) != 0)
423                     && (database != null) && (database.length() > 0)) {
424                 packet.writeString(database);
425             }
426             
427             send(packet);
428             checkErrorPacket();
429         }
430     }
431 
432     /**
433      * Does the server send back extra column info?
434      *
435      * @return true if so
436      */
437     protected boolean hasLongColumnInfo() {
438         return this.hasLongColumnInfo;
439     }
440 
441     /**
442      * Unpacks the Field information from the given packet. Understands pre 4.1
443      * and post 4.1 server version field packet structures.
444      *
445      * @param packet the packet containing the field information
446      * @param extractDefaultValues should default values be extracted?
447      *
448      * @return the unpacked field
449      */
450     protected final Field unpackField(Buffer packet,
451         boolean extractDefaultValues) throws SQLException {
452         if (this.use41Extensions) {
453             // we only store the position of the string and
454             // materialize only if needed...
455             if (this.has41NewNewProt) {
456                 int catalogNameStart = packet.getPosition() + 1;
457                 int catalogNameLength = packet.fastSkipLenString();
458             }
459 
460             int databaseNameStart = packet.getPosition() + 1;
461             int databaseNameLength = packet.fastSkipLenString();
462 
463             int tableNameStart = packet.getPosition() + 1;
464             int tableNameLength = packet.fastSkipLenString();
465 
466             // orgTableName is never used so skip
467             int originalTableNameStart = packet.getPosition() + 1;
468             int originalTableNameLength = packet.fastSkipLenString();
469 
470             // we only store the position again...
471             int nameStart = packet.getPosition() + 1;
472             int nameLength = packet.fastSkipLenString();
473 
474             // orgColName is not required so skip...
475             int originalColumnNameStart = packet.getPosition() + 1;
476             int originalColumnNameLength = packet.fastSkipLenString();
477 
478             packet.readByte();
479 
480             int charSetNumber = packet.readInt();
481 
482             long colLength = 0;
483 
484             if (this.has41NewNewProt) {
485                 colLength = packet.readLong();
486             } else {
487                 colLength = packet.readLongInt();
488             }
489 
490             int colType = packet.readByte() & 0xff;
491 
492             short colFlag = 0;
493 
494             if (this.hasLongColumnInfo) {
495                 colFlag = (short) (packet.readInt());
496             } else {
497                 colFlag = (short) (packet.readByte() & 0xff);
498             }
499 
500             int colDecimals = packet.readByte() & 0xff;
501 
502             int defaultValueStart = -1;
503             int defaultValueLength = -1;
504 
505             if (extractDefaultValues) {
506                 defaultValueStart = packet.getPosition() + 1;
507                 defaultValueLength = packet.fastSkipLenString();
508             }
509 
510             Field field = new Field(this.connection, packet.getByteBuffer(),
511                     databaseNameStart, databaseNameLength, tableNameStart,
512                     tableNameLength, originalTableNameStart,
513                     originalTableNameLength, nameStart, nameLength,
514                     originalColumnNameStart, originalColumnNameLength,
515                     colLength, colType, colFlag, colDecimals,
516                     defaultValueStart, defaultValueLength, charSetNumber);
517 
518             return field;
519         } else {
520             int tableNameStart = packet.getPosition() + 1;
521             int tableNameLength = packet.fastSkipLenString();
522             int nameStart = packet.getPosition() + 1;
523             int nameLength = packet.fastSkipLenString();
524             int colLength = packet.readnBytes();
525             int colType = packet.readnBytes();
526             packet.readByte(); // We know it's currently 2
527 
528             short colFlag = 0;
529 
530             if (this.hasLongColumnInfo) {
531                 colFlag = (short) (packet.readInt());
532             } else {
533                 colFlag = (short) (packet.readByte() & 0xff);
534             }
535 
536             int colDecimals = (packet.readByte() & 0xff);
537 
538             if (this.colDecimalNeedsBump) {
539                 colDecimals++;
540             }
541 
542             Field field = new Field(this.connection, packet.getBufferSource(),
543                     nameStart, nameLength, tableNameStart, tableNameLength,
544                     colLength, colType, colFlag, colDecimals);
545 
546             return field;
547         }
548     }
549 
550     /**
551      * Determines if the database charset is the same as the platform charset
552      */
553     protected void checkForCharsetMismatch() {
554         if (this.connection.useUnicode()
555                 && (this.connection.getEncoding() != null)) {
556             String encodingToCheck = jvmPlatformCharset;
557 
558             if (encodingToCheck == null) {
559                 encodingToCheck = System.getProperty("file.encoding");
560             }
561 
562             if (encodingToCheck == null) {
563                 this.platformDbCharsetMatches = false;
564             } else {
565                 this.platformDbCharsetMatches = encodingToCheck.equals(this.connection
566                         .getEncoding());
567             }
568         }
569     }
570 
571     static int getMaxBuf() {
572         return maxBufferSize;
573     }
574 
575     /**
576      * Get the major version of the MySQL server we are talking to.
577      *
578      * @return DOCUMENT ME!
579      */
580     final int getServerMajorVersion() {
581         return this.serverMajorVersion;
582     }
583 
584     /**
585      * Get the minor version of the MySQL server we are talking to.
586      *
587      * @return DOCUMENT ME!
588      */
589     final int getServerMinorVersion() {
590         return this.serverMinorVersion;
591     }
592 
593     /**
594      * Get the sub-minor version of the MySQL server we are talking to.
595      *
596      * @return DOCUMENT ME!
597      */
598     final int getServerSubMinorVersion() {
599         return this.serverSubMinorVersion;
600     }
601 
602     /**
603      * Get the version string of the server we are talking to
604      *
605      * @return DOCUMENT ME!
606      */
607     String getServerVersion() {
608         return this.serverVersion;
609     }
610 
611     /**
612      * Initialize communications with the MySQL server. Handles logging on, and
613      * handling initial connection errors.
614      *
615      * @param user DOCUMENT ME!
616      * @param password DOCUMENT ME!
617      * @param database DOCUMENT ME!
618      *
619      * @throws java.sql.SQLException DOCUMENT ME!
620      * @throws SQLException DOCUMENT ME!
621      */
622     void doHandshake(String user, String password, String database)
623         throws java.sql.SQLException {
624         // Read the first packet
625         Buffer buf = readPacket();
626 
627         // Get the protocol version
628         this.protocolVersion = buf.readByte();
629 
630         if (this.protocolVersion == -1) {
631             try {
632                 this.mysqlConnection.close();
633             } catch (Exception e) {
634                 ; // ignore
635             }
636 
637             int errno = 2000;
638 
639             errno = buf.readInt();
640 
641             String serverErrorMessage = buf.readString();
642 
643             StringBuffer errorBuf = new StringBuffer(" message from server: \"");
644             errorBuf.append(serverErrorMessage);
645             errorBuf.append("\"");
646 
647             String xOpen = SQLError.mysqlToXOpen(errno);
648 
649             throw new SQLException(SQLError.get(xOpen) + ", "
650                 + errorBuf.toString(), xOpen, errno);
651         }
652 
653         this.serverVersion = buf.readString();
654 
655         // Parse the server version into major/minor/subminor
656         int point = this.serverVersion.indexOf(".");
657 
658         if (point != -1) {
659             try {
660                 int n = Integer.parseInt(this.serverVersion.substring(0, point));
661                 this.serverMajorVersion = n;
662             } catch (NumberFormatException NFE1) {
663                 ;
664             }
665 
666             String remaining = this.serverVersion.substring(point + 1,
667                     this.serverVersion.length());
668             point = remaining.indexOf(".");
669 
670             if (point != -1) {
671                 try {
672                     int n = Integer.parseInt(remaining.substring(0, point));
673                     this.serverMinorVersion = n;
674                 } catch (NumberFormatException nfe) {
675                     ;
676                 }
677 
678                 remaining = remaining.substring(point + 1, remaining.length());
679 
680                 int pos = 0;
681 
682                 while (pos < remaining.length()) {
683                     if ((remaining.charAt(pos) < '0')
684                             || (remaining.charAt(pos) > '9')) {
685                         break;
686                     }
687 
688                     pos++;
689                 }
690 
691                 try {
692                     int n = Integer.parseInt(remaining.substring(0, pos));
693                     this.serverSubMinorVersion = n;
694                 } catch (NumberFormatException nfe) {
695                     ;
696                 }
697             }
698         }
699 
700         if (versionMeetsMinimum(4, 0, 8)) {
701             this.maxThreeBytes = (256 * 256 * 256) - 1;
702             this.useNewLargePackets = true;
703         } else {
704             this.maxThreeBytes = 255 * 255 * 255;
705             this.useNewLargePackets = false;
706         }
707 
708         this.colDecimalNeedsBump = versionMeetsMinimum(3, 23, 0);
709         this.colDecimalNeedsBump = !versionMeetsMinimum(3, 23, 15); // guess? Not noted in changelog
710         this.useNewUpdateCounts = versionMeetsMinimum(3, 22, 5);
711 
712         long threadId = buf.readLong();
713         seed = buf.readString();
714 
715         if (Driver.TRACE) {
716             Debug.msg(this, "Protocol Version: " + (int) this.protocolVersion);
717             Debug.msg(this, "Server Version: " + this.serverVersion);
718             Debug.msg(this, "Thread ID: " + threadId);
719             Debug.msg(this, "Crypt Seed: " + seed);
720         }
721 
722         this.serverCapabilities = 0;
723 
724         if (buf.getPosition() < buf.getBufLength()) {
725             serverCapabilities = buf.readInt();
726         }
727 
728         if (versionMeetsMinimum(4, 1, 1)) {
729             int position = buf.getPosition();
730             
731             this.serverCharsetIndex = buf.readByte() & 0xff;
732       //not used this.serverStatus = buf.readInt();
733       
734             buf.setPosition(position + 16);
735 
736             String seedPart2 = buf.readString();
737             StringBuffer newSeed = new StringBuffer(20);
738             newSeed.append(seed);
739             newSeed.append(seedPart2);
740             this.seed = newSeed.toString();
741         }
742 
743         if (((serverCapabilities & CLIENT_COMPRESS) != 0)
744                 && this.connection.useCompression()) {
745             clientParam |= CLIENT_COMPRESS;
746         }
747 
748         if ((database != null) && (database.length() > 0)) {
749             clientParam |= CLIENT_CONNECT_WITH_DB;
750         }
751 
752         if (((serverCapabilities & CLIENT_SSL) == 0)
753                 && this.connection.useSSL()) {
754             this.connection.setUseSSL(false);
755         }
756 
757         if ((serverCapabilities & CLIENT_LONG_FLAG) != 0) {
758             // We understand other column flags, as well
759             clientParam |= CLIENT_LONG_FLAG;
760             this.hasLongColumnInfo = true;
761         }
762 
763         // return FOUND rows
764         clientParam |= CLIENT_FOUND_ROWS;
765 
766         if (this.connection.allowLoadLocalInfile()) {
767             clientParam |= CLIENT_LOCAL_FILES;
768         }
769 
770         if (isInteractiveClient) {
771             clientParam |= CLIENT_INTERACTIVE;
772         }
773 
774         // Authenticate
775         if (this.protocolVersion > 9) {
776             clientParam |= CLIENT_LONG_PASSWORD; // for long passwords
777         } else {
778             clientParam &= ~CLIENT_LONG_PASSWORD;
779         }
780 
781         //
782         // 4.1 has some differences in the protocol
783         //
784         if (versionMeetsMinimum(4, 1, 0)) {
785             if (versionMeetsMinimum(4, 1, 1)) {
786                 clientParam |= CLIENT_PROTOCOL_41;
787                 this.has41NewNewProt = true;
788             } else {
789                 clientParam |= CLIENT_RESERVED;
790                 this.has41NewNewProt = false;
791             }
792 
793             this.use41Extensions = true;
794         }
795 
796         int passwordLength = 16;
797         int userLength = 0;
798         int databaseLength = 0;
799 
800         if (user != null) {
801             userLength = user.length();
802         }
803 
804         if (database != null) {
805             databaseLength = database.length();
806         }
807 
808         int packLength = (userLength + passwordLength + databaseLength) + 7
809             + HEADER_LENGTH;
810         Buffer packet = null;
811 
812         if (!connection.useSSL()) {
813             if ((serverCapabilities & CLIENT_SECURE_CONNECTION) != 0) {
814                 clientParam |= CLIENT_SECURE_CONNECTION;
815 
816                 if (versionMeetsMinimum(4, 1, 1)) {
817                     secureAuth411(null, packLength, user, password, database,
818                         true);
819                 } else {
820                     secureAuth(null, packLength, user, password, database, true);
821                 }
822             } else {
823                 packet = new Buffer(packLength);
824 
825                 if ((clientParam & CLIENT_RESERVED) != 0) {
826                     if (versionMeetsMinimum(4, 1, 1)) {
827                         packet.writeLong(clientParam);
828                         packet.writeLong(this.maxThreeBytes);
829 
830                         // charset, JDBC will connect as 'latin1',
831                         // and use 'SET NAMES' to change to the desired
832                         // charset after the connection is established.
833                         packet.writeByte((byte) 8);
834 
835                         // Set of bytes reserved for future use.
836                         packet.writeBytesNoNull(new byte[23]);
837                     } else {
838                         packet.writeLong(clientParam);
839                         packet.writeLong(this.maxThreeBytes);
840                     }
841                 } else {
842                     packet.writeInt((int) clientParam);
843                     packet.writeLongInt(this.maxThreeBytes);
844                 }
845 
846                 // User/Password data
847                 packet.writeString(user);
848 
849                 if (this.protocolVersion > 9) {
850                     packet.writeString(Util.newCrypt(password, this.seed));
851                 } else {
852                     packet.writeString(Util.oldCrypt(password, this.seed));
853                 }
854 
855                 if (((serverCapabilities & CLIENT_CONNECT_WITH_DB) != 0)
856                         && (database != null) && (database.length() > 0)) {
857                     packet.writeString(database);
858                 }
859 
860                 send(packet);
861             }
862         } else {
863             boolean doSecureAuth = false;
864 
865             if ((serverCapabilities & CLIENT_SECURE_CONNECTION) != 0) {
866                 clientParam |= CLIENT_SECURE_CONNECTION;
867                 doSecureAuth = true;
868             }
869 
870             clientParam |= CLIENT_SSL;
871             packet = new Buffer(packLength);
872 
873             if ((clientParam & CLIENT_RESERVED) != 0) {
874                 packet.writeLong(clientParam);
875             } else {
876                 packet.writeInt((int) clientParam);
877             }
878 
879             send(packet);
880 
881             javax.net.ssl.SSLSocketFactory sslFact = (javax.net.ssl.SSLSocketFactory) javax.net.ssl.SSLSocketFactory
882                 .getDefault();
883 
884             try {
885                 this.mysqlConnection = sslFact.createSocket(this.mysqlConnection,
886                         this.host, this.port, true);
887 
888                 // need to force TLSv1, or else JSSE tries to do a SSLv2 handshake
889                 // which MySQL doesn't understand
890                 ((javax.net.ssl.SSLSocket) this.mysqlConnection)
891                 .setEnabledProtocols(new String[] { "TLSv1" });
892                 ((javax.net.ssl.SSLSocket) this.mysqlConnection).startHandshake();
893                 this.mysqlInput = new BufferedInputStream(this.mysqlConnection
894                         .getInputStream(), 16384);
895                 this.mysqlOutput = new BufferedOutputStream(this.mysqlConnection
896                         .getOutputStream(), 16384);
897                 this.mysqlOutput.flush();
898             } catch (IOException ioEx) {
899                 StringBuffer message = new StringBuffer(SQLError.get(
900                             SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE));
901                 message.append(": ");
902                 message.append(ioEx.getClass().getName());
903                 message.append(", underlying cause: ");
904                 message.append(ioEx.getMessage());
905 
906                 if (!this.connection.useParanoidErrorMessages()) {
907                     message.append(Util.stackTraceToString(ioEx));
908                 }
909 
910                 throw new java.sql.SQLException(message.toString(),
911                     SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE, 0);
912             }
913 
914             packet.clear();
915 
916             if (doSecureAuth) {
917                 if (versionMeetsMinimum(4, 1, 1)) {
918                     secureAuth411(null, packLength, user, password, database,
919                         true);
920                 } else {
921                     secureAuth(null, packLength, user, password, database, true);
922                 }
923             } else {
924                 if ((clientParam & CLIENT_RESERVED) != 0) {
925                     packet.writeLong(clientParam);
926                     packet.writeLong(this.maxThreeBytes);
927                 } else {
928                     packet.writeInt((int) clientParam);
929                     packet.writeLongInt(this.maxThreeBytes);
930                 }
931 
932                 // User/Password data
933                 packet.writeString(user);
934 
935                 if (this.protocolVersion > 9) {
936                     packet.writeString(Util.newCrypt(password, seed));
937                 } else {
938                     packet.writeString(Util.oldCrypt(password, seed));
939                 }
940 
941                 if (((serverCapabilities & CLIENT_CONNECT_WITH_DB) != 0)
942                         && (database != null) && (database.length() > 0)) {
943                     packet.writeString(database);
944                 }
945 
946                 send(packet);
947             }
948         }
949 
950         // Check for errors, not for 4.1.1 or newer,
951         // as the new auth protocol doesn't work that way
952         // (see secureAuth411() for more details...)
953         if (!versionMeetsMinimum(4, 1, 1)) {
954             checkErrorPacket();
955         }
956 
957         //
958         // Can't enable compression until after handshake
959         //
960         if (((serverCapabilities & CLIENT_COMPRESS) != 0)
961                 && this.connection.useCompression()) {
962             // The following matches with ZLIB's
963             // compress()
964             this.deflater = new Deflater();
965             this.useCompression = true;
966             this.mysqlInput = new CompressedInputStream(this.mysqlInput);
967         }
968 
969         if (((serverCapabilities & CLIENT_CONNECT_WITH_DB) == 0)
970                 && (database != null) && (database.length() > 0)) {
971             try {
972                 sendCommand(MysqlDefs.INIT_DB, database, null);
973             } catch (Exception ex) {
974                 throw new SQLException(ex.toString()
975                     + (this.connection.useParanoidErrorMessages() ? ""
976                                                                   : Util
977                     .stackTraceToString(ex)),
978                     SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE);
979             }
980         }
981     }
982 
983     /**
984      * Retrieve one row from the MySQL server. Note: this method is not
985      * thread-safe, but it is only called from methods that are guarded by
986      * synchronizing on this object.
987      *
988      * @param columnCount DOCUMENT ME!
989      *
990      * @return DOCUMENT ME!
991      *
992      * @throws Exception DOCUMENT ME!
993      */
994     final byte[][] nextRow(int columnCount) throws Exception {
995         // Get the next incoming packet, re-using the packet because
996         // all the data we need gets copied out of it.
997         Buffer rowPacket = checkErrorPacket();
998 
999         //
1000        // Didn't read an error, so re-position to beginning
1001        // of packet in order to read result set data
1002        //
1003        int offset = 0;
1004
1005        //if (rowPacket.wasMultiPacket()) {
1006        //    if (this.useNewLargePackets) {
1007        //        offset = HEADER_LENGTH;
1008        //    } else {
1009        //        offset = HEADER_LENGTH + 1;
1010        //   }
1011        //}
1012        rowPacket.setPosition(rowPacket.getPosition() - 1);
1013
1014        byte[][] rowData = new byte[columnCount][];
1015
1016        if (!rowPacket.isLastDataPacket()) {
1017            for (int i = 0; i < columnCount; i++) {
1018                rowData[i] = rowPacket.readLenByteArray(offset);
1019
1020                if (Driver.TRACE) {
1021                    if (rowData[i] == null) {
1022                        Debug.msg(this, "Field value: NULL");
1023                    } else {
1024                        Debug.msg(this, "Field value: " + rowData[i].toString());
1025                    }
1026                }
1027            }
1028
1029            return rowData;
1030        }
1031
1032        return null;
1033    }
1034
1035    /**
1036     * Log-off of the MySQL server and close the socket.
1037     *
1038     * @throws SQLException DOCUMENT ME!
1039     */
1040    final void quit() throws SQLException {
1041        Buffer packet = new Buffer(6);
1042        this.packetSequence = -1;
1043        packet.writeByte((byte) MysqlDefs.QUIT);
1044        send(packet);
1045        forceClose();
1046    }
1047
1048    /**
1049     * Returns the packet used for sending data (used by PreparedStatement)
1050     * Guarded by external synchronization on a mutex.
1051     *
1052     * @return A packet to send data with
1053     */
1054    Buffer getSharedSendPacket() {
1055        if (this.sharedSendPacket == null) {
1056            this.sharedSendPacket = new Buffer(this.connection
1057                    .getNetBufferLength());
1058        }
1059
1060        return this.sharedSendPacket;
1061    }
1062
1063    void closeStreamer(RowData streamer) throws SQLException {
1064        if (this.streamingData == null) {
1065            throw new SQLException("Attempt to close streaming result set "
1066                + streamer
1067                + " when no streaming  result set was registered. This is an internal error.");
1068        }
1069
1070        if (streamer != this.streamingData) {
1071            throw new SQLException("Attempt to close streaming result set "
1072                + streamer + " that was not registered."
1073                + " Only one streaming result set may be open and in use per-connection. Ensure that you have called .close() on "
1074                + " any active result sets before attempting more queries.");
1075        }
1076
1077        this.streamingData = null;
1078    }
1079
1080    /**
1081     * Sets the buffer size to max-buf
1082     */
1083    void resetMaxBuf() {
1084        this.maxAllowedPacket = this.connection.getMaxAllowedPacket();
1085    }
1086
1087    /**
1088     * Send a command to the MySQL server If data is to be sent with command,
1089     * it should be put in ExtraData Raw packets can be sent by setting
1090     * QueryPacket to something other than null.
1091     *
1092     * @param command DOCUMENT ME!
1093     * @param extraData DOCUMENT ME!
1094     * @param queryPacket DOCUMENT ME!
1095     *
1096     * @return DOCUMENT ME!
1097     *
1098     * @throws Exception DOCUMENT ME!
1099     * @throws java.sql.SQLException DOCUMENT ME!
1100     */
1101    final Buffer sendCommand(int command, String extraData, Buffer queryPacket)
1102        throws Exception {
1103        checkForOutstandingStreamingData();
1104
1105        try {
1106            if (this.clearStreamBeforeEachQuery) {
1107                clearInputStream();
1108            }
1109
1110            //
1111            // PreparedStatements construct their own packets,
1112            // for efficiency's sake.
1113            //
1114            // If this is a generic query, we need to re-use
1115            // the sending packet.
1116            //
1117            if (queryPacket == null) {
1118                int packLength = HEADER_LENGTH + COMP_HEADER_LENGTH + 1
1119                    + ((extraData != null) ? extraData.length() : 0) + 2;
1120
1121                if (this.sendPacket == null) {
1122                    this.sendPacket = new Buffer(packLength);
1123                }
1124
1125                this.packetSequence = -1;
1126                this.sendPacket.clear();
1127
1128                this.sendPacket.writeByte((byte) command);
1129
1130                if ((command == MysqlDefs.INIT_DB)
1131                        || (command == MysqlDefs.CREATE_DB)
1132                        || (command == MysqlDefs.DROP_DB)
1133                        || (command == MysqlDefs.QUERY)) {
1134                    this.sendPacket.writeStringNoNull(extraData);
1135                } else if (command == MysqlDefs.PROCESS_KILL) {
1136                    long id = new Long(extraData).longValue();
1137                    this.sendPacket.writeLong(id);
1138                } else if ((command == MysqlDefs.RELOAD)
1139                        && (this.protocolVersion > 9)) {
1140                    Debug.msg(this, "Reload");
1141
1142                    //Packet.writeByte(reloadParam);
1143                }
1144
1145                send(this.sendPacket);
1146            } else {
1147                this.packetSequence = -1;
1148                send(queryPacket); // packet passed by PreparedStatement
1149            }
1150        } catch (SQLException sqlEx) {
1151            // don't wrap SQLExceptions
1152            throw sqlEx;
1153        } catch (Exception ex) {
1154            String underlyingMessage = ex.getMessage();
1155
1156            throw new java.sql.SQLException(SQLError.get(
1157                    SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE) + ": "
1158                + ex.getClass().getName() + ", "
1159                + ((underlyingMessage != null) ? underlyingMessage
1160                                               : "no message given by JVM")
1161                + (this.connection.useParanoidErrorMessages() ? ""
1162                                                              : Util
1163                .stackTraceToString(ex)),
1164                SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE, 0);
1165        }
1166
1167        return checkErrorPacket(command);
1168    }
1169
1170    /**
1171     * Send a query specified in the String "Query" to the MySQL server. This
1172     * method uses the specified character encoding to get the bytes from the
1173     * query string.
1174     *
1175     * @param query DOCUMENT ME!
1176     * @param maxRows DOCUMENT ME!
1177     * @param characterEncoding DOCUMENT ME!
1178     * @param conn DOCUMENT ME!
1179     * @param resultSetType DOCUMENT ME!
1180     * @param streamResults DOCUMENT ME!
1181     * @param catalog DOCUMENT ME!
1182     *
1183     * @return DOCUMENT ME!
1184     *
1185     * @throws Exception DOCUMENT ME!
1186     */
1187    final ResultSet sqlQuery(String query, int maxRows,
1188        String characterEncoding, Connection conn, int resultSetType,
1189        boolean streamResults, String catalog) throws Exception {
1190        // We don't know exactly how many bytes we're going to get
1191        // from the query. Since we're dealing with Unicode, the
1192        // max is 2, so pad it (2 * query) + space for headers
1193        int packLength = HEADER_LENGTH + 1 + (query.length() * 2) + 2;
1194
1195        if (this.sendPacket == null) {
1196            this.sendPacket = new Buffer(packLength);
1197        } else {
1198            this.sendPacket.clear();
1199        }
1200
1201        this.sendPacket.writeByte((byte) MysqlDefs.QUERY);
1202
1203        if (characterEncoding != null) {
1204            SingleByteCharsetConverter converter = this.connection
1205                .getCharsetConverter(characterEncoding);
1206
1207            if (this.platformDbCharsetMatches) {
1208                this.sendPacket.writeStringNoNull(query, characterEncoding,
1209                    this.connection.getServerCharacterEncoding(), converter, 
1210          this.connection.parserKnowsUnicode());
1211            } else {
1212                if (StringUtils.startsWithIgnoreCaseAndWs(query, "LOAD DATA")) {
1213                    this.sendPacket.writeBytesNoNull(query.getBytes());
1214                } else {
1215                    this.sendPacket.writeStringNoNull(query, characterEncoding,
1216                        this.connection.getServerCharacterEncoding(), converter, 
1217              this.connection.parserKnowsUnicode());
1218                }
1219            }
1220        } else {
1221            this.sendPacket.writeStringNoNull(query);
1222        }
1223
1224        return sqlQueryDirect(this.sendPacket, maxRows, conn, resultSetType,
1225            streamResults, catalog);
1226    }
1227
1228    /**
1229     * Send a query stored in a packet directly to the server.
1230     *
1231     * @param queryPacket DOCUMENT ME!
1232     * @param maxRows DOCUMENT ME!
1233     * @param conn DOCUMENT ME!
1234     * @param resultSetType DOCUMENT ME!
1235     * @param streamResults DOCUMENT ME!
1236     * @param catalog DOCUMENT ME!
1237     *
1238     * @return DOCUMENT ME!
1239     *
1240     * @throws Exception DOCUMENT ME!
1241     */
1242    final ResultSet sqlQueryDirect(Buffer queryPacket, int maxRows,
1243        Connection conn, int resultSetType, boolean streamResults,
1244        String catalog) throws Exception {
1245        StringBuffer profileMsgBuf = null; // used if profiling
1246        long queryStartTime = 0;
1247
1248        if (this.profileSql) {
1249            profileMsgBuf = new StringBuffer();
1250            queryStartTime = System.currentTimeMillis();
1251
1252            byte[] queryBuf = queryPacket.getByteBuffer();
1253
1254            int queryLength = queryPacket.getPosition();
1255            
1256            boolean queryTruncated = false;
1257            
1258            if (queryLength > MAX_QUERY_LENGTH_TO_LOG) {
1259              queryLength = MAX_QUERY_LENGTH_TO_LOG;
1260              
1261              queryTruncated = true;
1262            }
1263            
1264            // Extract the actual query from the network packet
1265            String query = new String(queryBuf, 5,
1266                    (queryLength - 5));
1267            profileMsgBuf.append("Query\t\"");
1268            profileMsgBuf.append(query);
1269            
1270            if (queryTruncated) {
1271              profileMsgBuf.append(" ... (long query truncated)");
1272            }
1273            
1274            profileMsgBuf.append("\"\texecution time:\t");
1275        }
1276
1277        // Send query command and sql query string
1278        Buffer resultPacket = sendCommand(MysqlDefs.QUERY, null, queryPacket);
1279
1280        if (this.profileSql) {
1281            long executionTime = System.currentTimeMillis() - queryStartTime;
1282            profileMsgBuf.append(executionTime);
1283            profileMsgBuf.append("\t");
1284        }
1285
1286        resultPacket.setPosition(resultPacket.getPosition() - 1);
1287
1288        long columnCount = resultPacket.readFieldLength();
1289
1290        if (Driver.TRACE) {
1291            Debug.msg(this, "Column count: " + columnCount);
1292        }
1293
1294        if (columnCount == 0) {
1295            if (this.profileSql) {
1296                System.err.println(profileMsgBuf.toString());
1297            }
1298
1299            return buildResultSetWithUpdates(resultPacket);
1300        } else if (columnCount == Buffer.NULL_LENGTH) {
1301            String charEncoding = null;
1302
1303            if (this.connection.useUnicode()) {
1304                charEncoding = this.connection.getEncoding();
1305            }
1306
1307            String fileName = null;
1308
1309            if (this.platformDbCharsetMatches) {
1310                fileName = ((charEncoding != null)
1311                    ? resultPacket.readString(charEncoding)
1312                    : resultPacket.readString());
1313            } else {
1314                fileName = resultPacket.readString();
1315            }
1316
1317            return sendFileToServer(fileName);
1318        } else {
1319            long fetchStartTime = 0;
1320
1321            if (this.profileSql) {
1322                fetchStartTime = System.currentTimeMillis();
1323            }
1324
1325            com.mysql.jdbc.ResultSet results = getResultSet(columnCount,
1326                    maxRows, resultSetType, streamResults, catalog);
1327
1328            if (this.profileSql) {
1329                long fetchElapsedTime = System.currentTimeMillis()
1330                    - fetchStartTime;
1331                profileMsgBuf.append("result set fetch time:\t");
1332                profileMsgBuf.append(fetchElapsedTime);
1333                System.err.println(profileMsgBuf.toString());
1334            }
1335
1336            return results;
1337        }
1338    }
1339
1340    /**
1341     * Returns the host this IO is connected to
1342     *
1343     * @return DOCUMENT ME!
1344     */
1345    String getHost() {
1346        return this.host;
1347    }
1348
1349    /**
1350     * Does the version of the MySQL server we are connected to meet the given
1351     * minimums?
1352     *
1353     * @param major DOCUMENT ME!
1354     * @param minor DOCUMENT ME!
1355     * @param subminor DOCUMENT ME!
1356     *
1357     * @return DOCUMENT ME!
1358     */
1359    boolean versionMeetsMinimum(int major, int minor, int subminor) {
1360        if (getServerMajorVersion() >= major) {
1361            if (getServerMajorVersion() == major) {
1362                if (getServerMinorVersion() >= minor) {
1363                    if (getServerMinorVersion() == minor) {
1364                        return (getServerSubMinorVersion() >= subminor);
1365                    } else {
1366                        // newer than major.minor
1367                        return true;
1368                    }
1369                } else {
1370                    // older than major.minor
1371                    return false;
1372                }
1373            } else {
1374                // newer than major
1375                return true;
1376            }
1377        } else {
1378            return false;
1379        }
1380    }
1381
1382    private final int readFully(InputStream in, byte[] b, int off, int len)
1383        throws IOException {
1384        if (len < 0) {
1385            throw new IndexOutOfBoundsException();
1386        }
1387
1388        int n = 0;
1389
1390        while (n < len) {
1391            int count = in.read(b, off + n, len - n);
1392
1393            if (count < 0) {
1394                throw new EOFException();
1395            }
1396
1397            n += count;
1398        }
1399
1400        return n;
1401    }
1402
1403    /**
1404     * Read one packet from the MySQL server
1405     *
1406     * @return DOCUMENT ME!
1407     *
1408     * @throws SQLException DOCUMENT ME!
1409     * @throws java.sql.SQLException DOCUMENT ME!
1410     */
1411    private final Buffer readPacket() throws SQLException {
1412        try {
1413            int lengthRead = readFully(mysqlInput, this.packetHeaderBuf, 0, 4);
1414
1415            if (lengthRead < 4) {
1416                forceClose();
1417                throw new IOException("Unexpected end of input stream");
1418            }
1419
1420            int packetLength = ((int) (this.packetHeaderBuf[0] & 0xff))
1421                + (((int) (this.packetHeaderBuf[1] & 0xff)) << 8)
1422                + (((int) (this.packetHeaderBuf[2] & 0xff)) << 16);
1423
1424            byte multiPacketSeq = this.packetHeaderBuf[3];
1425
1426            // Read data
1427            byte[] buffer = new byte[packetLength + 1];
1428            readFully(this.mysqlInput, buffer, 0, packetLength);
1429            buffer[packetLength] = 0;
1430
1431            Buffer packet = new Buffer(buffer);
1432
1433            return packet;
1434        } catch (IOException ioEx) {
1435            StringBuffer message = new StringBuffer(SQLError.get(
1436                        SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE));
1437            message.append(": ");
1438            message.append(ioEx.getClass().getName());
1439            message.append(", underlying cause: ");
1440            message.append(ioEx.getMessage());
1441
1442            if (!this.connection.useParanoidErrorMessages()) {
1443                message.append(Util.stackTraceToString(ioEx));
1444            }
1445
1446            throw new java.sql.SQLException(message.toString(),
1447                SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE, 0);
1448        }
1449    }
1450
1451    private com.mysql.jdbc.ResultSet buildResultSetWithRows(String catalog,
1452        com.mysql.jdbc.Field[] fields, RowData rows, int resultSetConcurrency)
1453        throws SQLException {
1454        switch (resultSetConcurrency) {
1455        case java.sql.ResultSet.CONCUR_READ_ONLY:
1456            return new com.mysql.jdbc.ResultSet(catalog, fields, rows,
1457                this.connection);
1458
1459        case java.sql.ResultSet.CONCUR_UPDATABLE:
1460            return new com.mysql.jdbc.UpdatableResultSet(catalog, fields, rows,
1461                this.connection);
1462
1463        default:
1464            return new com.mysql.jdbc.ResultSet(catalog, fields, rows,
1465                this.connection);
1466        }
1467    }
1468
1469    private com.mysql.jdbc.ResultSet buildResultSetWithUpdates(
1470        Buffer resultPacket) throws SQLException {
1471        long updateCount = -1;
1472        long updateID = -1;
1473        String info = null;
1474
1475        try {
1476            if (this.useNewUpdateCounts) {
1477                updateCount = resultPacket.newReadLength();
1478                updateID = resultPacket.newReadLength();
1479            } else {
1480                updateCount = (long) resultPacket.readLength();
1481                updateID = (long) resultPacket.readLength();
1482            }
1483
1484            if (this.connection.isReadInfoMsgEnabled()) {
1485                if (this.use41Extensions) {
1486                    int serverStatus = resultPacket.readInt();
1487                    int warningCount = resultPacket.readInt();
1488
1489                    resultPacket.readByte(); // advance pointer
1490                }
1491
1492                info = resultPacket.readString();
1493            }
1494        } catch (Exception ex) {
1495            throw new java.sql.SQLException(SQLError.get(
1496                    SQLError.SQL_STATE_GENERAL_ERROR) + ": "
1497                + ex.getClass().getName(), SQLError.SQL_STATE_GENERAL_ERROR, -1);
1498        }
1499
1500        if (Driver.TRACE) {
1501            Debug.msg(this, "Update Count = " + updateCount);
1502        }
1503
1504        ResultSet updateRs = new ResultSet(updateCount, updateID);
1505
1506        if (info != null) {
1507            updateRs.setServerInfo(info);
1508        }
1509
1510        return updateRs;
1511    }
1512
1513    /**
1514     * Don't hold on to overly-large packets
1515     */
1516    private void reclaimLargeReusablePacket() {
1517        if ((this.reusablePacket != null)
1518                && (this.reusablePacket.getBufLength() > 1048576)) {
1519            this.reusablePacket = new Buffer(this.connection.getNetBufferLength());
1520        }
1521    }
1522
1523    /**
1524     * Re-use a packet to read from the MySQL server
1525     *
1526     * @param reuse DOCUMENT ME!
1527     *
1528     * @return DOCUMENT ME!
1529     *
1530     * @throws SQLException DOCUMENT ME!
1531     * @throws SQLException DOCUMENT ME!
1532     */
1533    private final Buffer reuseAndReadPacket(Buffer reuse)
1534        throws SQLException {
1535        try {
1536            reuse.setWasMultiPacket(false);
1537
1538            int lengthRead = readFully(mysqlInput, this.packetHeaderBuf, 0, 4);
1539
1540            if (lengthRead < 4) {
1541                forceClose();
1542                throw new IOException("Unexpected end of input stream");
1543            }
1544
1545            int packetLength = ((int) (this.packetHeaderBuf[0] & 0xff))
1546                + (((int) (this.packetHeaderBuf[1] & 0xff)) << 8)
1547                + (((int) (this.packetHeaderBuf[2] & 0xff)) << 16);
1548
1549            byte multiPacketSeq = this.packetHeaderBuf[3];
1550
1551            //byte multiPacketSeq = (byte) this.mysqlInput.read();
1552            // Set the Buffer to it's original state
1553            reuse.setPosition(0);
1554            reuse.setSendLength(0);
1555
1556            // Do we need to re-alloc the byte buffer?
1557            //
1558            // Note: We actually check the length of the buffer,
1559            // rather than getBufLength(), because getBufLength() is not
1560            // necesarily the actual length of the byte array
1561            // used as the buffer
1562            if (reuse.getByteBuffer().length <= packetLength) {
1563                reuse.setByteBuffer(new byte[packetLength + 1]);
1564            }
1565
1566            // Set the new length
1567            reuse.setBufLength(packetLength);
1568
1569            // Read the data from the server
1570            readFully(this.mysqlInput, reuse.getByteBuffer(), 0, packetLength);
1571
1572            boolean isMultiPacket = false;
1573
1574            if (packetLength == maxThreeBytes) {
1575                reuse.setPosition((int) maxThreeBytes);
1576
1577                int packetEndPoint = packetLength;
1578
1579                // it's multi-packet
1580                isMultiPacket = true;
1581
1582                lengthRead = readFully(mysqlInput, this.packetHeaderBuf, 0, 4);
1583
1584                if (lengthRead < 4) {
1585                    forceClose();
1586                    throw new IOException("Unexpected end of input stream");
1587                }
1588
1589                packetLength = ((int) (this.packetHeaderBuf[0] & 0xff))
1590                    + (((int) (this.packetHeaderBuf[1] & 0xff)) << 8)
1591                    + (((int) (this.packetHeaderBuf[2] & 0xff)) << 16);
1592
1593                Buffer multiPacket = new Buffer(packetLength);
1594                boolean firstMultiPkt = true;
1595
1596                while (true) {
1597                    if (!firstMultiPkt) {
1598                        lengthRead = readFully(mysqlInput,
1599                                this.packetHeaderBuf, 0, 4);
1600
1601                        if (lengthRead < 4) {
1602                            forceClose();
1603                            throw new IOException(
1604                                "Unexpected end of input stream");
1605                        }
1606
1607                        packetLength = ((int) (this.packetHeaderBuf[0] & 0xff))
1608                            + (((int) (this.packetHeaderBuf[1] & 0xff)) << 8)
1609                            + (((int) (this.packetHeaderBuf[2] & 0xff)) << 16);
1610                    } else {
1611                        firstMultiPkt = false;
1612                    }
1613
1614                    if (!this.useNewLargePackets && (packetLength == 1)) {
1615                        clearInputStream();
1616
1617                        break;
1618                    } else if (packetLength < this.maxThreeBytes) {
1619                        byte newPacketSeq = this.packetHeaderBuf[3];
1620
1621                        if (newPacketSeq != (multiPacketSeq + 1)) {
1622                            throw new IOException(
1623                                "Packets received out of order");
1624                        }
1625
1626                        multiPacketSeq = newPacketSeq;
1627
1628                        // Set the Buffer to it's original state
1629                        multiPacket.setPosition(0);
1630                        multiPacket.setSendLength(0);
1631
1632                        // Set the new length
1633                        multiPacket.setBufLength(packetLength);
1634
1635                        // Read the data from the server
1636                        byte[] byteBuf = multiPacket.getByteBuffer();
1637                        int lengthToWrite = packetLength;
1638
1639                        int bytesRead = readFully(this.mysqlInput, byteBuf, 0,
1640                                packetLength);
1641
1642                        if (bytesRead != lengthToWrite) {
1643                            throw new SQLException(
1644                                "Short read from server, expected "
1645                                + lengthToWrite + " bytes, received only "
1646                                + bytesRead + ".",
1647                                SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE);
1648                        }
1649
1650                        reuse.writeBytesNoNull(byteBuf, 0, lengthToWrite);
1651
1652                        packetEndPoint += lengthToWrite;
1653
1654                        break; // end of multipacket sequence
1655                    }
1656
1657                    byte newPacketSeq = this.packetHeaderBuf[3];
1658
1659                    if (newPacketSeq != (multiPacketSeq + 1)) {
1660                        throw new IOException("Packets received out of order");
1661                    }
1662
1663                    multiPacketSeq = newPacketSeq;
1664
1665                    // Set the Buffer to it's original state
1666                    multiPacket.setPosition(0);
1667                    multiPacket.setSendLength(0);
1668
1669                    // Set the new length
1670                    multiPacket.setBufLength(packetLength);
1671
1672                    // Read the data from the server
1673                    byte[] byteBuf = multiPacket.getByteBuffer();
1674                    int lengthToWrite = packetLength;
1675
1676                    int bytesRead = readFully(this.mysqlInput, byteBuf, 0,
1677                            packetLength);
1678
1679                    if (bytesRead != lengthToWrite) {
1680                        throw new SQLException(
1681                            "Short read from server, expected " + lengthToWrite
1682                            + " bytes, received only " + bytesRead + ".",
1683                            SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE);
1684                    }
1685
1686                    reuse.writeBytesNoNull(byteBuf, 0, lengthToWrite);
1687
1688                    packetEndPoint += lengthToWrite;
1689                }
1690
1691                //reuse.writeByte((byte) 0);
1692                reuse.setPosition(0);
1693                reuse.setWasMultiPacket(true);
1694            }
1695
1696            if (!isMultiPacket) {
1697                reuse.getByteBuffer()[packetLength] = 0; // Null-termination
1698            }
1699
1700            return reuse;
1701        } catch (IOException ioEx) {
1702            StringBuffer message = new StringBuffer(SQLError.get(
1703                        SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE));
1704            message.append(": ");
1705            message.append(ioEx.getClass().getName());
1706            message.append(", underlying cause: ");
1707            message.append(ioEx.getMessage());
1708
1709            if (!this.connection.useParanoidErrorMessages()) {
1710                message.append(Util.stackTraceToString(ioEx));
1711            }
1712
1713            throw new java.sql.SQLException(message.toString(),
1714                SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE, 0);
1715        }
1716    }
1717
1718    /**
1719     * Send a packet to the MySQL server
1720     *
1721     * @param packet DOCUMENT ME!
1722     *
1723     * @throws SQLException DOCUMENT ME!
1724     */
1725    private final void send(Buffer packet) throws SQLException {
1726        int l = packet.getPosition();
1727        send(packet, l);
1728
1729        // 
1730        // Don't hold on to large packets
1731        //
1732        if (packet == this.sharedSendPacket) {
1733            reclaimLargeSharedSendPacket();
1734        }
1735    }
1736
1737    private final void send(Buffer packet, int packetLen)
1738        throws SQLException {
1739        try {
1740            if (packetLen > this.maxAllowedPacket) {
1741                throw new PacketTooBigException(packetLen, this.maxAllowedPacket);
1742            }
1743
1744            if ((serverMajorVersion >= 4) && (packetLen >= maxThreeBytes)) {
1745                sendSplitPackets(packet);
1746            } else {
1747                this.packetSequence++;
1748
1749                Buffer packetToSend = packet;
1750
1751                packetToSend.setPosition(0);
1752
1753                if (this.useCompression) {
1754                    packetToSend = compressPacket(packet, 0, packetLen,
1755                            HEADER_LENGTH);
1756                    packetLen = packetToSend.getPosition();
1757                } else {
1758                    packetToSend.writeLongInt(packetLen - HEADER_LENGTH);
1759                    packetToSend.writeByte(this.packetSequence);
1760                }
1761
1762                this.mysqlOutput.write(packetToSend.getByteBuffer(), 0,
1763                    packetLen);
1764                this.mysqlOutput.flush();
1765            }
1766
1767            // 
1768            // Don't hold on to large packets
1769            //
1770            if (packet == this.sharedSendPacket) {
1771                reclaimLargeSharedSendPacket();
1772            }
1773        } catch (IOException ioEx) {
1774            StringBuffer message = new StringBuffer(SQLError.get(
1775                        SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE));
1776            message.append(": ");
1777            message.append(ioEx.getClass().getName());
1778            message.append(", underlying cause: ");
1779            message.append(ioEx.getMessage());
1780
1781            if (!this.connection.useParanoidErrorMessages()) {
1782                message.append(Util.stackTraceToString(ioEx));
1783            }
1784
1785            throw new java.sql.SQLException(message.toString(),
1786                SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE, 0);
1787        }
1788    }
1789
1790    /**
1791     * Reads and sends a file to the server for LOAD DATA LOCAL INFILE
1792     *
1793     * @param fileName the file name to send.
1794     *
1795     * @return DOCUMENT ME!
1796     *
1797     * @throws SQLException DOCUMENT ME!
1798     */
1799    private final ResultSet sendFileToServer(String fileName)
1800        throws SQLException {
1801        Buffer filePacket = (loadFileBufRef == null) ? null
1802                                                     : (Buffer) (loadFileBufRef
1803            .get());
1804
1805        int packetLength = Math.min(this.connection.getMaxAllowedPacket()
1806                - (HEADER_LENGTH * 3),
1807                alignPacketSize(this.connection.getMaxAllowedPacket() - 16, 4096)
1808                - (HEADER_LENGTH * 3));
1809
1810        //
1811        // This packet may be _way_ too large to actually allocate,
1812        // unforunately, LOAD DATA LOCAL INFILE requires this setup...
1813        //
1814        try {
1815            if (filePacket == null) {
1816                filePacket = new Buffer((int) (packetLength + HEADER_LENGTH));
1817                loadFileBufRef = new SoftReference(filePacket);
1818            }
1819        } catch (OutOfMemoryError oom) {
1820            // Attempt to do this, but it might not work...
1821            // The server is expecting at least one packet, so we 
1822            // send an empty 'EOF' packet...
1823            this.reusablePacket.clear();
1824            send(this.reusablePacket);
1825
1826            throw new SQLException("Unable to allocate packet of size '"
1827                + (packetLength + HEADER_LENGTH)
1828                + "' for LOAD DATA LOCAL INFILE. Either increase heap space available to your JVM, or adjust the MySQL server variable 'max_allowed_packet'",
1829                SQLError.SQL_STATE_MEMORY_ALLOCATION_FAILURE);
1830        }
1831
1832        filePacket.clear();
1833        send(filePacket, 0);
1834
1835        byte[] fileBuf = new byte[packetLength];
1836
1837        BufferedInputStream fileIn = null;
1838
1839        try {
1840            fileIn = new BufferedInputStream(new FileInputStream(fileName));
1841
1842            int bytesRead = 0;
1843
1844            while ((bytesRead = fileIn.read(fileBuf)) != -1) {
1845                filePacket.clear();
1846                filePacket.writeBytesNoNull(fileBuf, 0, bytesRead);
1847                send(filePacket);
1848            }
1849        } catch (IOException ioEx) {
1850            StringBuffer messageBuf = new StringBuffer("Unable to open file ");
1851
1852            if (!this.connection.useParanoidErrorMessages()) {
1853                messageBuf.append("'");
1854
1855                if (fileName != null) {
1856                    messageBuf.append(fileName);
1857                }
1858
1859                messageBuf.append("'");
1860            }
1861
1862            messageBuf.append("for 'LOAD DATA LOCAL INFILE' command.");
1863
1864            if (!this.connection.useParanoidErrorMessages()) {
1865                messageBuf.append("Due to underlying IOException: ");
1866                messageBuf.append(Util.stackTraceToString(ioEx));
1867            }
1868
1869            throw new SQLException(messageBuf.toString(),
1870                SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
1871        } finally {
1872            if (fileIn != null) {
1873                try {
1874                    fileIn.close();
1875                } catch (Exception ex) {
1876                    throw new SQLException("Unable to close local file during LOAD DATA LOCAL INFILE command",
1877                        SQLError.SQL_STATE_GENERAL_ERROR);
1878                }
1879
1880                fileIn = null;
1881            } else {
1882                // file open failed, but server needs one packet
1883                filePacket.clear();
1884                send(filePacket);
1885            }
1886        }
1887
1888        // send empty packet to mark EOF
1889        filePacket.clear();
1890        send(filePacket);
1891
1892        Buffer resultPacket = checkErrorPacket();
1893
1894        return buildResultSetWithUpdates(resultPacket);
1895    }
1896
1897    /**
1898     * Checks for errors in the reply packet, and if none, returns the reply
1899     * packet, ready for reading
1900     *
1901     * @return DOCUMENT ME!
1902     *
1903     * @throws SQLException DOCUMENT ME!
1904     */
1905    private Buffer checkErrorPacket() throws SQLException {
1906        return checkErrorPacket(-1);
1907    }
1908
1909    /**
1910     * Checks for errors in the reply packet, and if none, returns the reply
1911     * packet, ready for reading
1912     *
1913     * @param command the command being issued (if used)
1914     *
1915     * @return DOCUMENT ME!
1916     *
1917     * @throws SQLException if an error packet was received
1918     * @throws java.sql.SQLException DOCUMENT ME!
1919     */
1920    private Buffer checkErrorPacket(int command) throws SQLException {
1921        int statusCode = 0;
1922        Buffer resultPacket = null;
1923
1924        try {
1925            // Check return value, if we get a java.io.EOFException,
1926            // the server has gone away. We'll pass it on up the
1927            // exception chain and let someone higher up decide
1928            // what to do (barf, reconnect, etc).
1929            resultPacket = reuseAndReadPacket(this.reusablePacket);
1930            statusCode = resultPacket.readByte();
1931        } catch (SQLException sqlEx) {
1932            // don't wrap SQLExceptions
1933            throw sqlEx;
1934        } catch (Exception fallThru) {
1935            String underlyingMessage = fallThru.getMessage();
1936
1937            throw new java.sql.SQLException(SQLError.get(
1938                    SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE) + ": "
1939                + fallThru.getClass().getName() + ", "
1940                + ((underlyingMessage != null) ? underlyingMessage
1941                                               : "no message given by JVM")
1942                + (this.connection.useParanoidErrorMessages() ? ""
1943                                                              : Util
1944                .stackTraceToString(fallThru)),
1945                SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE, 0);
1946        }
1947
1948        // Error handling
1949        if (statusCode == (byte) 0xff) {
1950            String serverErrorMessage;
1951            int errno = 2000;
1952
1953            if (this.protocolVersion > 9) {
1954                errno = resultPacket.readInt();
1955
1956                String xOpen = null;
1957
1958                serverErrorMessage = resultPacket.readString();
1959
1960                if (serverErrorMessage.startsWith("#")) {
1961                    // we have an SQLState
1962                    if (serverErrorMessage.length() > 6) {
1963                        xOpen = serverErrorMessage.substring(1, 6);
1964                        serverErrorMessage = serverErrorMessage.substring(6);
1965
1966                        if (xOpen.equals("HY000")) {
1967                            xOpen = SQLError.mysqlToXOpen(errno);
1968                        }
1969                    } else {
1970                        xOpen = SQLError.mysqlToXOpen(errno);
1971                    }
1972                } else {
1973                    xOpen = SQLError.mysqlToXOpen(errno);
1974                }
1975
1976                clearInputStream();
1977
1978                StringBuffer errorBuf = new StringBuffer();
1979
1980                String xOpenErrorMessage = SQLError.get(xOpen);
1981
1982                if (!this.connection.getUseOnlyServerErrorMessages()) {
1983                    if (xOpenErrorMessage != null) {
1984                        errorBuf.append(xOpenErrorMessage);
1985                        errorBuf.append(" message from server: \""); 
1986                    }
1987                }
1988
1989                errorBuf.append(serverErrorMessage);
1990
1991                if (!this.connection.getUseOnlyServerErrorMessages()) {
1992                    if (xOpenErrorMessage != null) {
1993                        errorBuf.append("\"");
1994                    }
1995                }
1996                
1997                throw new SQLException(errorBuf.toString(), xOpen, errno);
1998            } else {
1999                serverErrorMessage = resultPacket.readString();
2000                clearInputStream();
2001
2002                if (serverErrorMessage.indexOf("Unknown column") != -1) {
2003                    
2004                    StringBuffer errorBuf = new StringBuffer();
2005
2006                    String xOpenErrorMessage = SQLError.get(SQLError.SQL_STATE_COLUMN_NOT_FOUND);
2007
2008                    if (!this.connection.getUseOnlyServerErrorMessages()) {
2009                        if (xOpenErrorMessage != null) {
2010                            errorBuf.append(xOpenErrorMessage);
2011                            errorBuf.append(" message from server: \""); 
2012                        }
2013                    }
2014
2015                    errorBuf.append(serverErrorMessage);
2016
2017                    if (!this.connection.getUseOnlyServerErrorMessages()) {
2018                        if (xOpenErrorMessage != null) {
2019                            errorBuf.append("\"");
2020                        }
2021                    }
2022                    
2023                    throw new java.sql.SQLException(errorBuf.toString(),
2024                        SQLError.SQL_STATE_COLUMN_NOT_FOUND, -1);
2025                } else {
2026                  StringBuffer errorBuf = new StringBuffer();
2027
2028                    String xOpenErrorMessage = SQLError.get(SQLError.SQL_STATE_GENERAL_ERROR);
2029
2030                    if (!this.connection.getUseOnlyServerErrorMessages()) {
2031                        if (xOpenErrorMessage != null) {
2032                            errorBuf.append(xOpenErrorMessage);
2033                            errorBuf.append(" message from server: \""); 
2034                        }
2035                    }
2036
2037                    errorBuf.append(serverErrorMessage);
2038
2039                    if (!this.connection.getUseOnlyServerErrorMessages()) {
2040                        if (xOpenErrorMessage != null) {
2041                            errorBuf.append("\"");
2042                        }
2043                    }
2044                    
2045                    throw new java.sql.SQLException(errorBuf.toString(),
2046                        SQLError.SQL_STATE_GENERAL_ERROR, -1);
2047                }
2048            }
2049        }
2050
2051        return resultPacket;
2052    }
2053
2054    /**
2055     * Sends a large packet to the server as a series of smaller packets
2056     *
2057     * @param packet DOCUMENT ME!
2058     *
2059     * @throws SQLException DOCUMENT ME!
2060     * @throws SQLException DOCUMENT ME!
2061     */
2062    private final void sendSplitPackets(Buffer packet)
2063        throws SQLException {
2064        try {
2065            //
2066            // Big packets are handled by splitting them in packets of MAX_THREE_BYTES
2067            // length. The last packet is always a packet that is < MAX_THREE_BYTES.
2068            // (The last packet may even have a length of 0)
2069            //
2070            //
2071            // NB: Guarded by execSQL. If the driver changes architecture, this
2072            // will need to be synchronized in some other way
2073            //
2074            Buffer headerPacket = (splitBufRef == null) ? null
2075                                                        : (Buffer) (splitBufRef
2076                .get());
2077
2078            //
2079            // Store this packet in a soft reference...It can be re-used if not GC'd (so clients
2080            // that use it frequently won't have to re-alloc the 16M buffer), but we don't
2081            // penalize infrequent users of large packets by keeping 16M allocated all of the time
2082            //
2083            if (headerPacket == null) {
2084                headerPacket = new Buffer((int) (maxThreeBytes + HEADER_LENGTH));
2085                splitBufRef = new SoftReference(headerPacket);
2086            }
2087
2088            int len = packet.getPosition();
2089            int splitSize = (int) maxThreeBytes;
2090            int originalPacketPos = HEADER_LENGTH;
2091            byte[] origPacketBytes = packet.getByteBuffer();
2092            byte[] headerPacketBytes = headerPacket.getByteBuffer();
2093
2094            if (Driver.DEBUG) {
2095                System.out.println("\n\nSending split packets for packet of "
2096                    + len + " bytes:\n");
2097            }
2098
2099            while (len >= maxThreeBytes) {
2100                headerPacket.setPosition(0);
2101                headerPacket.writeLongInt(splitSize);
2102                this.packetSequence++;
2103                headerPacket.writeByte(this.packetSequence);
2104                System.arraycopy(origPacketBytes, originalPacketPos,
2105                    headerPacketBytes, 4, splitSize);
2106                this.mysqlOutput.write(headerPacketBytes, 0,
2107                    splitSize + HEADER_LENGTH);
2108                this.mysqlOutput.flush();
2109
2110                if (Driver.DEBUG) {
2111                    System.out.print("  total packet length (header & data) "
2112                        + (splitSize + HEADER_LENGTH) + "\nheader: ");
2113                    headerPacket.dumpHeader();
2114                    System.out.println();
2115                    System.out.print("last eight bytes: ");
2116                    headerPacket.dumpNBytes(((splitSize + HEADER_LENGTH) - 8), 8);
2117                    System.out.println();
2118                }
2119
2120                originalPacketPos += splitSize;
2121                len -= splitSize;
2122            }
2123
2124            //
2125            // Write last packet
2126            //
2127            headerPacket.clear();
2128            headerPacket.setPosition(0);
2129            headerPacket.writeLongInt(len - HEADER_LENGTH);
2130            this.packetSequence++;
2131            headerPacket.writeByte(this.packetSequence);
2132
2133            if (len != 0) {
2134                System.arraycopy(origPacketBytes, originalPacketPos,
2135                    headerPacketBytes, 4, len - HEADER_LENGTH);
2136            }
2137
2138            this.mysqlOutput.write(headerPacket.getByteBuffer(), 0, len);
2139            this.mysqlOutput.flush();
2140
2141            if (Driver.DEBUG) {
2142                System.out.print("  total packet length (header & data) " + len
2143                    + ",\nheader: ");
2144                headerPacket.dumpHeader();
2145                System.out.println();
2146                System.out.print("last packet bytes: ");
2147                headerPacket.dumpNBytes(0, len);
2148                System.out.println();
2149            }
2150        } catch (IOException ioEx) {
2151            StringBuffer message = new StringBuffer(SQLError.get(
2152                        SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE));
2153            message.append(": ");
2154            message.append(ioEx.getClass().getName());
2155            message.append(", underlying cause: ");
2156            message.append(ioEx.getMessage());
2157
2158            if (!this.connection.useParanoidErrorMessages()) {
2159                message.append(Util.stackTraceToString(ioEx));
2160            }
2161
2162            throw new java.sql.SQLException(message.toString(),
2163                SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE, 0);
2164        }
2165    }
2166
2167    private int alignPacketSize(int a, int l) {
2168        return ((((a) + (l)) - 1) & ~((l) - 1));
2169    }
2170
2171    private void checkForOutstandingStreamingData() throws SQLException {
2172        if (this.streamingData != null) {
2173            if (!this.connection.getClobberStreamingResults()) {
2174                throw new SQLException("Streaming result set "
2175                    + this.streamingData + " is still active."
2176                    + " Only one streaming result set may be open and in use per-connection. Ensure that you have called .close() on "
2177                    + " any active result sets before attempting more queries.");
2178            } else {
2179                // Close the result set
2180                this.streamingData.getOwner().realClose(false);
2181
2182                // clear any pending data....
2183                clearInputStream();
2184            }
2185        }
2186    }
2187
2188    private void clearInputStream() throws SQLException {
2189        try {
2190            int len = this.mysqlInput.available();
2191
2192            while (len > 0) {
2193                this.mysqlInput.skip(len);
2194                len = this.mysqlInput.available();
2195            }
2196        } catch (IOException ioEx) {
2197            throw new SQLException("I/O error while clearing input stream of old results",
2198                SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE);
2199        }
2200    }
2201
2202    private Buffer compressPacket(Buffer packet, int offset, int packetLen,
2203        int headerLength) throws SQLException {
2204        packet.writeLongInt(packetLen - headerLength);
2205        packet.writeByte((byte) 0); // wrapped packet has 0 packet seq.
2206
2207        int lengthToWrite = 0;
2208        int compressedLength = 0;
2209        byte[] bytesToCompress = packet.getByteBuffer();
2210        byte[] compressedBytes = null;
2211        int offsetWrite = 0;
2212
2213        if (true /*packetLen < MIN_COMPRESS_LEN*/) {
2214            lengthToWrite = packetLen;
2215            compressedBytes = packet.getByteBuffer();
2216            compressedLength = 0;
2217            offsetWrite = offset;
2218        } else {
2219            compressedBytes = new byte[bytesToCompress.length * 2];
2220
2221            this.deflater.reset();
2222            this.deflater.setInput(bytesToCompress, offset, packetLen);
2223            this.deflater.finish();
2224
2225            int compLen = this.deflater.deflate(compressedBytes);
2226
2227            if (compLen > packetLen) {
2228                lengthToWrite = packetLen;
2229                compressedBytes = packet.getByteBuffer();
2230                compressedLength = 0;
2231                offsetWrite = offset;
2232            } else {
2233                lengthToWrite = compLen;
2234                headerLength += COMP_HEADER_LENGTH;
2235                compressedLength = packetLen;
2236            }
2237        }
2238
2239        Buffer compressedPacket = new Buffer(packetLen + headerLength);
2240
2241        compressedPacket.setPosition(0);
2242        compressedPacket.writeLongInt(lengthToWrite);
2243        compressedPacket.writeByte(this.packetSequence);
2244        compressedPacket.writeLongInt(compressedLength);
2245        compressedPacket.writeBytesNoNull(compressedBytes, offsetWrite,
2246            lengthToWrite);
2247
2248        return compressedPacket;
2249    }
2250
2251    private SocketFactory createSocketFactory() throws SQLException {
2252        try {
2253            if (socketFactoryClassName == null) {
2254                throw new SQLException("No name specified for socket factory",
2255                    SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE);
2256            }
2257
2258            return (SocketFactory) (Class.forName(socketFactoryClassName)
2259                                         .newInstance());
2260        } catch (Exception ex) {
2261            throw new SQLException("Could not create socket factory '"
2262                + socketFactoryClassName + "' due to underlying exception: "
2263                + ex.toString()
2264                + (this.connection.useParanoidErrorMessages() ? ""
2265                                                              : Util
2266                .stackTraceToString(ex)),
2267                SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE);
2268        }
2269    }
2270
2271    /**
2272     * Ensures that we don't hold on to overly-large send packets
2273     */
2274    private void reclaimLargeSharedSendPacket() {
2275        if ((this.sharedSendPacket != null)
2276                && (this.sharedSendPacket.getBufLength() > 1048576)) {
2277            this.sharedSendPacket = new Buffer(this.connection
2278                    .getNetBufferLength());
2279        }
2280    }
2281
2282    /**
2283     * Secure authentication for 4.1 and newer servers.
2284     *
2285     * @param packet DOCUMENT ME!
2286     * @param packLength
2287     * @param user
2288     * @param password
2289     * @param database DOCUMENT ME!
2290     * @param writeClientParams
2291     *
2292     * @throws SQLException
2293     */
2294    private void secureAuth(Buffer packet, int packLength, String user,
2295        String password, String database, boolean writeClientParams)
2296        throws SQLException {
2297        // Passwords can be 16 chars long
2298        if (packet == null) {
2299            packet = new Buffer(packLength);
2300        }
2301
2302        if (writeClientParams) {
2303            if (this.use41Extensions) {
2304                if (versionMeetsMinimum(4, 1, 1)) {
2305                    packet.writeLong(this.clientParam);
2306                    packet.writeLong(this.maxThreeBytes);
2307
2308                    // charset, JDBC will connect as 'latin1',
2309                    // and use 'SET NAMES' to change to the desired
2310                    // charset after the connection is established.
2311                    packet.writeByte((byte) 8);
2312
2313                    // Set of bytes reserved for future use.
2314                    packet.writeBytesNoNull(new byte[23]);
2315                } else {
2316                    packet.writeLong(this.clientParam);
2317                    packet.writeLong(this.maxThreeBytes);
2318                }
2319            } else {
2320                packet.writeInt((int) this.clientParam);
2321                packet.writeLongInt(this.maxThreeBytes);
2322            }
2323        }
2324
2325        // User/Password data
2326        packet.writeString(user);
2327
2328        if (password.length() != 0) {
2329            /* Prepare false scramble  */
2330            packet.writeString(FALSE_SCRAMBLE);
2331        } else {
2332            /* For empty password*/
2333            packet.writeString("");
2334        }
2335
2336        if (((this.serverCapabilities & CLIENT_CONNECT_WITH_DB) != 0)
2337                && (database != null) && (database.length() > 0)) {
2338            packet.writeString(database);
2339        }
2340
2341        send(packet);
2342
2343        //
2344        // Don't continue stages if password is empty
2345        //
2346        if (password.length() > 0) {
2347            Buffer b = readPacket();
2348
2349            b.setPosition(0);
2350
2351            byte[] replyAsBytes = b.getByteBuffer();
2352
2353            if ((replyAsBytes.length == 25) && (replyAsBytes[0] != 0)) {
2354                // Old passwords will have '*' at the first byte of hash */
2355                if (replyAsBytes[0] != '*') {
2356                    try {
2357                        /* Build full password hash as it is required to decode scramble */
2358                        byte[] buff = Security.passwordHashStage1(password);
2359
2360                        /* Store copy as we'll need it later */
2361                        byte[] passwordHash = new byte[buff.length];
2362                        System.arraycopy(buff, 0, passwordHash, 0, buff.length);
2363
2364                        /* Finally hash complete password using hash we got from server */
2365                        passwordHash = Security.passwordHashStage2(passwordHash,
2366                                replyAsBytes);
2367
2368                        byte[] packetDataAfterSalt = new byte[replyAsBytes.length
2369                            - 5];
2370
2371                        System.arraycopy(replyAsBytes, 4, packetDataAfterSalt,
2372                            0, replyAsBytes.length - 5);
2373
2374                        byte[] mysqlScrambleBuff = new byte[20];
2375
2376                        /* Decypt and store scramble 4 = hash for stage2 */
2377                        Security.passwordCrypt(packetDataAfterSalt,
2378                            mysqlScrambleBuff, passwordHash, 20);
2379
2380                        /* Encode scramble with password. Recycle buffer */
2381                        Security.passwordCrypt(mysqlScrambleBuff, buff, buff, 20);
2382
2383                        Buffer packet2 = new Buffer(25);
2384                        packet2.writeBytesNoNull(buff);
2385
2386                        this.packetSequence++;
2387
2388                        send(packet2, 24);
2389                    } catch (NoSuchAlgorithmException nse) {
2390                        throw new SQLException(
2391                            "Failed to create message digest 'SHA-1' for authentication. "
2392                            + " You must use a JDK that supports JCE to be able to use secure connection authentication",
2393                            SQLError.SQL_STATE_GENERAL_ERROR);
2394                    }
2395                } else {
2396                    try {
2397                        /* Create password to decode scramble */
2398                        byte[] passwordHash = Security.createKeyFromOldPassword(password);
2399
2400                        /* Decypt and store scramble 4 = hash for stage2 */
2401                        byte[] netReadPos4 = new byte[replyAsBytes.length - 5];
2402
2403                        System.arraycopy(replyAsBytes, 4, netReadPos4, 0,
2404                            replyAsBytes.length - 5);
2405
2406                        byte[] mysqlScrambleBuff = new byte[20];
2407
2408                        /* Decypt and store scramble 4 = hash for stage2 */
2409                        Security.passwordCrypt(netReadPos4, mysqlScrambleBuff,
2410                            passwordHash, 20);
2411
2412                        /* Finally scramble decoded scramble with password */
2413                        String scrambledPassword = Util.scramble(new String(
2414                                    mysqlScrambleBuff), password);
2415
2416                        Buffer packet2 = new Buffer(packLength);
2417                        packet2.writeString(scrambledPassword);
2418                        this.packetSequence++;
2419
2420                        send(packet2, 24);
2421                    } catch (NoSuchAlgorithmException nse) {
2422                        throw new SQLException(
2423                            "Failed to create message digest 'SHA-1' for authentication. "
2424                            + " You must use a JDK that supports JCE to be able to use secure connection authentication",
2425                            SQLError.SQL_STATE_GENERAL_ERROR);
2426                    }
2427                }
2428            }
2429        }
2430    }
2431
2432    /**
2433     * Secure authentication for 4.1.1 and newer servers.
2434     *
2435     * @param packet DOCUMENT ME!
2436     * @param packLength
2437     * @param user
2438     * @param password
2439     * @param database DOCUMENT ME!
2440     * @param writeClientParams
2441     *
2442     * @throws SQLException
2443     */
2444    private void secureAuth411(Buffer packet, int packLength, String user,
2445        String password, String database, boolean writeClientParams)
2446        throws SQLException {
2447        //  SERVER:  public_seed=create_random_string()
2448        //       send(public_seed)
2449        //
2450        //  CLIENT:  recv(public_seed)
2451        //       hash_stage1=sha1("password")
2452        //       hash_stage2=sha1(hash_stage1)
2453        //       reply=xor(hash_stage1, sha1(public_seed,hash_stage2)
2454        //
2455        //       // this three steps are done in scramble()
2456        //
2457        //       send(reply)
2458        //
2459        //
2460        //  SERVER:  recv(reply)
2461        //       hash_stage1=xor(reply, sha1(public_seed,hash_stage2))
2462        //       candidate_hash2=sha1(hash_stage1)
2463        //       check(candidate_hash2==hash_stage2)
2464        // Passwords can be 16 chars long
2465        if (packet == null) {
2466            packet = new Buffer(packLength);
2467        }
2468
2469        if (writeClientParams) {
2470            if (this.use41Extensions) {
2471                if (versionMeetsMinimum(4, 1, 1)) {
2472                    packet.writeLong(this.clientParam);
2473                    packet.writeLong(this.maxThreeBytes);
2474
2475                    // charset, JDBC will connect as 'latin1',
2476                    // and use 'SET NAMES' to change to the desired
2477                    // charset after the connection is established.
2478                    packet.writeByte((byte) 8);
2479
2480                    // Set of bytes reserved for future use.
2481                    packet.writeBytesNoNull(new byte[23]);
2482                } else {
2483                    packet.writeLong(this.clientParam);
2484                    packet.writeLong(this.maxThreeBytes);
2485                }
2486            } else {
2487                packet.writeInt((int) this.clientParam);
2488                packet.writeLongInt(this.maxThreeBytes);
2489            }
2490        }
2491
2492        // User/Password data
2493        packet.writeString(user);
2494
2495        if (password.length() != 0) {
2496            packet.writeByte((byte) 0x14);
2497
2498            try {
2499                packet.writeBytesNoNull(Security.scramble411(password, this.seed));
2500            } catch (NoSuchAlgorithmException nse) {
2501                throw new SQLException(
2502                    "Failed to create message digest 'SHA-1' for authentication. "
2503                    + " You must use a JDK that supports JCE to be able to use secure connection authentication",
2504                    SQLError.SQL_STATE_GENERAL_ERROR);
2505            }
2506        } else {
2507            /* For empty password*/
2508            packet.writeByte((byte) 0);
2509        }
2510
2511        if (((this.serverCapabilities & CLIENT_CONNECT_WITH_DB) != 0)
2512                && (database != null) && (database.length() > 0)) {
2513            packet.writeString(database);
2514        }
2515
2516        send(packet);
2517
2518        byte savePacketSequence = this.packetSequence++;
2519
2520        Buffer reply = checkErrorPacket();
2521
2522        if (reply.isLastDataPacket()) {
2523            /*
2524                  By sending this very specific reply server asks us to send scrambled
2525                  password in old format. The reply contains scramble_323.
2526            */
2527            this.packetSequence = ++savePacketSequence;
2528            packet.clear();
2529
2530            String seed323 = this.seed.substring(0, 8);
2531            packet.writeString(Util.newCrypt(password, seed323));
2532            send(packet);
2533
2534            /* Read what server thinks about out new auth message report */
2535            checkErrorPacket();
2536        }
2537    }
2538
2539    /**
2540     * Secure authentication for 4.1.1 and newer servers.
2541     *
2542     * @param packLength
2543     * @param serverCapabilities DOCUMENT ME!
2544     * @param clientParam
2545     * @param user
2546     * @param password
2547     * @param database DOCUMENT ME!
2548     *
2549     * @throws SQLException
2550     */
2551    private void secureAuth411(int packLength, int serverCapabilities,
2552        long clientParam, String user, String password, String database)
2553        throws SQLException {
2554        //  SERVER:  public_seed=create_random_string()
2555        //       send(public_seed)
2556        //
2557        //  CLIENT:  recv(public_seed)
2558        //       hash_stage1=sha1("password")
2559        //       hash_stage2=sha1(hash_stage1)
2560        //       reply=xor(hash_stage1, sha1(public_seed,hash_stage2)
2561        //
2562        //       // this three steps are done in scramble()
2563        //
2564        //       send(reply)
2565        //
2566        //
2567        //  SERVER:  recv(reply)
2568        //       hash_stage1=xor(reply, sha1(public_seed,hash_stage2))
2569        //       candidate_hash2=sha1(hash_stage1)
2570        //       check(candidate_hash2==hash_stage2)
2571        // Passwords can be 16 chars long
2572        Buffer packet = new Buffer(packLength);
2573
2574        if (this.use41Extensions) {
2575            if (versionMeetsMinimum(4, 1, 1)) {
2576                packet.writeLong(this.clientParam);
2577                packet.writeLong(this.maxThreeBytes);
2578
2579                // charset, JDBC will connect as 'latin1',
2580                // and use 'SET NAMES' to change to the desired
2581                // charset after the connection is established.
2582                packet.writeByte((byte) 8);
2583
2584                // Set of bytes reserved for future use.
2585                packet.writeBytesNoNull(new byte[23]);
2586            } else {
2587                packet.writeLong(this.clientParam);
2588                packet.writeLong(this.maxThreeBytes);
2589            }
2590        } else {
2591            packet.writeInt((int) this.clientParam);
2592            packet.writeLongInt(this.maxThreeBytes);
2593        }
2594
2595        // User/Password data
2596        packet.writeString(user);
2597
2598        if (password.length() != 0) {
2599            packet.writeByte((byte) 0x14);
2600
2601            try {
2602                packet.writeBytesNoNull(Security.scramble411(password, this.seed));
2603            } catch (NoSuchAlgorithmException nse) {
2604                throw new SQLException(
2605                    "Failed to create message digest 'SHA-1' for authentication. "
2606                    + " You must use a JDK that supports JCE to be able to use secure connection authentication",
2607                    SQLError.SQL_STATE_GENERAL_ERROR);
2608            }
2609        } else {
2610            /* For empty password*/
2611            packet.writeByte((byte) 0);
2612        }
2613
2614        if (((serverCapabilities & CLIENT_CONNECT_WITH_DB) != 0)
2615                && (database != null) && (database.length() > 0)) {
2616            packet.writeString(database);
2617        }
2618
2619        send(packet);
2620
2621        byte savePacketSequence = this.packetSequence++;
2622
2623        Buffer reply = checkErrorPacket();
2624
2625        if (reply.isLastDataPacket()) {
2626            /*
2627              By sending this very specific reply server asks us to send scrambled
2628              password in old format. The reply contains scramble_323.
2629            */
2630            this.packetSequence = ++savePacketSequence;
2631            packet.clear();
2632
2633            String seed323 = this.seed.substring(0, 8);
2634            packet.writeString(Util.newCrypt(password, seed323));
2635            send(packet);
2636
2637            /* Read what server thinks about out new auth message report */
2638            checkErrorPacket();
2639        }
2640    }
2641}