EMMA Coverage Report (generated Wed Jul 26 14:28:59 CDT 2006)
[all classes][com.mysql.jdbc]

COVERAGE SUMMARY FOR SOURCE FILE [MysqlIO.java]

nameclass, %method, %block, %line, %
MysqlIO.java100% (1/1)86%  (60/70)65%  (4510/6920)69%  (1025/1486)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class MysqlIO100% (1/1)86%  (60/70)65%  (4510/6920)69%  (1025/1486)
changeDatabaseTo (String): void 0%   (0/1)0%   (0/54)0%   (0/10)
checkPacketSequencing (byte): void 0%   (0/1)0%   (0/91)0%   (0/7)
dumpPacketRingBuffer (): void 0%   (0/1)0%   (0/55)0%   (0/10)
enqueuePacketForDebugging (boolean, boolean, int, byte [], Buffer): void 0%   (0/1)0%   (0/173)0%   (0/34)
getCalendarInstanceForSessionOrNew (): Calendar 0%   (0/1)0%   (0/9)0%   (0/3)
getLastPacketSentTimeMs (): long 0%   (0/1)0%   (0/3)0%   (0/1)
isDataAvailable (): boolean 0%   (0/1)0%   (0/18)0%   (0/3)
negotiateSSLConnection (String, String, String, int): void 0%   (0/1)0%   (0/154)0%   (0/31)
resetReadPacketSequence (): void 0%   (0/1)0%   (0/4)0%   (0/2)
secureAuth (Buffer, int, String, String, String, boolean): void 0%   (0/1)0%   (0/266)0%   (0/56)
getPacketDumpToLog (Buffer, int): String 100% (1/1)20%  (7/35)25%  (2/8)
createSocketFactory (): SocketFactory 100% (1/1)21%  (9/43)40%  (2/5)
isSetNeededForAutoCommitMode (boolean): boolean 100% (1/1)22%  (9/41)29%  (2/7)
closeStreamer (RowData): void 100% (1/1)24%  (11/45)67%  (4/6)
unpackNativeEncodedColumn (Buffer, Field [], int, Object []): void 100% (1/1)40%  (281/694)39%  (58.3/151)
adjustStartForFieldLength (int, int): int 100% (1/1)52%  (15/29)57%  (4/7)
secureAuth411 (Buffer, int, String, String, String, boolean): void 100% (1/1)55%  (78/143)62%  (21/34)
compressPacket (Buffer, int, int, int): Buffer 100% (1/1)59%  (59/100)59%  (19/32)
checkErrorPacket (int): Buffer 100% (1/1)61%  (129/212)70%  (33/47)
forceClose (): void 100% (1/1)65%  (22/34)62%  (10/16)
readPacket (): Buffer 100% (1/1)69%  (144/209)70%  (26.5/38)
sqlQueryDirect (Statement, String, String, Buffer, int, Connection, int, int,... 100% (1/1)69%  (350/506)79%  (65/82)
sendSplitPackets (Buffer): void 100% (1/1)70%  (142/202)73%  (33.7/46)
reuseAndReadPacket (Buffer): Buffer 100% (1/1)72%  (373/519)76%  (74/98)
buildResultSetWithUpdates (Statement, Buffer): ResultSet 100% (1/1)74%  (79/107)83%  (21.7/26)
doHandshake (String, String, String): void 100% (1/1)76%  (495/655)73%  (102/139)
checkForCharsetMismatch (): void 100% (1/1)76%  (22/29)75%  (6/8)
extractNativeEncodedColumn (Buffer, Field [], int, Object []): void 100% (1/1)78%  (101/129)93%  (28/30)
sendCommand (int, String, Buffer, boolean, String): Buffer 100% (1/1)79%  (156/197)83%  (39/47)
<static initializer> 100% (1/1)84%  (26/31)88%  (11.4/13)
changeUser (String, String, String): void 100% (1/1)85%  (100/118)89%  (23/26)
isVersion (int, int, int): boolean 100% (1/1)88%  (14/16)88%  (0.9/1)
readFully (InputStream, byte [], int, int): int 100% (1/1)88%  (30/34)89%  (8/9)
explainSlowQuery (byte [], String): void 100% (1/1)89%  (59/66)88%  (14.9/17)
MysqlIO (String, int, Properties, String, Connection, int): void 100% (1/1)89%  (253/283)91%  (67/74)
unpackField (Buffer, boolean): Field 100% (1/1)90%  (224/249)89%  (51/57)
sendFileToServer (Statement, String): ResultSet 100% (1/1)91%  (233/257)93%  (51/55)
readAllResults (Statement, int, int, int, boolean, String, Buffer, boolean, l... 100% (1/1)94%  (106/113)91%  (20/22)
send (Buffer, int): void 100% (1/1)94%  (159/169)97%  (36/37)
readServerStatusForResultSets (Buffer): void 100% (1/1)95%  (42/44)99%  (9.9/10)
readResultsForQueryOrUpdate (Statement, int, int, int, boolean, String, Buffe... 100% (1/1)97%  (61/63)99%  (13.8/14)
alignPacketSize (int, int): int 100% (1/1)100% (12/12)100% (1/1)
buildResultSetWithRows (Statement, String, Field [], RowData, int, int, boole... 100% (1/1)100% (48/48)100% (11/11)
checkErrorPacket (): Buffer 100% (1/1)100% (4/4)100% (1/1)
checkForOutstandingStreamingData (): void 100% (1/1)100% (36/36)100% (6/6)
clearInputStream (): void 100% (1/1)100% (29/29)100% (8/8)
disableMultiQueries (): void 100% (1/1)100% (20/20)100% (6/6)
enableMultiQueries (): void 100% (1/1)100% (20/20)100% (6/6)
fetchRowsViaCursor (List, long, Field [], int): List 100% (1/1)100% (54/54)100% (12/12)
getHost (): String 100% (1/1)100% (3/3)100% (1/1)
getMaxBuf (): int 100% (1/1)100% (2/2)100% (1/1)
getResultSet (Statement, long, int, int, int, boolean, String, boolean, boole... 100% (1/1)100% (153/153)100% (29/29)
getServerMajorVersion (): int 100% (1/1)100% (3/3)100% (1/1)
getServerMinorVersion (): int 100% (1/1)100% (3/3)100% (1/1)
getServerStatus (): int 100% (1/1)100% (3/3)100% (1/1)
getServerSubMinorVersion (): int 100% (1/1)100% (3/3)100% (1/1)
getServerVersion (): String 100% (1/1)100% (3/3)100% (1/1)
getSharedSendPacket (): Buffer 100% (1/1)100% (14/14)100% (3/3)
getThreadId (): long 100% (1/1)100% (3/3)100% (1/1)
hadWarnings (): boolean 100% (1/1)100% (3/3)100% (1/1)
hasLongColumnInfo (): boolean 100% (1/1)100% (3/3)100% (1/1)
nextRow (Field [], int, boolean, int): Object [] 100% (1/1)100% (59/59)100% (16/16)
quit (): void 100% (1/1)100% (19/19)100% (6/6)
readSingleRowSet (long, int, int, boolean, Field []): RowData 100% (1/1)100% (53/53)100% (14/14)
reclaimLargeReusablePacket (): void 100% (1/1)100% (17/17)100% (3/3)
reclaimLargeSharedSendPacket (): void 100% (1/1)100% (17/17)100% (3/3)
resetMaxBuf (): void 100% (1/1)100% (6/6)100% (2/2)
scanForAndThrowDataTruncation (): void 100% (1/1)100% (21/21)100% (3/3)
unpackBinaryResultSetRow (Field [], Buffer, int): Object [] 100% (1/1)100% (78/78)100% (18/18)
versionMeetsMinimum (int, int, int): boolean 100% (1/1)100% (32/32)100% (9/9)

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      There are special exceptions to the terms and conditions of the GPL
9      as it is applied to this software. View the full text of the
10      exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
11      software distribution.
12 
13      This program is distributed in the hope that it will be useful,
14      but WITHOUT ANY WARRANTY; without even the implied warranty of
15      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16      GNU General Public License for more details.
17 
18      You should have received a copy of the GNU General Public License
19      along with this program; if not, write to the Free Software
20      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21 
22 
23 
24 */
25package com.mysql.jdbc;
26 
27import com.mysql.jdbc.profiler.ProfileEventSink;
28import com.mysql.jdbc.profiler.ProfilerEvent;
29import com.mysql.jdbc.util.ReadAheadInputStream;
30import com.mysql.jdbc.util.ResultSetUtil;
31 
32import java.io.BufferedInputStream;
33import java.io.BufferedOutputStream;
34import java.io.ByteArrayOutputStream;
35import java.io.EOFException;
36import java.io.FileInputStream;
37import java.io.IOException;
38import java.io.InputStream;
39import java.io.OutputStreamWriter;
40 
41import java.lang.ref.SoftReference;
42 
43import java.math.BigInteger;
44 
45import java.net.InetSocketAddress;
46import java.net.MalformedURLException;
47import java.net.Socket;
48import java.net.URL;
49 
50import java.nio.ByteBuffer;
51import java.nio.channels.SocketChannel;
52 
53import java.security.NoSuchAlgorithmException;
54 
55import java.sql.SQLException;
56 
57import java.util.ArrayList;
58import java.util.Calendar;
59import java.util.Iterator;
60import java.util.LinkedList;
61import java.util.List;
62import java.util.Properties;
63import java.util.zip.Deflater;
64 
65 
66/**
67 * This class is used by Connection for communicating with the MySQL server.
68 *
69 * @author Mark Matthews
70 * @version $Id: MysqlIO.java 5417 2006-06-20 21:33:56 +0000 (Tue, 20 Jun 2006) mmatthews $
71 *
72 * @see java.sql.Connection
73 */
74class MysqlIO {
75    protected static final int NULL_LENGTH = ~0;
76    protected static final int COMP_HEADER_LENGTH = 3;
77    protected static final int MIN_COMPRESS_LEN = 50;
78    protected static final int HEADER_LENGTH = 4;
79    private static int maxBufferSize = 65535;
80    private static final int CLIENT_COMPRESS = 32; /* Can use compression
81    protcol */
82    protected static final int CLIENT_CONNECT_WITH_DB = 8;
83    private static final int CLIENT_FOUND_ROWS = 2;
84    private static final int CLIENT_LOCAL_FILES = 128; /* Can use LOAD DATA
85    LOCAL */
86 
87    /* Found instead of
88       affected rows */
89    private static final int CLIENT_LONG_FLAG = 4; /* Get all column flags */
90    private static final int CLIENT_LONG_PASSWORD = 1; /* new more secure
91    passwords */
92    private static final int CLIENT_PROTOCOL_41 = 512; // for > 4.1.1
93    private static final int CLIENT_INTERACTIVE = 1024;
94    protected static final int CLIENT_SSL = 2048;
95    private static final int CLIENT_TRANSACTIONS = 8192; // Client knows about transactions
96    protected static final int CLIENT_RESERVED = 16384; // for 4.1.0 only
97    protected static final int CLIENT_SECURE_CONNECTION = 32768;
98    private static final int CLIENT_MULTI_QUERIES = 65536; // Enable/disable multiquery support
99    private static final int CLIENT_MULTI_RESULTS = 131072; // Enable/disable multi-results
100    private static final int SERVER_STATUS_IN_TRANS = 1;
101    private static final int SERVER_STATUS_AUTOCOMMIT = 2; // Server in auto_commit mode
102    private static final int SERVER_MORE_RESULTS_EXISTS = 8; // Multi query - next query exists
103    private static final int SERVER_QUERY_NO_GOOD_INDEX_USED = 16;
104    private static final int SERVER_QUERY_NO_INDEX_USED = 32;
105        private static final int  SERVER_STATUS_CURSOR_EXISTS = 64;
106    private static final String FALSE_SCRAMBLE = "xxxxxxxx"; //$NON-NLS-1$
107    protected static final int MAX_QUERY_SIZE_TO_LOG = 1024; // truncate logging of queries at 1K
108    protected static final int MAX_QUERY_SIZE_TO_EXPLAIN = 1024 * 1024; // don't explain queries above 1MB
109 
110    /**
111     * We store the platform 'encoding' here, only used to avoid munging
112     * filenames for LOAD DATA LOCAL INFILE...
113     */
114    private static String jvmPlatformCharset = null;
115    
116    /**
117     * Are we using packed or unpacked binary result set rows?
118     */
119    private boolean binaryResultsAreUnpacked = true;
120 
121    /**
122     * We need to have a 'marker' for all-zero datetimes so that ResultSet
123     * can decide what to do based on connection setting
124     */
125    protected final static String ZERO_DATE_VALUE_MARKER = "0000-00-00";
126    protected final static String ZERO_DATETIME_VALUE_MARKER = "0000-00-00 00:00:00";
127 
128    static {
129        OutputStreamWriter outWriter = null;
130 
131        //
132        // Use the I/O system to get the encoding (if possible), to avoid
133        // security restrictions on System.getProperty("file.encoding") in
134        // applets (why is that restricted?)
135        //
136        try {
137            outWriter = new OutputStreamWriter(new ByteArrayOutputStream());
138            jvmPlatformCharset = outWriter.getEncoding();
139        } finally {
140            try {
141                if (outWriter != null) {
142                    outWriter.close();
143                }
144            } catch (IOException ioEx) {
145                // ignore
146            }
147        }
148    }
149 
150    /** Max number of bytes to dump when tracing the protocol */
151    private final static int MAX_PACKET_DUMP_LENGTH = 1024;
152    private boolean packetSequenceReset = false;
153    protected int serverCharsetIndex;
154 
155    //
156    // Use this when reading in rows to avoid thousands of new()
157    // calls, because the byte arrays just get copied out of the
158    // packet anyway
159    //
160    private Buffer reusablePacket = null;
161    private Buffer sendPacket = null;
162    private Buffer sharedSendPacket = null;
163 
164    /** Data to the server */
165    protected BufferedOutputStream mysqlOutput = null;
166    protected com.mysql.jdbc.Connection connection;
167    private Deflater deflater = null;
168    protected InputStream mysqlInput = null;
169    private LinkedList packetDebugRingBuffer = null;
170    private RowData streamingData = null;
171 
172    /** The connection to the server */
173    protected Socket mysqlConnection = null;
174    private SocketFactory socketFactory = null;
175 
176    //
177    // Packet used for 'LOAD DATA LOCAL INFILE'
178    //
179    // We use a SoftReference, so that we don't penalize intermittent
180    // use of this feature
181    //
182    private SoftReference loadFileBufRef;
183 
184    //
185    // Used to send large packets to the server versions 4+
186    // We use a SoftReference, so that we don't penalize intermittent
187    // use of this feature
188    //
189    private SoftReference splitBufRef;
190    protected String host = null;
191    protected String seed;
192    private String serverVersion = null;
193    private String socketFactoryClassName = null;
194    private byte[] packetHeaderBuf = new byte[4];
195    private boolean colDecimalNeedsBump = false; // do we need to increment the colDecimal flag?
196    private boolean hadWarnings = false;
197    private boolean has41NewNewProt = false;
198 
199    /** Does the server support long column info? */
200    private boolean hasLongColumnInfo = false;
201    private boolean isInteractiveClient = false;
202    private boolean logSlowQueries = false;
203 
204    /**
205     * Does the character set of this connection match the character set of the
206     * platform
207     */
208    private boolean platformDbCharsetMatches = true; // changed once we've connected.
209    private boolean profileSql = false;
210    private boolean queryBadIndexUsed = false;
211    private boolean queryNoIndexUsed = false;
212 
213    /** Should we use 4.1 protocol extensions? */
214    private boolean use41Extensions = false;
215    private boolean useCompression = false;
216    private boolean useNewLargePackets = false;
217    private boolean useNewUpdateCounts = false; // should we use the new larger update counts?
218    private byte packetSequence = 0;
219    private byte readPacketSequence = -1;
220    private boolean checkPacketSequence = false;
221    byte protocolVersion = 0;
222    private int maxAllowedPacket = 1024 * 1024;
223    protected int maxThreeBytes = 255 * 255 * 255;
224    protected int port = 3306;
225    protected int serverCapabilities;
226    private int serverMajorVersion = 0;
227    private int serverMinorVersion = 0;
228    private int serverStatus = 0;
229    private int serverSubMinorVersion = 0;
230    private int warningCount = 0;
231    protected long clientParam = 0;
232    protected long lastPacketSentTimeMs = 0;
233    private boolean traceProtocol = false;
234    private boolean enablePacketDebug = false;
235    private ByteBuffer channelClearBuf;
236    private Calendar sessionCalendar;
237        private boolean useConnectWithDb;
238        private boolean needToGrabQueryFromPacket;
239        private boolean autoGenerateTestcaseScript;
240        private long threadId;
241 
242    /**
243     * Constructor:  Connect to the MySQL server and setup a stream connection.
244     *
245     * @param host the hostname to connect to
246     * @param port the port number that the server is listening on
247     * @param props the Properties from DriverManager.getConnection()
248     * @param socketFactoryClassName the socket factory to use
249     * @param conn the Connection that is creating us
250     * @param socketTimeout the timeout to set for the socket (0 means no
251     *        timeout)
252     *
253     * @throws IOException if an IOException occurs during connect.
254     * @throws SQLException if a database access error occurs.
255     */
256    public MysqlIO(String host, int port, Properties props,
257        String socketFactoryClassName, com.mysql.jdbc.Connection conn,
258        int socketTimeout) throws IOException, SQLException {
259        this.connection = conn;
260 
261        if (this.connection.getEnablePacketDebug()) {
262            this.packetDebugRingBuffer = new LinkedList();
263        }
264 
265        this.logSlowQueries = this.connection.getLogSlowQueries();
266 
267        this.reusablePacket = new Buffer(
268                        this.connection.getNetBufferLength());
269 
270        this.port = port;
271        this.host = host;
272 
273        this.socketFactoryClassName = socketFactoryClassName;
274        this.socketFactory = createSocketFactory();
275        
276        this.mysqlConnection = this.socketFactory.connect(this.host,
277                        this.port, props);
278        
279        if (socketTimeout != 0) {
280                try {
281                        this.mysqlConnection.setSoTimeout(socketTimeout);
282                } catch (Exception ex) {
283                        /* Ignore if the platform does not support it */
284                        ;
285                }
286        }
287        
288        this.mysqlConnection = this.socketFactory.beforeHandshake();
289        
290        if (this.connection.getUseReadAheadInput()) {
291                this.mysqlInput = new ReadAheadInputStream(this.mysqlConnection.getInputStream(), 16384, 
292                                this.connection.getTraceProtocol(),
293                                this.connection.getLog());        
294        } else if (this.connection.useUnbufferedInput()) {
295                this.mysqlInput = this.mysqlConnection.getInputStream();
296        } else {
297                this.mysqlInput = new BufferedInputStream(this.mysqlConnection.getInputStream(),
298                                16384);
299        }
300        
301        this.mysqlOutput = new BufferedOutputStream(this.mysqlConnection.getOutputStream(),
302                        16384);
303        
304 
305        this.isInteractiveClient = this.connection.getInteractiveClient();
306        this.profileSql = this.connection.getProfileSql();
307        this.sessionCalendar = Calendar.getInstance();
308        this.autoGenerateTestcaseScript = this.connection.getAutoGenerateTestcaseScript();
309        
310        this.needToGrabQueryFromPacket = (this.profileSql || 
311                        this.logSlowQueries || 
312                        this.autoGenerateTestcaseScript);
313    }
314 
315    /**
316     * Does the server send back extra column info?
317     *
318     * @return true if so
319     */
320    public boolean hasLongColumnInfo() {
321        return this.hasLongColumnInfo;
322    }
323 
324    protected boolean isDataAvailable() throws SQLException {
325        try {
326            return this.mysqlInput.available() > 0;
327        } catch (IOException ioEx) {
328            throw new CommunicationsException(this.connection,
329                this.lastPacketSentTimeMs, ioEx);
330        }
331    }
332 
333    /**
334     * DOCUMENT ME!
335     *
336     * @return Returns the lastPacketSentTimeMs.
337     */
338    protected long getLastPacketSentTimeMs() {
339        return this.lastPacketSentTimeMs;
340    }
341 
342    /**
343     * Build a result set. Delegates to buildResultSetWithRows() to build a
344     * JDBC-version-specific ResultSet, given rows as byte data, and field
345     * information.
346     *
347     * @param callingStatement DOCUMENT ME!
348     * @param columnCount the number of columns in the result set
349     * @param maxRows the maximum number of rows to read (-1 means all rows)
350     * @param resultSetType (TYPE_FORWARD_ONLY, TYPE_SCROLL_????)
351     * @param resultSetConcurrency the type of result set (CONCUR_UPDATABLE or
352     *        READ_ONLY)
353     * @param streamResults should the result set be read all at once, or
354     *        streamed?
355     * @param catalog the database name in use when the result set was created
356     * @param isBinaryEncoded is this result set in native encoding?
357     * @param unpackFieldInfo should we read MYSQL_FIELD info (if available)?
358     *
359     * @return a result set
360     *
361     * @throws SQLException if a database access error occurs
362     */
363    protected ResultSet getResultSet(Statement callingStatement,
364        long columnCount, int maxRows, int resultSetType,
365        int resultSetConcurrency, boolean streamResults, String catalog,
366        boolean isBinaryEncoded, boolean unpackFieldInfo)
367        throws SQLException {
368        Buffer packet; // The packet from the server
369        Field[] fields = null;
370 
371        if (unpackFieldInfo) {
372            fields = new Field[(int) columnCount];
373        }
374 
375        // Read in the column information
376        for (int i = 0; i < columnCount; i++) {
377            Buffer fieldPacket = null;
378 
379            fieldPacket = readPacket();
380            
381            if (unpackFieldInfo) {
382                fields[i] = unpackField(fieldPacket, false);
383            }
384        }
385 
386        packet = reuseAndReadPacket(this.reusablePacket);
387 
388        readServerStatusForResultSets(packet);
389                
390                //
391                // Handle cursor-based fetch first
392                //
393                
394                if (this.connection.versionMeetsMinimum(5, 0, 2)
395                                && isBinaryEncoded
396                                && callingStatement != null
397                                && callingStatement.getFetchSize() != 0
398                                && callingStatement.getResultSetType() == ResultSet.TYPE_FORWARD_ONLY) {
399                        ServerPreparedStatement prepStmt = (com.mysql.jdbc.ServerPreparedStatement) callingStatement;
400        
401                        Field[] fieldMetadata = ((com.mysql.jdbc.ResultSetMetaData) prepStmt.getMetaData()).fields;
402 
403                        boolean usingCursor = true;
404                        
405                        //
406                        // Server versions 5.0.5 or newer will only open
407                        // a cursor and set this flag if they can, otherwise
408                        // they punt and go back to mysql_store_results() behavior
409                        //
410                        
411                        if (this.connection.versionMeetsMinimum(5, 0, 5)) {
412                                usingCursor = (this.serverStatus & 
413                                                SERVER_STATUS_CURSOR_EXISTS) != 0;
414                        }
415                        
416                        if (usingCursor) {
417                                RowData rows = new CursorRowProvider(
418                                        this,
419                                        prepStmt,
420                                        ((com.mysql.jdbc.ResultSetMetaData) prepStmt.getMetaData()).fields);
421 
422                                ResultSet rs = buildResultSetWithRows(
423                                        callingStatement,
424                                        catalog,
425                                        ((com.mysql.jdbc.ResultSetMetaData) prepStmt.getMetaData()).fields,
426                                        rows, resultSetType, resultSetConcurrency, isBinaryEncoded);
427                                
428                                if (usingCursor) {
429                                rs.setFetchSize(callingStatement.getFetchSize());
430                        }
431                                
432                                return rs;
433                        }
434                }
435                
436        RowData rowData = null;
437       
438        if (!streamResults) {
439            rowData = readSingleRowSet(columnCount, maxRows,
440                    resultSetConcurrency, isBinaryEncoded, fields);
441        } else {
442            rowData = new RowDataDynamic(this, (int) columnCount, fields,
443                    isBinaryEncoded);
444            this.streamingData = rowData;
445        }
446 
447        ResultSet rs = buildResultSetWithRows(callingStatement, catalog, fields,
448            rowData, resultSetType, resultSetConcurrency, isBinaryEncoded);
449 
450        
451        
452        return rs;
453    }
454 
455    /**
456     * Forcibly closes the underlying socket to MySQL.
457     */
458    protected final void forceClose() {
459        try {
460            if (this.mysqlInput != null) {
461                this.mysqlInput.close();
462            }
463        } catch (IOException ioEx) {
464            // we can't do anything constructive about this
465            // Let the JVM clean it up later
466            this.mysqlInput = null;
467        }
468 
469        try {
470            if (this.mysqlOutput != null) {
471                this.mysqlOutput.close();
472            }
473        } catch (IOException ioEx) {
474            // we can't do anything constructive about this
475            // Let the JVM clean it up later
476            this.mysqlOutput = null;
477        }
478 
479        try {
480            if (this.mysqlConnection != null) {
481                this.mysqlConnection.close();
482            }
483        } catch (IOException ioEx) {
484            // we can't do anything constructive about this
485            // Let the JVM clean it up later
486            this.mysqlConnection = null;
487        }
488    }
489 
490    /**
491     * Read one packet from the MySQL server
492     *
493     * @return the packet from the server.
494     *
495     * @throws SQLException DOCUMENT ME!
496     * @throws CommunicationsException DOCUMENT ME!
497     */
498    protected final Buffer readPacket() throws SQLException {
499        try {
500            
501            int lengthRead = readFully(this.mysqlInput,
502                    this.packetHeaderBuf, 0, 4);
503 
504            if (lengthRead < 4) {
505                forceClose();
506                throw new IOException(Messages.getString("MysqlIO.1")); //$NON-NLS-1$
507            }
508 
509            int packetLength = (this.packetHeaderBuf[0] & 0xff) +
510                ((this.packetHeaderBuf[1] & 0xff) << 8) +
511                ((this.packetHeaderBuf[2] & 0xff) << 16);
512 
513            if (this.traceProtocol) {
514                StringBuffer traceMessageBuf = new StringBuffer();
515 
516                traceMessageBuf.append(Messages.getString("MysqlIO.2")); //$NON-NLS-1$
517                traceMessageBuf.append(packetLength);
518                traceMessageBuf.append(Messages.getString("MysqlIO.3")); //$NON-NLS-1$
519                traceMessageBuf.append(StringUtils.dumpAsHex(
520                        this.packetHeaderBuf, 4));
521 
522                this.connection.getLog().logTrace(traceMessageBuf.toString());
523            }
524 
525            byte multiPacketSeq = this.packetHeaderBuf[3];
526 
527            if (!this.packetSequenceReset) {
528                if (this.enablePacketDebug && this.checkPacketSequence) {
529                    checkPacketSequencing(multiPacketSeq);
530                }
531            } else {
532                this.packetSequenceReset = false;
533            }
534 
535            this.readPacketSequence = multiPacketSeq;
536 
537            // Read data
538            byte[] buffer = new byte[packetLength + 1];
539            int numBytesRead = readFully(this.mysqlInput, buffer, 0,
540                    packetLength);
541 
542            if (numBytesRead != packetLength) {
543                throw new IOException("Short read, expected " +
544                    packetLength + " bytes, only read " + numBytesRead);
545            }
546 
547            buffer[packetLength] = 0;
548 
549            Buffer packet = new Buffer(buffer);
550            packet.setBufLength(packetLength + 1);
551 
552            if (this.traceProtocol) {
553                StringBuffer traceMessageBuf = new StringBuffer();
554 
555                traceMessageBuf.append(Messages.getString("MysqlIO.4")); //$NON-NLS-1$
556                traceMessageBuf.append(getPacketDumpToLog(packet,
557                        packetLength));
558 
559                this.connection.getLog().logTrace(traceMessageBuf.toString());
560            }
561 
562            if (this.enablePacketDebug) {
563                enqueuePacketForDebugging(false, false, 0,
564                    this.packetHeaderBuf, packet);
565            }
566 
567            return packet;
568        } catch (IOException ioEx) {
569            throw new CommunicationsException(this.connection,
570                this.lastPacketSentTimeMs, ioEx);
571        } catch (OutOfMemoryError oom) {
572                try {
573                            this.connection.realClose(false, false, true, oom);
574                    } finally {
575                            throw oom;
576                    }
577        }
578    }
579 
580    /**
581     * Unpacks the Field information from the given packet. Understands pre 4.1
582     * and post 4.1 server version field packet structures.
583     *
584     * @param packet the packet containing the field information
585     * @param extractDefaultValues should default values be extracted?
586     *
587     * @return the unpacked field
588     *
589     * @throws SQLException DOCUMENT ME!
590     */
591    protected final Field unpackField(Buffer packet,
592        boolean extractDefaultValues) throws SQLException {
593        if (this.use41Extensions) {
594            // we only store the position of the string and
595            // materialize only if needed...
596            if (this.has41NewNewProt) {
597                // Not used yet, 5.0?
598                int catalogNameStart = packet.getPosition() + 1;
599                int catalogNameLength = packet.fastSkipLenString();
600                catalogNameStart = adjustStartForFieldLength(catalogNameStart, catalogNameLength);
601            }
602 
603            int databaseNameStart = packet.getPosition() + 1;
604            int databaseNameLength = packet.fastSkipLenString();
605            databaseNameStart = adjustStartForFieldLength(databaseNameStart, databaseNameLength);
606 
607            int tableNameStart = packet.getPosition() + 1;
608            int tableNameLength = packet.fastSkipLenString();
609            tableNameStart = adjustStartForFieldLength(tableNameStart, tableNameLength);
610            
611            // orgTableName is never used so skip
612            int originalTableNameStart = packet.getPosition() + 1;
613            int originalTableNameLength = packet.fastSkipLenString();
614            originalTableNameStart = adjustStartForFieldLength(originalTableNameStart, originalTableNameLength);
615 
616            // we only store the position again...
617            int nameStart = packet.getPosition() + 1;
618            int nameLength = packet.fastSkipLenString();
619            
620            nameStart = adjustStartForFieldLength(nameStart, nameLength);
621 
622            // orgColName is not required so skip...
623            int originalColumnNameStart = packet.getPosition() + 1;
624            int originalColumnNameLength = packet.fastSkipLenString();
625            originalColumnNameStart = adjustStartForFieldLength(originalColumnNameStart, originalColumnNameLength);
626 
627            packet.readByte();
628 
629            short charSetNumber = (short) packet.readInt();
630 
631            long colLength = 0;
632 
633            if (this.has41NewNewProt) {
634                colLength = packet.readLong();
635            } else {
636                colLength = packet.readLongInt();
637            }
638 
639            int colType = packet.readByte() & 0xff;
640 
641            short colFlag = 0;
642 
643            if (this.hasLongColumnInfo) {
644                colFlag = (short) packet.readInt();
645            } else {
646                colFlag = (short) (packet.readByte() & 0xff);
647            }
648 
649            int colDecimals = packet.readByte() & 0xff;
650 
651            int defaultValueStart = -1;
652            int defaultValueLength = -1;
653 
654            if (extractDefaultValues) {
655                defaultValueStart = packet.getPosition() + 1;
656                defaultValueLength = packet.fastSkipLenString();
657            }
658 
659            Field field = new Field(this.connection, packet.getByteBuffer(),
660                    databaseNameStart, databaseNameLength, tableNameStart,
661                    tableNameLength, originalTableNameStart,
662                    originalTableNameLength, nameStart, nameLength,
663                    originalColumnNameStart, originalColumnNameLength,
664                    colLength, colType, colFlag, colDecimals,
665                    defaultValueStart, defaultValueLength, charSetNumber);
666 
667            return field;
668        }
669 
670        int tableNameStart = packet.getPosition() + 1;
671        int tableNameLength = packet.fastSkipLenString();
672        tableNameStart = adjustStartForFieldLength(tableNameStart, tableNameLength);
673        
674        int nameStart = packet.getPosition() + 1;
675        int nameLength = packet.fastSkipLenString();
676        nameStart = adjustStartForFieldLength(nameStart, nameLength);
677        
678        int colLength = packet.readnBytes();
679        int colType = packet.readnBytes();
680        packet.readByte(); // We know it's currently 2
681 
682        short colFlag = 0;
683 
684        if (this.hasLongColumnInfo) {
685            colFlag = (short) (packet.readInt());
686        } else {
687            colFlag = (short) (packet.readByte() & 0xff);
688        }
689 
690        int colDecimals = (packet.readByte() & 0xff);
691 
692        if (this.colDecimalNeedsBump) {
693            colDecimals++;
694        }
695 
696        Field field = new Field(this.connection, packet.getByteBuffer(),
697                nameStart, nameLength, tableNameStart, tableNameLength,
698                colLength, colType, colFlag, colDecimals);
699 
700        return field;
701    }
702 
703    private int adjustStartForFieldLength(int nameStart, int nameLength) {
704            if (nameLength < 251) {
705                    return nameStart;
706            }
707            
708                if (nameLength >= 251 && nameLength < 65536) {
709                        return nameStart + 2;
710                }
711                
712                if (nameLength >= 65536 && nameLength < 16777216) {
713                        return nameStart + 3;
714                }
715                
716                return nameStart + 8;
717        }
718 
719    protected boolean isSetNeededForAutoCommitMode(boolean autoCommitFlag) {
720        if (this.use41Extensions && this.connection.getElideSetAutoCommits()) {
721            boolean autoCommitModeOnServer = ((this.serverStatus &
722                SERVER_STATUS_AUTOCOMMIT) != 0);
723 
724            if (!autoCommitFlag) {
725                // Just to be safe, check if a transaction is in progress on the server....
726                // if so, then we must be in autoCommit == false
727                // therefore return the opposite of transaction status
728                boolean inTransactionOnServer = ((this.serverStatus &
729                    SERVER_STATUS_IN_TRANS) != 0);
730 
731                return !inTransactionOnServer;
732            }
733 
734            return !autoCommitModeOnServer;
735        }
736 
737        return true;
738    }
739 
740    /**
741     * Re-authenticates as the given user and password
742     *
743     * @param userName DOCUMENT ME!
744     * @param password DOCUMENT ME!
745     * @param database DOCUMENT ME!
746     *
747     * @throws SQLException DOCUMENT ME!
748     */
749    protected void changeUser(String userName, String password, String database)
750        throws SQLException {
751        this.packetSequence = -1;
752 
753        int passwordLength = 16;
754        int userLength = 0;
755 
756        if (userName != null) {
757            userLength = userName.length();
758        }
759 
760        int packLength = (userLength + passwordLength) + 7 + HEADER_LENGTH;
761 
762        if ((this.serverCapabilities & CLIENT_SECURE_CONNECTION) != 0) {
763            Buffer changeUserPacket = new Buffer(packLength + 1);
764            changeUserPacket.writeByte((byte) MysqlDefs.COM_CHANGE_USER);
765 
766            if (versionMeetsMinimum(4, 1, 1)) {
767                secureAuth411(changeUserPacket, packLength, userName, password,
768                    database, false);
769            } else {
770                secureAuth(changeUserPacket, packLength, userName, password,
771                    database, false);
772            }
773        } else {
774            // Passwords can be 16 chars long
775            Buffer packet = new Buffer(packLength);
776            packet.writeByte((byte) MysqlDefs.COM_CHANGE_USER);
777 
778            // User/Password data
779            packet.writeString(userName);
780 
781            if (this.protocolVersion > 9) {
782                packet.writeString(Util.newCrypt(password, this.seed));
783            } else {
784                packet.writeString(Util.oldCrypt(password, this.seed));
785            }
786 
787                        boolean localUseConnectWithDb = this.useConnectWithDb && 
788                                (database != null && database.length() > 0);
789                        
790            if (localUseConnectWithDb) {
791                packet.writeString(database);
792            }
793 
794            send(packet, packet.getPosition());
795            checkErrorPacket();
796                        
797                        if (!localUseConnectWithDb) {
798                                changeDatabaseTo(database);
799                        }
800        }
801    }
802 
803    /**
804     * Checks for errors in the reply packet, and if none, returns the reply
805     * packet, ready for reading
806     *
807     * @return a packet ready for reading.
808     *
809     * @throws SQLException is the packet is an error packet
810     */
811    protected Buffer checkErrorPacket() throws SQLException {
812        return checkErrorPacket(-1);
813    }
814 
815    /**
816     * Determines if the database charset is the same as the platform charset
817     */
818    protected void checkForCharsetMismatch() {
819        if (this.connection.getUseUnicode() &&
820                (this.connection.getEncoding() != null)) {
821            String encodingToCheck = jvmPlatformCharset;
822 
823            if (encodingToCheck == null) {
824                encodingToCheck = System.getProperty("file.encoding"); //$NON-NLS-1$
825            }
826 
827            if (encodingToCheck == null) {
828                this.platformDbCharsetMatches = false;
829            } else {
830                this.platformDbCharsetMatches = encodingToCheck.equals(this.connection.getEncoding());
831            }
832        }
833    }
834 
835    protected void clearInputStream() throws SQLException {
836    
837        try {
838            int len = this.mysqlInput.available();
839 
840            while (len > 0) {
841                this.mysqlInput.skip(len);
842                len = this.mysqlInput.available();
843            }
844        } catch (IOException ioEx) {
845            throw new CommunicationsException(this.connection,
846                this.lastPacketSentTimeMs, ioEx);
847        }
848    }
849 
850    protected void resetReadPacketSequence() {
851        this.readPacketSequence = 0;
852    }
853 
854    protected void dumpPacketRingBuffer() throws SQLException {
855        if ((this.packetDebugRingBuffer != null) &&
856                this.connection.getEnablePacketDebug()) {
857            StringBuffer dumpBuffer = new StringBuffer();
858 
859            dumpBuffer.append("Last " + this.packetDebugRingBuffer.size() +
860                " packets received from server, from oldest->newest:\n");
861            dumpBuffer.append("\n");
862 
863            for (Iterator ringBufIter = this.packetDebugRingBuffer.iterator();
864                    ringBufIter.hasNext();) {
865                dumpBuffer.append((StringBuffer) ringBufIter.next());
866                dumpBuffer.append("\n");
867            }
868 
869            this.connection.getLog().logTrace(dumpBuffer.toString());
870        }
871    }
872 
873    /**
874     * Runs an 'EXPLAIN' on the given query and dumps the results to  the log
875     *
876     * @param querySQL DOCUMENT ME!
877     * @param truncatedQuery DOCUMENT ME!
878     *
879     * @throws SQLException DOCUMENT ME!
880     */
881    protected void explainSlowQuery(byte[] querySQL, String truncatedQuery)
882        throws SQLException {
883        if (StringUtils.startsWithIgnoreCaseAndWs(truncatedQuery, "SELECT")) { //$NON-NLS-1$
884 
885            PreparedStatement stmt = null;
886            java.sql.ResultSet rs = null;
887 
888            try {
889                stmt = this.connection.clientPrepareStatement("EXPLAIN ?"); //$NON-NLS-1$
890                stmt.setBytesNoEscapeNoQuotes(1, querySQL);
891                rs = stmt.executeQuery();
892 
893                StringBuffer explainResults = new StringBuffer(Messages.getString(
894                            "MysqlIO.8") + truncatedQuery //$NON-NLS-1$
895                         +Messages.getString("MysqlIO.9")); //$NON-NLS-1$
896 
897                ResultSetUtil.appendResultSetSlashGStyle(explainResults, rs);
898 
899                this.connection.getLog().logWarn(explainResults.toString());
900            } catch (SQLException sqlEx) {
901            } finally {
902                if (rs != null) {
903                    rs.close();
904                }
905 
906                if (stmt != null) {
907                    stmt.close();
908                }
909            }
910        } else {
911        }
912    }
913 
914    static int getMaxBuf() {
915        return maxBufferSize;
916    }
917 
918    /**
919     * Get the major version of the MySQL server we are talking to.
920     *
921     * @return DOCUMENT ME!
922     */
923    final int getServerMajorVersion() {
924        return this.serverMajorVersion;
925    }
926 
927    /**
928     * Get the minor version of the MySQL server we are talking to.
929     *
930     * @return DOCUMENT ME!
931     */
932    final int getServerMinorVersion() {
933        return this.serverMinorVersion;
934    }
935 
936    /**
937     * Get the sub-minor version of the MySQL server we are talking to.
938     *
939     * @return DOCUMENT ME!
940     */
941    final int getServerSubMinorVersion() {
942        return this.serverSubMinorVersion;
943    }
944 
945    /**
946     * Get the version string of the server we are talking to
947     *
948     * @return DOCUMENT ME!
949     */
950    String getServerVersion() {
951        return this.serverVersion;
952    }
953 
954    /**
955     * Initialize communications with the MySQL server. Handles logging on, and
956     * handling initial connection errors.
957     *
958     * @param user DOCUMENT ME!
959     * @param password DOCUMENT ME!
960     * @param database DOCUMENT ME!
961     *
962     * @throws SQLException DOCUMENT ME!
963     * @throws CommunicationsException DOCUMENT ME!
964     */
965    void doHandshake(String user, String password, String database)
966        throws SQLException {
967        // Read the first packet
968        this.checkPacketSequence = false;
969        this.readPacketSequence = 0;
970 
971        Buffer buf = readPacket();
972 
973        // Get the protocol version
974        this.protocolVersion = buf.readByte();
975 
976        if (this.protocolVersion == -1) {
977            try {
978                this.mysqlConnection.close();
979            } catch (Exception e) {
980                ; // ignore
981            }
982 
983            int errno = 2000;
984 
985            errno = buf.readInt();
986 
987            String serverErrorMessage = buf.readString();
988 
989            StringBuffer errorBuf = new StringBuffer(Messages.getString(
990                        "MysqlIO.10")); //$NON-NLS-1$
991            errorBuf.append(serverErrorMessage);
992            errorBuf.append("\""); //$NON-NLS-1$
993 
994            String xOpen = SQLError.mysqlToSqlState(errno,
995                    this.connection.getUseSqlStateCodes());
996 
997            throw SQLError.createSQLException(SQLError.get(xOpen) + ", " //$NON-NLS-1$
998                 +errorBuf.toString(), xOpen, errno);
999        }
1000 
1001        this.serverVersion = buf.readString();
1002 
1003        // Parse the server version into major/minor/subminor
1004        int point = this.serverVersion.indexOf("."); //$NON-NLS-1$
1005 
1006        if (point != -1) {
1007            try {
1008                int n = Integer.parseInt(this.serverVersion.substring(0, point));
1009                this.serverMajorVersion = n;
1010            } catch (NumberFormatException NFE1) {
1011                ;
1012            }
1013 
1014            String remaining = this.serverVersion.substring(point + 1,
1015                    this.serverVersion.length());
1016            point = remaining.indexOf("."); //$NON-NLS-1$
1017 
1018            if (point != -1) {
1019                try {
1020                    int n = Integer.parseInt(remaining.substring(0, point));
1021                    this.serverMinorVersion = n;
1022                } catch (NumberFormatException nfe) {
1023                    ;
1024                }
1025 
1026                remaining = remaining.substring(point + 1, remaining.length());
1027 
1028                int pos = 0;
1029 
1030                while (pos < remaining.length()) {
1031                    if ((remaining.charAt(pos) < '0') ||
1032                            (remaining.charAt(pos) > '9')) {
1033                        break;
1034                    }
1035 
1036                    pos++;
1037                }
1038 
1039                try {
1040                    int n = Integer.parseInt(remaining.substring(0, pos));
1041                    this.serverSubMinorVersion = n;
1042                } catch (NumberFormatException nfe) {
1043                    ;
1044                }
1045            }
1046        }
1047 
1048        if (versionMeetsMinimum(4, 0, 8)) {
1049            this.maxThreeBytes = (256 * 256 * 256) - 1;
1050            this.useNewLargePackets = true;
1051        } else {
1052            this.maxThreeBytes = 255 * 255 * 255;
1053            this.useNewLargePackets = false;
1054        }
1055 
1056        this.colDecimalNeedsBump = versionMeetsMinimum(3, 23, 0);
1057        this.colDecimalNeedsBump = !versionMeetsMinimum(3, 23, 15); // guess? Not noted in changelog
1058        this.useNewUpdateCounts = versionMeetsMinimum(3, 22, 5);
1059 
1060        threadId = buf.readLong(); 
1061        this.seed = buf.readString();
1062 
1063        this.serverCapabilities = 0;
1064 
1065        if (buf.getPosition() < buf.getBufLength()) {
1066            this.serverCapabilities = buf.readInt();
1067        }
1068 
1069        if (versionMeetsMinimum(4, 1, 1)) {
1070            int position = buf.getPosition();
1071 
1072            /* New protocol with 16 bytes to describe server characteristics */
1073            this.serverCharsetIndex = buf.readByte() & 0xff;
1074            this.serverStatus = buf.readInt();
1075            buf.setPosition(position + 16);
1076 
1077            String seedPart2 = buf.readString();
1078            StringBuffer newSeed = new StringBuffer(20);
1079            newSeed.append(this.seed);
1080            newSeed.append(seedPart2);
1081            this.seed = newSeed.toString();
1082        }
1083 
1084        if (((this.serverCapabilities & CLIENT_COMPRESS) != 0) &&
1085                this.connection.getUseCompression()) {
1086            this.clientParam |= CLIENT_COMPRESS;
1087        }
1088 
1089                this.useConnectWithDb = (database != null) && 
1090                        (database.length() > 0) &&
1091                        !this.connection.getCreateDatabaseIfNotExist();
1092                
1093        if (this.useConnectWithDb) {
1094            this.clientParam |= CLIENT_CONNECT_WITH_DB;
1095        }
1096 
1097        if (((this.serverCapabilities & CLIENT_SSL) == 0) &&
1098                this.connection.getUseSSL()) {
1099            if (this.connection.getRequireSSL()) {
1100                this.connection.close();
1101                forceClose();
1102                throw SQLError.createSQLException(Messages.getString("MysqlIO.15"), //$NON-NLS-1$
1103                    SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE);
1104            }
1105 
1106            this.connection.setUseSSL(false);
1107        }
1108 
1109        if ((this.serverCapabilities & CLIENT_LONG_FLAG) != 0) {
1110            // We understand other column flags, as well
1111            this.clientParam |= CLIENT_LONG_FLAG;
1112            this.hasLongColumnInfo = true;
1113        }
1114 
1115        // return FOUND rows
1116        this.clientParam |= CLIENT_FOUND_ROWS;
1117 
1118        if (this.connection.getAllowLoadLocalInfile()) {
1119            this.clientParam |= CLIENT_LOCAL_FILES;
1120        }
1121 
1122        if (this.isInteractiveClient) {
1123            this.clientParam |= CLIENT_INTERACTIVE;
1124        }
1125 
1126        // Authenticate
1127        if (this.protocolVersion > 9) {
1128            this.clientParam |= CLIENT_LONG_PASSWORD; // for long passwords
1129        } else {
1130            this.clientParam &= ~CLIENT_LONG_PASSWORD;
1131        }
1132 
1133        //
1134        // 4.1 has some differences in the protocol
1135        //
1136        if (versionMeetsMinimum(4, 1, 0)) {
1137            if (versionMeetsMinimum(4, 1, 1)) {
1138                this.clientParam |= CLIENT_PROTOCOL_41;
1139                this.has41NewNewProt = true;
1140 
1141                // Need this to get server status values
1142                this.clientParam |= CLIENT_TRANSACTIONS;
1143 
1144                // We always allow multiple result sets
1145                this.clientParam |= CLIENT_MULTI_RESULTS;
1146 
1147                // We allow the user to configure whether
1148                // or not they want to support multiple queries
1149                // (by default, this is disabled).
1150                if (this.connection.getAllowMultiQueries()) {
1151                    this.clientParam |= CLIENT_MULTI_QUERIES;
1152                }
1153            } else {
1154                this.clientParam |= CLIENT_RESERVED;
1155                this.has41NewNewProt = false;
1156            }
1157 
1158            this.use41Extensions = true;
1159        }
1160 
1161        int passwordLength = 16;
1162        int userLength = 0;
1163        int databaseLength = 0;
1164 
1165        if (user != null) {
1166            userLength = user.length();
1167        }
1168 
1169        if (database != null) {
1170            databaseLength = database.length();
1171        }
1172 
1173        int packLength = (userLength + passwordLength + databaseLength) + 7 +
1174            HEADER_LENGTH;
1175        Buffer packet = null;
1176 
1177        if (!this.connection.getUseSSL()) {
1178            if ((this.serverCapabilities & CLIENT_SECURE_CONNECTION) != 0) {
1179                this.clientParam |= CLIENT_SECURE_CONNECTION;
1180 
1181                if (versionMeetsMinimum(4, 1, 1)) {
1182                    secureAuth411(null, packLength, user, password, database,
1183                        true);
1184                } else {
1185                    secureAuth(null, packLength, user, password, database, true);
1186                }
1187            } else {
1188                // Passwords can be 16 chars long
1189                packet = new Buffer(packLength);
1190 
1191                if ((this.clientParam & CLIENT_RESERVED) != 0) {
1192                    if (versionMeetsMinimum(4, 1, 1)) {
1193                        packet.writeLong(this.clientParam);
1194                        packet.writeLong(this.maxThreeBytes);
1195 
1196                        // charset, JDBC will connect as 'latin1',
1197                        // and use 'SET NAMES' to change to the desired
1198                        // charset after the connection is established.
1199                        packet.writeByte((byte) 8);
1200 
1201                        // Set of bytes reserved for future use.
1202                        packet.writeBytesNoNull(new byte[23]);
1203                    } else {
1204                        packet.writeLong(this.clientParam);
1205                        packet.writeLong(this.maxThreeBytes);
1206                    }
1207                } else {
1208                    packet.writeInt((int) this.clientParam);
1209                    packet.writeLongInt(this.maxThreeBytes);
1210                }
1211 
1212                // User/Password data
1213                packet.writeString(user, "Cp1252", this.connection);
1214 
1215                if (this.protocolVersion > 9) {
1216                    packet.writeString(Util.newCrypt(password, this.seed), "Cp1252", this.connection);
1217                } else {
1218                    packet.writeString(Util.oldCrypt(password, this.seed), "Cp1252", this.connection);
1219                }
1220 
1221                if (this.useConnectWithDb) {
1222                    packet.writeString(database, "Cp1252", this.connection);
1223                }
1224 
1225                send(packet, packet.getPosition());
1226            }
1227        } else {
1228            negotiateSSLConnection(user, password, database, packLength);
1229        }
1230 
1231        // Check for errors, not for 4.1.1 or newer,
1232        // as the new auth protocol doesn't work that way
1233        // (see secureAuth411() for more details...)
1234        if (!versionMeetsMinimum(4, 1, 1)) {
1235            checkErrorPacket();
1236        }
1237 
1238        //
1239        // Can't enable compression until after handshake
1240        //
1241        if (((this.serverCapabilities & CLIENT_COMPRESS) != 0) &&
1242                this.connection.getUseCompression()) {
1243            // The following matches with ZLIB's
1244            // compress()
1245            this.deflater = new Deflater();
1246            this.useCompression = true;
1247            this.mysqlInput = new CompressedInputStream(this.connection,
1248                    this.mysqlInput);
1249        }
1250 
1251        if (!this.useConnectWithDb) {
1252            changeDatabaseTo(database);
1253        }
1254    }
1255 
1256        private void changeDatabaseTo(String database) throws SQLException, CommunicationsException {
1257                if (database == null || database.length() == 0) {
1258                        return;
1259                }
1260                
1261                try {
1262                    sendCommand(MysqlDefs.INIT_DB, database, null, false, null);
1263                } catch (Exception ex) {
1264                        if (this.connection.getCreateDatabaseIfNotExist()) {
1265                                sendCommand(MysqlDefs.QUERY, "CREATE DATABASE IF NOT EXISTS " +
1266                                        database,
1267                                        null, false, null);
1268                                sendCommand(MysqlDefs.INIT_DB, database, null, false, null);
1269                        } else {
1270                                throw new CommunicationsException(this.connection,
1271                                                this.lastPacketSentTimeMs, ex);
1272                        }
1273                }
1274        }
1275 
1276    /**
1277    * Retrieve one row from the MySQL server. Note: this method is not
1278    * thread-safe, but it is only called from methods that are guarded by
1279    * synchronizing on this object.
1280    *
1281    * @param fields DOCUMENT ME!
1282    * @param columnCount DOCUMENT ME!
1283    * @param isBinaryEncoded DOCUMENT ME!
1284    * @param resultSetConcurrency DOCUMENT ME!
1285    *
1286    * @return DOCUMENT ME!
1287    *
1288    * @throws SQLException DOCUMENT ME!
1289    */
1290    final Object[] nextRow(Field[] fields, int columnCount,
1291        boolean isBinaryEncoded, int resultSetConcurrency)
1292        throws SQLException {
1293        // Get the next incoming packet, re-using the packet because
1294        // all the data we need gets copied out of it.
1295        Buffer rowPacket = checkErrorPacket();
1296 
1297        if (!isBinaryEncoded) {
1298            //
1299            // Didn't read an error, so re-position to beginning
1300            // of packet in order to read result set data
1301            //
1302            rowPacket.setPosition(rowPacket.getPosition() - 1);
1303 
1304            if (!rowPacket.isLastDataPacket()) {
1305                byte[][] rowData = new byte[columnCount][];
1306 
1307                int offset = 0;
1308 
1309                for (int i = 0; i < columnCount; i++) {
1310                    rowData[i] = rowPacket.readLenByteArray(offset);
1311                }
1312 
1313                return rowData;
1314            }
1315 
1316            readServerStatusForResultSets(rowPacket);
1317 
1318            return null;
1319        }
1320 
1321        // 
1322        // Handle binary-encoded data for server-side   
1323        // PreparedStatements...
1324        //
1325        if (!rowPacket.isLastDataPacket()) {
1326            return unpackBinaryResultSetRow(fields, rowPacket,
1327                resultSetConcurrency);
1328        }
1329 
1330        rowPacket.setPosition(rowPacket.getPosition() - 1);
1331        readServerStatusForResultSets(rowPacket);
1332 
1333        return null;
1334    }
1335 
1336    /**
1337     * Log-off of the MySQL server and close the socket.
1338     *
1339     * @throws SQLException DOCUMENT ME!
1340     */
1341    final void quit() throws SQLException {
1342        Buffer packet = new Buffer(6);
1343        this.packetSequence = -1;
1344        packet.writeByte((byte) MysqlDefs.QUIT);
1345        send(packet, packet.getPosition());
1346        forceClose();
1347    }
1348 
1349    /**
1350     * Returns the packet used for sending data (used by PreparedStatement)
1351     * Guarded by external synchronization on a mutex.
1352     *
1353     * @return A packet to send data with
1354     */
1355    Buffer getSharedSendPacket() {
1356        if (this.sharedSendPacket == null) {
1357                this.sharedSendPacket = new Buffer(
1358                                this.connection.getNetBufferLength());
1359        }
1360 
1361        return this.sharedSendPacket;
1362    }
1363 
1364    void closeStreamer(RowData streamer) throws SQLException {
1365        if (this.streamingData == null) {
1366            throw SQLError.createSQLException(Messages.getString("MysqlIO.17") //$NON-NLS-1$
1367                 +streamer + Messages.getString("MysqlIO.18")); //$NON-NLS-1$
1368        }
1369 
1370        if (streamer != this.streamingData) {
1371            throw SQLError.createSQLException(Messages.getString("MysqlIO.19") //$NON-NLS-1$
1372                 +streamer + Messages.getString("MysqlIO.20") //$NON-NLS-1$
1373                 +Messages.getString("MysqlIO.21") //$NON-NLS-1$
1374                 +Messages.getString("MysqlIO.22")); //$NON-NLS-1$
1375        }
1376 
1377        this.streamingData = null;
1378    }
1379 
1380    ResultSet readAllResults(Statement callingStatement, int maxRows,
1381        int resultSetType, int resultSetConcurrency, boolean streamResults,
1382        String catalog, Buffer resultPacket, boolean isBinaryEncoded,
1383        long preSentColumnCount, boolean unpackFieldInfo)
1384        throws SQLException {
1385        resultPacket.setPosition(resultPacket.getPosition() - 1);
1386 
1387        ResultSet topLevelResultSet = readResultsForQueryOrUpdate(callingStatement,
1388                maxRows, resultSetType, resultSetConcurrency, streamResults,
1389                catalog, resultPacket, isBinaryEncoded, preSentColumnCount,
1390                unpackFieldInfo);
1391 
1392        ResultSet currentResultSet = topLevelResultSet;
1393 
1394        boolean checkForMoreResults = ((this.clientParam &
1395            CLIENT_MULTI_RESULTS) != 0);
1396 
1397        boolean serverHasMoreResults = (this.serverStatus &
1398            SERVER_MORE_RESULTS_EXISTS) != 0;
1399 
1400        //
1401        // TODO: We need to support streaming of multiple result sets
1402        //
1403        if (serverHasMoreResults && streamResults) {
1404            clearInputStream();
1405 
1406            throw SQLError.createSQLException(Messages.getString("MysqlIO.23"), //$NON-NLS-1$
1407                SQLError.SQL_STATE_DRIVER_NOT_CAPABLE);
1408        }
1409 
1410        boolean moreRowSetsExist = checkForMoreResults & serverHasMoreResults;
1411 
1412        while (moreRowSetsExist) {
1413                Buffer fieldPacket = checkErrorPacket();
1414            fieldPacket.setPosition(0);
1415            
1416            if ((fieldPacket.readByte(0) == 0) &&
1417                    (fieldPacket.readByte(1) == 0) &&
1418                    (fieldPacket.readByte(2) == 0)) {
1419                break;
1420            }
1421 
1422            ResultSet newResultSet = readResultsForQueryOrUpdate(callingStatement,
1423                    maxRows, resultSetType, resultSetConcurrency,
1424                    streamResults, catalog, fieldPacket, isBinaryEncoded,
1425                    preSentColumnCount, unpackFieldInfo);
1426 
1427            currentResultSet.setNextResultSet(newResultSet);
1428 
1429            currentResultSet = newResultSet;
1430 
1431            moreRowSetsExist = (this.serverStatus & SERVER_MORE_RESULTS_EXISTS) != 0;
1432        }
1433 
1434        if (!streamResults) {
1435            clearInputStream();
1436        }
1437 
1438        reclaimLargeReusablePacket();
1439 
1440        return topLevelResultSet;
1441    }
1442 
1443    /**
1444     * Sets the buffer size to max-buf
1445     */
1446    void resetMaxBuf() {
1447        this.maxAllowedPacket = this.connection.getMaxAllowedPacket();
1448    }
1449 
1450    /**
1451     * Send a command to the MySQL server If data is to be sent with command,
1452     * it should be put in extraData.
1453     *
1454     * Raw packets can be sent by setting queryPacket to something other
1455     * than null.
1456     *
1457     * @param command the MySQL protocol 'command' from MysqlDefs
1458     * @param extraData any 'string' data for the command
1459     * @param queryPacket a packet pre-loaded with data for the protocol (i.e.
1460     * from a client-side prepared statement).
1461     * @param skipCheck do not call checkErrorPacket() if true
1462     * @param extraDataCharEncoding the character encoding of the extraData
1463     * parameter.
1464     *
1465     * @return the response packet from the server
1466     *
1467     * @throws SQLException if an I/O error or SQL error occurs
1468     */
1469   
1470    final Buffer sendCommand(int command, String extraData, Buffer queryPacket,
1471        boolean skipCheck, String extraDataCharEncoding)
1472        throws SQLException {
1473        //
1474        // We cache these locally, per-command, as the checks
1475        // for them are in very 'hot' sections of the I/O code
1476        // and we save 10-15% in overall performance by doing this...
1477        //
1478        this.enablePacketDebug = this.connection.getEnablePacketDebug();
1479        this.traceProtocol = this.connection.getTraceProtocol();
1480        this.readPacketSequence = 0;
1481 
1482        try {
1483                
1484            checkForOutstandingStreamingData();
1485           
1486            // Clear serverStatus...this value is guarded by an
1487            // external mutex, as you can only ever be processing 
1488            // one command at a time
1489            this.serverStatus = 0;
1490            this.hadWarnings = false;
1491            this.warningCount = 0;
1492 
1493            this.queryNoIndexUsed = false;
1494            this.queryBadIndexUsed = false;
1495 
1496            //
1497            // Compressed input stream needs cleared at beginning
1498            // of each command execution...
1499            //
1500            if (this.useCompression) {
1501                int bytesLeft = this.mysqlInput.available();
1502 
1503                if (bytesLeft > 0) {
1504                    this.mysqlInput.skip(bytesLeft);
1505                }
1506            }
1507 
1508            try {
1509                clearInputStream();
1510 
1511                //
1512                // PreparedStatements construct their own packets,
1513                // for efficiency's sake.
1514                //
1515                // If this is a generic query, we need to re-use
1516                // the sending packet.
1517                //
1518                if (queryPacket == null) {
1519                    int packLength = HEADER_LENGTH + COMP_HEADER_LENGTH + 1 +
1520                        ((extraData != null) ? extraData.length() : 0) + 2;
1521 
1522                    if (this.sendPacket == null) {
1523                        this.sendPacket = new Buffer(packLength);
1524                    }
1525 
1526                    this.packetSequence = -1;
1527                    this.readPacketSequence = 0;
1528                    this.checkPacketSequence = true;
1529                    this.sendPacket.clear();
1530 
1531                    this.sendPacket.writeByte((byte) command);
1532 
1533                    if ((command == MysqlDefs.INIT_DB) ||
1534                            (command == MysqlDefs.CREATE_DB) ||
1535                            (command == MysqlDefs.DROP_DB) ||
1536                            (command == MysqlDefs.QUERY) ||
1537                            (command == MysqlDefs.COM_PREPARE)) {
1538                        if (extraDataCharEncoding == null) {
1539                            this.sendPacket.writeStringNoNull(extraData);
1540                        } else {
1541                            this.sendPacket.writeStringNoNull(extraData,
1542                                extraDataCharEncoding,
1543                                this.connection.getServerCharacterEncoding(),
1544                                this.connection.parserKnowsUnicode(), this.connection);
1545                        }
1546                    } else if (command == MysqlDefs.PROCESS_KILL) {
1547                        long id = new Long(extraData).longValue();
1548                        this.sendPacket.writeLong(id);
1549                    }
1550 
1551                    send(this.sendPacket, this.sendPacket.getPosition());
1552                } else {
1553                    this.packetSequence = -1;
1554                    send(queryPacket, queryPacket.getPosition()); // packet passed by PreparedStatement
1555                }
1556            } catch (SQLException sqlEx) {
1557                // don't wrap SQLExceptions
1558                throw sqlEx;
1559            } catch (Exception ex) {
1560                throw new CommunicationsException(this.connection,
1561                    this.lastPacketSentTimeMs, ex);
1562            }
1563 
1564            Buffer returnPacket = null;
1565 
1566            if (!skipCheck) {
1567                if ((command == MysqlDefs.COM_EXECUTE) ||
1568                        (command == MysqlDefs.COM_RESET_STMT)) {
1569                    this.readPacketSequence = 0;
1570                    this.packetSequenceReset = true;
1571                }
1572 
1573                returnPacket = checkErrorPacket(command);
1574            }
1575 
1576            return returnPacket;
1577        } catch (IOException ioEx) {
1578            throw new CommunicationsException(this.connection,
1579                this.lastPacketSentTimeMs, ioEx);
1580        }
1581    }
1582 
1583    /**
1584     * Send a query stored in a packet directly to the server.
1585     *
1586     * @param callingStatement DOCUMENT ME!
1587     * @param resultSetConcurrency DOCUMENT ME!
1588     * @param characterEncoding DOCUMENT ME!
1589     * @param queryPacket DOCUMENT ME!
1590     * @param maxRows DOCUMENT ME!
1591     * @param conn DOCUMENT ME!
1592     * @param resultSetType DOCUMENT ME!
1593     * @param resultSetConcurrency DOCUMENT ME!
1594     * @param streamResults DOCUMENT ME!
1595     * @param catalog DOCUMENT ME!
1596     * @param unpackFieldInfo should we read MYSQL_FIELD info (if available)?
1597     *
1598     * @return DOCUMENT ME!
1599     *
1600     * @throws Exception DOCUMENT ME!
1601     */
1602    final ResultSet sqlQueryDirect(Statement callingStatement, String query,
1603        String characterEncoding, Buffer queryPacket, int maxRows,
1604        Connection conn, int resultSetType, int resultSetConcurrency,
1605        boolean streamResults, String catalog, boolean unpackFieldInfo)
1606        throws Exception {
1607        long queryStartTime = 0;
1608        long queryEndTime = 0;
1609 
1610        if (query != null) {
1611                
1612                
1613            // We don't know exactly how many bytes we're going to get
1614            // from the query. Since we're dealing with Unicode, the
1615            // max is 2, so pad it (2 * query) + space for headers
1616            int packLength = HEADER_LENGTH + 1 + (query.length() * 2) + 2;
1617 
1618            if (this.sendPacket == null) {
1619                    this.sendPacket = new Buffer(packLength);
1620            } else {
1621                this.sendPacket.clear();
1622            }
1623 
1624            this.sendPacket.writeByte((byte) MysqlDefs.QUERY);
1625 
1626            if (characterEncoding != null) {
1627                if (this.platformDbCharsetMatches) {
1628                    this.sendPacket.writeStringNoNull(query, characterEncoding,
1629                        this.connection.getServerCharacterEncoding(),
1630                        this.connection.parserKnowsUnicode(),
1631                        this.connection);
1632                } else {
1633                    if (StringUtils.startsWithIgnoreCaseAndWs(query, "LOAD DATA")) { //$NON-NLS-1$
1634                        this.sendPacket.writeBytesNoNull(query.getBytes());
1635                    } else {
1636                        this.sendPacket.writeStringNoNull(query,
1637                            characterEncoding,
1638                            this.connection.getServerCharacterEncoding(),
1639                            this.connection.parserKnowsUnicode(),
1640                            this.connection);
1641                    }
1642                }
1643            } else {
1644                this.sendPacket.writeStringNoNull(query);
1645            }
1646 
1647            queryPacket = this.sendPacket;
1648        }
1649 
1650        byte[] queryBuf = null;
1651        int oldPacketPosition = 0;
1652 
1653        
1654        
1655        if (needToGrabQueryFromPacket) {
1656            queryBuf = queryPacket.getByteBuffer();
1657 
1658            // save the packet position
1659            oldPacketPosition = queryPacket.getPosition();
1660 
1661            queryStartTime = System.currentTimeMillis();
1662        }
1663 
1664        // Send query command and sql query string
1665        Buffer resultPacket = sendCommand(MysqlDefs.QUERY, null, queryPacket,
1666                false, null);
1667 
1668        long fetchBeginTime = 0;
1669        long fetchEndTime = 0;
1670 
1671        String profileQueryToLog = null;
1672 
1673        boolean queryWasSlow = false;
1674 
1675        if (this.profileSql || this.logSlowQueries) {
1676            queryEndTime = System.currentTimeMillis();
1677 
1678            boolean shouldExtractQuery = false;
1679 
1680            if (this.profileSql) {
1681                shouldExtractQuery = true;
1682            } else if (this.logSlowQueries &&
1683                    ((queryEndTime - queryStartTime) > this.connection.getSlowQueryThresholdMillis())) {
1684                shouldExtractQuery = true;
1685                queryWasSlow = true;
1686            }
1687 
1688            if (shouldExtractQuery) {
1689                // Extract the actual query from the network packet 
1690                boolean truncated = false;
1691 
1692                int extractPosition = oldPacketPosition;
1693 
1694                if (oldPacketPosition > this.connection.getMaxQuerySizeToLog()) {
1695                    extractPosition = this.connection.getMaxQuerySizeToLog() + 5;
1696                    truncated = true;
1697                }
1698 
1699                profileQueryToLog = new String(queryBuf, 5,
1700                        (extractPosition - 5));
1701 
1702                if (truncated) {
1703                    profileQueryToLog += Messages.getString("MysqlIO.25"); //$NON-NLS-1$
1704                }
1705            }
1706 
1707            fetchBeginTime = queryEndTime;
1708        }
1709        
1710        if (this.autoGenerateTestcaseScript) {
1711                String testcaseQuery = null;
1712                
1713                if (query != null) {
1714                        testcaseQuery = query;
1715                } else {
1716                        testcaseQuery = new String(queryBuf, 5,
1717                        (oldPacketPosition - 5));
1718                }
1719                
1720                    StringBuffer debugBuf = new StringBuffer(testcaseQuery.length() + 32);
1721                    this.connection.generateConnectionCommentBlock(debugBuf);
1722                    debugBuf.append(testcaseQuery);
1723                    debugBuf.append(';');
1724                    this.connection.dumpTestcaseQuery(debugBuf.toString());
1725            }
1726        
1727        ResultSet rs = readAllResults(callingStatement, maxRows, resultSetType,
1728                resultSetConcurrency, streamResults, catalog, resultPacket,
1729                false, -1L, unpackFieldInfo);
1730 
1731        if (queryWasSlow) {
1732            StringBuffer mesgBuf = new StringBuffer(48 +
1733                    profileQueryToLog.length());
1734            mesgBuf.append(Messages.getString("MysqlIO.26")); //$NON-NLS-1$
1735            mesgBuf.append(this.connection.getSlowQueryThresholdMillis());
1736            mesgBuf.append(Messages.getString("MysqlIO.26a"));
1737            mesgBuf.append((queryEndTime - queryStartTime));
1738            mesgBuf.append(Messages.getString("MysqlIO.27")); //$NON-NLS-1$
1739            mesgBuf.append(profileQueryToLog);
1740 
1741            ProfileEventSink eventSink = ProfileEventSink.getInstance(this.connection);
1742 
1743            eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_QUERY,
1744                    "", catalog, this.connection.getId(), //$NON-NLS-1$
1745                    (callingStatement != null) ? callingStatement.getId() : 999,
1746                    rs.resultId, System.currentTimeMillis(),
1747                    (int) (queryEndTime - queryStartTime), null,
1748                    new Throwable(), profileQueryToLog));
1749            
1750            //this.connection.getLog().logWarn(mesgBuf.toString());
1751 
1752            if (this.connection.getExplainSlowQueries()) {
1753                if (oldPacketPosition < MAX_QUERY_SIZE_TO_EXPLAIN) {
1754                    explainSlowQuery(queryPacket.getBytes(5,
1755                            (oldPacketPosition - 5)), profileQueryToLog);
1756                } else {
1757                    this.connection.getLog().logWarn(Messages.getString(
1758                            "MysqlIO.28") //$NON-NLS-1$
1759                         +MAX_QUERY_SIZE_TO_EXPLAIN +
1760                        Messages.getString("MysqlIO.29")); //$NON-NLS-1$
1761                }
1762            }
1763        }
1764 
1765        if (this.profileSql) {
1766            fetchEndTime = System.currentTimeMillis();
1767 
1768            ProfileEventSink eventSink = ProfileEventSink.getInstance(this.connection);
1769 
1770            eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_QUERY,
1771                    "", catalog, this.connection.getId(), //$NON-NLS-1$
1772                    (callingStatement != null) ? callingStatement.getId() : 999,
1773                    rs.resultId, System.currentTimeMillis(),
1774                    (int) (queryEndTime - queryStartTime), null,
1775                    new Throwable(), profileQueryToLog));
1776 
1777            eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_FETCH,
1778                    "", catalog, this.connection.getId(), //$NON-NLS-1$
1779                    (callingStatement != null) ? callingStatement.getId() : 999,
1780                    rs.resultId, System.currentTimeMillis(),
1781                    (int) (fetchEndTime - fetchBeginTime), null,
1782                    new Throwable(), null));
1783 
1784            if (this.queryBadIndexUsed) {
1785                eventSink.consumeEvent(new ProfilerEvent(
1786                        ProfilerEvent.TYPE_WARN, "", catalog, //$NON-NLS-1$
1787                        this.connection.getId(),
1788                        (callingStatement != null) ? callingStatement.getId()
1789                                                   : 999, rs.resultId,
1790                        System.currentTimeMillis(),
1791                        (int) (queryEndTime - queryStartTime), null,
1792                        new Throwable(),
1793                        Messages.getString("MysqlIO.33") //$NON-NLS-1$
1794                         +profileQueryToLog));
1795            }
1796 
1797            if (this.queryNoIndexUsed) {
1798                eventSink.consumeEvent(new ProfilerEvent(
1799                        ProfilerEvent.TYPE_WARN, "", catalog, //$NON-NLS-1$
1800                        this.connection.getId(),
1801                        (callingStatement != null) ? callingStatement.getId()
1802                                                   : 999, rs.resultId,
1803                        System.currentTimeMillis(),
1804                        (int) (queryEndTime - queryStartTime), null,
1805                        new Throwable(),
1806                        Messages.getString("MysqlIO.35") //$NON-NLS-1$
1807                         +profileQueryToLog));
1808            }
1809        }
1810 
1811        if (this.hadWarnings) {
1812            scanForAndThrowDataTruncation();
1813        }
1814 
1815        return rs;
1816    }
1817 
1818    /**
1819     * Returns the host this IO is connected to
1820     *
1821     * @return DOCUMENT ME!
1822     */
1823    String getHost() {
1824        return this.host;
1825    }
1826 
1827    /**
1828     * Is the version of the MySQL server we are connected to the given
1829     * version?
1830     *
1831     * @param major the major version
1832     * @param minor the minor version
1833     * @param subminor the subminor version
1834     *
1835     * @return true if the version of the MySQL server we are connected  is the
1836     *         given version
1837     */
1838    boolean isVersion(int major, int minor, int subminor) {
1839        return ((major == getServerMajorVersion()) &&
1840        (minor == getServerMinorVersion()) &&
1841        (subminor == getServerSubMinorVersion()));
1842    }
1843 
1844    /**
1845     * Does the version of the MySQL server we are connected to meet the given
1846     * minimums?
1847     *
1848     * @param major DOCUMENT ME!
1849     * @param minor DOCUMENT ME!
1850     * @param subminor DOCUMENT ME!
1851     *
1852     * @return DOCUMENT ME!
1853     */
1854    boolean versionMeetsMinimum(int major, int minor, int subminor) {
1855        if (getServerMajorVersion() >= major) {
1856            if (getServerMajorVersion() == major) {
1857                if (getServerMinorVersion() >= minor) {
1858                    if (getServerMinorVersion() == minor) {
1859                        return (getServerSubMinorVersion() >= subminor);
1860                    }
1861 
1862                    // newer than major.minor
1863                    return true;
1864                }
1865 
1866                // older than major.minor
1867                return false;
1868            }
1869 
1870            // newer than major  
1871            return true;
1872        }
1873 
1874        return false;
1875    }
1876 
1877    /**
1878     * Returns the hex dump of the given packet, truncated to
1879     * MAX_PACKET_DUMP_LENGTH if packetLength exceeds that value.
1880     *
1881     * @param packetToDump the packet to dump in hex
1882     * @param packetLength the number of bytes to dump
1883     *
1884     * @return the hex dump of the given packet
1885     */
1886    private final static String getPacketDumpToLog(Buffer packetToDump,
1887        int packetLength) {
1888        if (packetLength < MAX_PACKET_DUMP_LENGTH) {
1889            return packetToDump.dump(packetLength);
1890        }
1891 
1892        StringBuffer packetDumpBuf = new StringBuffer(MAX_PACKET_DUMP_LENGTH * 4);
1893        packetDumpBuf.append(packetToDump.dump(MAX_PACKET_DUMP_LENGTH));
1894        packetDumpBuf.append(Messages.getString("MysqlIO.36")); //$NON-NLS-1$
1895        packetDumpBuf.append(MAX_PACKET_DUMP_LENGTH);
1896        packetDumpBuf.append(Messages.getString("MysqlIO.37")); //$NON-NLS-1$
1897 
1898        return packetDumpBuf.toString();
1899    }
1900 
1901    private final int readFully(InputStream in, byte[] b, int off, int len)
1902        throws IOException {
1903        if (len < 0) {
1904            throw new IndexOutOfBoundsException();
1905        }
1906 
1907        int n = 0;
1908 
1909        while (n < len) {
1910            int count = in.read(b, off + n, len - n);
1911 
1912            if (count < 0) {
1913                throw new EOFException();
1914            }
1915 
1916            n += count;
1917        }
1918 
1919        return n;
1920    }
1921 
1922    /**
1923     * Reads one result set off of the wire, if the result is actually an
1924     * update count, creates an update-count only result set.
1925     *
1926     * @param callingStatement DOCUMENT ME!
1927     * @param maxRows the maximum rows to return in the result set.
1928     * @param resultSetType scrollability
1929     * @param resultSetConcurrency updatability
1930     * @param streamResults should the driver leave the results on the wire,
1931     *        and read them only when needed?
1932     * @param catalog the catalog in use
1933     * @param resultPacket the first packet of information in the result set
1934     * @param isBinaryEncoded is this result set from a prepared statement?
1935     * @param preSentColumnCount do we already know the number of columns?
1936     * @param unpackFieldInfo should we unpack the field information?
1937     *
1938     * @return a result set that either represents the rows, or an update count
1939     *
1940     * @throws SQLException if an error occurs while reading the rows
1941     */
1942    private final ResultSet readResultsForQueryOrUpdate(
1943        Statement callingStatement, int maxRows, int resultSetType,
1944        int resultSetConcurrency, boolean streamResults, String catalog,
1945        Buffer resultPacket, boolean isBinaryEncoded, long preSentColumnCount,
1946        boolean unpackFieldInfo) throws SQLException {
1947        long columnCount = resultPacket.readFieldLength();
1948 
1949        if (columnCount == 0) {
1950            return buildResultSetWithUpdates(callingStatement, resultPacket);
1951        } else if (columnCount == Buffer.NULL_LENGTH) {
1952            String charEncoding = null;
1953 
1954            if (this.connection.getUseUnicode()) {
1955                charEncoding = this.connection.getEncoding();
1956            }
1957 
1958            String fileName = null;
1959 
1960            if (this.platformDbCharsetMatches) {
1961                fileName = ((charEncoding != null)
1962                    ? resultPacket.readString(charEncoding)
1963                    : resultPacket.readString());
1964            } else {
1965                fileName = resultPacket.readString();
1966            }
1967 
1968            return sendFileToServer(callingStatement, fileName);
1969        } else {
1970            com.mysql.jdbc.ResultSet results = getResultSet(callingStatement,
1971                    columnCount, maxRows, resultSetType, resultSetConcurrency,
1972                    streamResults, catalog, isBinaryEncoded, unpackFieldInfo);
1973 
1974            return results;
1975        }
1976    }
1977 
1978    private int alignPacketSize(int a, int l) {
1979        return ((((a) + (l)) - 1) & ~((l) - 1));
1980    }
1981 
1982    private com.mysql.jdbc.ResultSet buildResultSetWithRows(
1983        Statement callingStatement, String catalog,
1984        com.mysql.jdbc.Field[] fields, RowData rows, int resultSetType,
1985        int resultSetConcurrency, boolean isBinaryEncoded)
1986        throws SQLException {
1987        ResultSet rs = null;
1988 
1989        switch (resultSetConcurrency) {
1990        case java.sql.ResultSet.CONCUR_READ_ONLY:
1991            rs = new com.mysql.jdbc.ResultSet(catalog, fields, rows,
1992                    this.connection, callingStatement);
1993 
1994            if (isBinaryEncoded) {
1995                rs.setBinaryEncoded();
1996            }
1997 
1998            break;
1999 
2000        case java.sql.ResultSet.CONCUR_UPDATABLE:
2001            rs = new com.mysql.jdbc.UpdatableResultSet(catalog, fields, rows,
2002                    this.connection, callingStatement);
2003 
2004            break;
2005 
2006        default:
2007            return new com.mysql.jdbc.ResultSet(catalog, fields, rows,
2008                this.connection, callingStatement);
2009        }
2010 
2011        rs.setResultSetType(resultSetType);
2012        rs.setResultSetConcurrency(resultSetConcurrency);
2013 
2014        return rs;
2015    }
2016 
2017    private com.mysql.jdbc.ResultSet buildResultSetWithUpdates(
2018        Statement callingStatement, Buffer resultPacket)
2019        throws SQLException {
2020        long updateCount = -1;
2021        long updateID = -1;
2022        String info = null;
2023 
2024        try {
2025            if (this.useNewUpdateCounts) {
2026                updateCount = resultPacket.newReadLength();
2027                updateID = resultPacket.newReadLength();
2028            } else {
2029                updateCount = resultPacket.readLength();
2030                updateID = resultPacket.readLength();
2031            }
2032 
2033            if (this.use41Extensions) {
2034                this.serverStatus = resultPacket.readInt();
2035 
2036                this.warningCount = resultPacket.readInt();
2037 
2038                if (this.warningCount > 0) {
2039                    this.hadWarnings = true; // this is a 'latch', it's reset by sendCommand()
2040                }
2041 
2042                resultPacket.readByte(); // advance pointer
2043 
2044                if (this.profileSql) {
2045                    this.queryNoIndexUsed = (this.serverStatus &
2046                        SERVER_QUERY_NO_GOOD_INDEX_USED) != 0;
2047                    this.queryBadIndexUsed = (this.serverStatus &
2048                        SERVER_QUERY_NO_INDEX_USED) != 0;
2049                }
2050            }
2051 
2052            if (this.connection.isReadInfoMsgEnabled()) {
2053                info = resultPacket.readString();
2054            }
2055        } catch (Exception ex) {
2056            throw SQLError.createSQLException(SQLError.get(
2057                    SQLError.SQL_STATE_GENERAL_ERROR) + ": " //$NON-NLS-1$
2058                 +ex.getClass().getName(), SQLError.SQL_STATE_GENERAL_ERROR, -1);
2059        }
2060 
2061        ResultSet updateRs = new com.mysql.jdbc.ResultSet(updateCount,
2062                updateID, this.connection, callingStatement);
2063 
2064        if (info != null) {
2065            updateRs.setServerInfo(info);
2066        }
2067 
2068        return updateRs;
2069    }
2070 
2071    private void checkForOutstandingStreamingData() throws SQLException {
2072        if (this.streamingData != null) {
2073            if (!this.connection.getClobberStreamingResults()) {
2074                throw SQLError.createSQLException(Messages.getString("MysqlIO.39") //$NON-NLS-1$
2075                     +this.streamingData +
2076                    Messages.getString("MysqlIO.40") //$NON-NLS-1$
2077                     +Messages.getString("MysqlIO.41") //$NON-NLS-1$
2078                     +Messages.getString("MysqlIO.42")); //$NON-NLS-1$
2079            }
2080 
2081            // Close the result set
2082            this.streamingData.getOwner().realClose(false);
2083 
2084            // clear any pending data....
2085            clearInputStream();
2086        }
2087    }
2088 
2089    private Buffer compressPacket(Buffer packet, int offset, int packetLen,
2090        int headerLength) throws SQLException {
2091        packet.writeLongInt(packetLen - headerLength);
2092        packet.writeByte((byte) 0); // wrapped packet has 0 packet seq.
2093 
2094        int lengthToWrite = 0;
2095        int compressedLength = 0;
2096        byte[] bytesToCompress = packet.getByteBuffer();
2097        byte[] compressedBytes = null;
2098        int offsetWrite = 0;
2099 
2100        if (packetLen < MIN_COMPRESS_LEN) {
2101            lengthToWrite = packetLen;
2102            compressedBytes = packet.getByteBuffer();
2103            compressedLength = 0;
2104            offsetWrite = offset;
2105        } else {
2106            compressedBytes = new byte[bytesToCompress.length * 2];
2107 
2108            this.deflater.reset();
2109            this.deflater.setInput(bytesToCompress, offset, packetLen);
2110            this.deflater.finish();
2111 
2112            int compLen = this.deflater.deflate(compressedBytes);
2113 
2114            if (compLen > packetLen) {
2115                lengthToWrite = packetLen;
2116                compressedBytes = packet.getByteBuffer();
2117                compressedLength = 0;
2118                offsetWrite = offset;
2119            } else {
2120                lengthToWrite = compLen;
2121                headerLength += COMP_HEADER_LENGTH;
2122                compressedLength = packetLen;
2123            }
2124        }
2125 
2126        Buffer compressedPacket = new Buffer(packetLen + headerLength);
2127 
2128        compressedPacket.setPosition(0);
2129        compressedPacket.writeLongInt(lengthToWrite);
2130        compressedPacket.writeByte(this.packetSequence);
2131        compressedPacket.writeLongInt(compressedLength);
2132        compressedPacket.writeBytesNoNull(compressedBytes, offsetWrite,
2133            lengthToWrite);
2134 
2135        return compressedPacket;
2136    }
2137 
2138    private final void readServerStatusForResultSets(Buffer rowPacket)
2139        throws SQLException {
2140        if (this.use41Extensions) {
2141            rowPacket.readByte(); // skips the 'last packet' flag
2142 
2143            this.warningCount = rowPacket.readInt();
2144 
2145            if (this.warningCount > 0) {
2146                this.hadWarnings = true; // this is a 'latch', it's reset by sendCommand()
2147            }
2148 
2149            this.serverStatus = rowPacket.readInt();
2150 
2151            if (this.profileSql) {
2152                this.queryNoIndexUsed = (this.serverStatus &
2153                    SERVER_QUERY_NO_GOOD_INDEX_USED) != 0;
2154                this.queryBadIndexUsed = (this.serverStatus &
2155                    SERVER_QUERY_NO_INDEX_USED) != 0;
2156            }
2157        }
2158    }
2159    
2160    private SocketFactory createSocketFactory() throws SQLException {
2161        try {
2162            if (this.socketFactoryClassName == null) {
2163                throw SQLError.createSQLException(Messages.getString("MysqlIO.75"), //$NON-NLS-1$
2164                    SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE);
2165            }
2166 
2167            return (SocketFactory) (Class.forName(this.socketFactoryClassName)
2168                                         .newInstance());
2169        } catch (Exception ex) {
2170            throw SQLError.createSQLException(Messages.getString("MysqlIO.76") //$NON-NLS-1$
2171                 +this.socketFactoryClassName +
2172                Messages.getString("MysqlIO.77") + ex.toString() //$NON-NLS-1$
2173                 +(this.connection.getParanoid() ? "" //$NON-NLS-1$
2174                                                 : Util.stackTraceToString(ex)),
2175                SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE);
2176        }
2177    }
2178 
2179    private void enqueuePacketForDebugging(boolean isPacketBeingSent,
2180        boolean isPacketReused, int sendLength, byte[] header, Buffer packet)
2181        throws SQLException {
2182        if ((this.packetDebugRingBuffer.size() + 1) > this.connection.getPacketDebugBufferSize()) {
2183            this.packetDebugRingBuffer.removeFirst();
2184        }
2185 
2186        StringBuffer packetDump = null;
2187 
2188        if (!isPacketBeingSent) {
2189            int bytesToDump = Math.min(MAX_PACKET_DUMP_LENGTH,
2190                    packet.getBufLength());
2191 
2192            Buffer packetToDump = new Buffer(4 + bytesToDump);
2193 
2194            packetToDump.setPosition(0);
2195            packetToDump.writeBytesNoNull(header);
2196            packetToDump.writeBytesNoNull(packet.getBytes(0, bytesToDump));
2197 
2198            String packetPayload = packetToDump.dump(bytesToDump);
2199 
2200            packetDump = new StringBuffer(96 + packetPayload.length());
2201 
2202            packetDump.append("Server ");
2203 
2204            if (isPacketReused) {
2205                packetDump.append("(re-used)");
2206            } else {
2207                packetDump.append("(new)");
2208            }
2209 
2210            packetDump.append(" ");
2211            packetDump.append(packet.toSuperString());
2212            packetDump.append(" --------------------> Client\n");
2213            packetDump.append("\nPacket payload:\n\n");
2214            packetDump.append(packetPayload);
2215 
2216            if (bytesToDump == MAX_PACKET_DUMP_LENGTH) {
2217                packetDump.append("\nNote: Packet of " + packet.getBufLength() +
2218                    " bytes truncated to " + MAX_PACKET_DUMP_LENGTH +
2219                    " bytes.\n");
2220            }
2221        } else {
2222            int bytesToDump = Math.min(MAX_PACKET_DUMP_LENGTH, sendLength);
2223 
2224            String packetPayload = packet.dump(bytesToDump);
2225 
2226            packetDump = new StringBuffer(64 + 4 + packetPayload.length());
2227 
2228            packetDump.append("Client ");
2229            packetDump.append(packet.toSuperString());
2230            packetDump.append("--------------------> Server\n");
2231            packetDump.append("\nPacket payload:\n\n");
2232            packetDump.append(packetPayload);
2233 
2234            if (bytesToDump == MAX_PACKET_DUMP_LENGTH) {
2235                packetDump.append("\nNote: Packet of " + sendLength +
2236                    " bytes truncated to " + MAX_PACKET_DUMP_LENGTH +
2237                    " bytes.\n");
2238            }
2239        }
2240 
2241        this.packetDebugRingBuffer.addLast(packetDump);
2242    }
2243    
2244    private RowData readSingleRowSet(long columnCount, int maxRows,
2245        int resultSetConcurrency, boolean isBinaryEncoded, Field[] fields)
2246        throws SQLException {
2247        RowData rowData;
2248        ArrayList rows = new ArrayList();
2249 
2250        // Now read the data
2251        Object rowBytes = nextRow(fields, (int) columnCount, isBinaryEncoded,
2252                resultSetConcurrency);
2253        
2254        int rowCount = 0;
2255 
2256        if (rowBytes != null) {
2257            rows.add(rowBytes);
2258            rowCount = 1;
2259        }
2260 
2261        while (rowBytes != null) {
2262            rowBytes = nextRow(fields, (int) columnCount, isBinaryEncoded,
2263                    resultSetConcurrency);
2264 
2265            if (rowBytes != null) {
2266                    if ((maxRows == -1) || (rowCount < maxRows)) {
2267                            rows.add(rowBytes);
2268                            rowCount++;
2269                    }
2270            }
2271        }
2272 
2273        rowData = new RowDataStatic(rows);
2274 
2275        return rowData;
2276    }
2277 
2278    /**
2279     * Don't hold on to overly-large packets
2280     */
2281    private void reclaimLargeReusablePacket() {
2282        if ((this.reusablePacket != null) &&
2283                (this.reusablePacket.getCapacity() > 1048576)) {
2284            this.reusablePacket = new Buffer(this.connection.getNetBufferLength());
2285        }
2286    }
2287 
2288    /**
2289     * Re-use a packet to read from the MySQL server
2290     *
2291     * @param reuse DOCUMENT ME!
2292     *
2293     * @return DOCUMENT ME!
2294     *
2295     * @throws SQLException DOCUMENT ME!
2296     * @throws SQLException DOCUMENT ME!
2297     */
2298    private final Buffer reuseAndReadPacket(Buffer reuse)
2299        throws SQLException {
2300        
2301            try {
2302                    reuse.setWasMultiPacket(false);
2303                    
2304                    int lengthRead = readFully(this.mysqlInput,
2305                                    this.packetHeaderBuf, 0, 4);
2306                    
2307                    if (lengthRead < 4) {
2308                            forceClose();
2309                            throw new IOException(Messages.getString("MysqlIO.43")); //$NON-NLS-1$
2310                    }
2311                    
2312                    int packetLength = (this.packetHeaderBuf[0] & 0xff) +
2313                    ((this.packetHeaderBuf[1] & 0xff) << 8) +
2314                    ((this.packetHeaderBuf[2] & 0xff) << 16);
2315                    
2316                    if (this.traceProtocol) {
2317                            StringBuffer traceMessageBuf = new StringBuffer();
2318                            
2319                            traceMessageBuf.append(Messages.getString("MysqlIO.44")); //$NON-NLS-1$
2320                            traceMessageBuf.append(packetLength);
2321                            traceMessageBuf.append(Messages.getString("MysqlIO.45")); //$NON-NLS-1$
2322                            traceMessageBuf.append(StringUtils.dumpAsHex(
2323                                            this.packetHeaderBuf, 4));
2324                            
2325                            this.connection.getLog().logTrace(traceMessageBuf.toString());
2326                    }
2327                    
2328                    byte multiPacketSeq = this.packetHeaderBuf[3];
2329                    
2330                    if (!this.packetSequenceReset) {
2331                            if (this.enablePacketDebug && this.checkPacketSequence) {
2332                                    checkPacketSequencing(multiPacketSeq);
2333                            }
2334                    } else {
2335                            this.packetSequenceReset = false;
2336                    }
2337                    
2338                    this.readPacketSequence = multiPacketSeq;
2339                    
2340                    // Set the Buffer to it's original state
2341                    reuse.setPosition(0);
2342                    
2343                    // Do we need to re-alloc the byte buffer?
2344                    //
2345                    // Note: We actually check the length of the buffer,
2346                    // rather than getBufLength(), because getBufLength() is not
2347                    // necesarily the actual length of the byte array
2348                    // used as the buffer
2349                    if (reuse.getByteBuffer().length <= packetLength) {
2350                            reuse.setByteBuffer(new byte[packetLength + 1]);
2351                    }
2352                    
2353                    // Set the new length
2354                    reuse.setBufLength(packetLength);
2355                    
2356                    // Read the data from the server
2357                    int numBytesRead = readFully(this.mysqlInput,
2358                                    reuse.getByteBuffer(), 0, packetLength);
2359                    
2360                    if (numBytesRead != packetLength) {
2361                            throw new IOException("Short read, expected " +
2362                                            packetLength + " bytes, only read " + numBytesRead);
2363                    }
2364                    
2365                    if (this.traceProtocol) {
2366                            StringBuffer traceMessageBuf = new StringBuffer();
2367                            
2368                            traceMessageBuf.append(Messages.getString("MysqlIO.46")); //$NON-NLS-1$
2369                            traceMessageBuf.append(getPacketDumpToLog(reuse,
2370                                            packetLength));
2371                            
2372                            this.connection.getLog().logTrace(traceMessageBuf.toString());
2373                    }
2374                    
2375                    if (this.enablePacketDebug) {
2376                            enqueuePacketForDebugging(false, true, 0,
2377                                            this.packetHeaderBuf, reuse);
2378                    }
2379                    
2380                    boolean isMultiPacket = false;
2381                    
2382                    if (packetLength == this.maxThreeBytes) {
2383                            reuse.setPosition(this.maxThreeBytes);
2384                            
2385                            int packetEndPoint = packetLength;
2386                            
2387                            // it's multi-packet
2388                            isMultiPacket = true;
2389                            
2390                            lengthRead = readFully(this.mysqlInput,
2391                                            this.packetHeaderBuf = new byte[4], 0, 4);
2392                            
2393                            if (lengthRead < 4) {
2394                                    forceClose();
2395                                    throw new IOException(Messages.getString("MysqlIO.47")); //$NON-NLS-1$
2396                            }
2397                            
2398                            packetLength = (this.packetHeaderBuf[0] & 0xff) +
2399                                    ((this.packetHeaderBuf[1] & 0xff) << 8) +
2400                                    ((this.packetHeaderBuf[2] & 0xff) << 16);
2401                            
2402                            Buffer multiPacket = new Buffer(packetLength);
2403                            boolean firstMultiPkt = true;
2404                            
2405                            while (true) {
2406                                    if (!firstMultiPkt) {
2407                                            lengthRead = readFully(this.mysqlInput,
2408                                                            this.packetHeaderBuf = new byte[4], 0, 4);
2409                                            
2410                                            if (lengthRead < 4) {
2411                                                    forceClose();
2412                                                    throw new IOException(Messages.getString(
2413                                                    "MysqlIO.48")); //$NON-NLS-1$
2414                                            }
2415                                            
2416                                            packetLength = (this.packetHeaderBuf[0] & 0xff) +
2417                                            ((this.packetHeaderBuf[1] & 0xff) << 8) +
2418                                            ((this.packetHeaderBuf[2] & 0xff) << 16);
2419                                    } else {
2420                                            firstMultiPkt = false;
2421                                    }
2422                                    
2423                                    if (!this.useNewLargePackets && (packetLength == 1)) {
2424                                            clearInputStream();
2425                                            
2426                                            break;
2427                                    } else if (packetLength < this.maxThreeBytes) {
2428                                            byte newPacketSeq = this.packetHeaderBuf[3];
2429                                            
2430                                            if (newPacketSeq != (multiPacketSeq + 1)) {
2431                                                    throw new IOException(Messages.getString(
2432                                                    "MysqlIO.49")); //$NON-NLS-1$
2433                                            }
2434                                            
2435                                            multiPacketSeq = newPacketSeq;
2436                                            
2437                                            // Set the Buffer to it's original state
2438                                            multiPacket.setPosition(0);
2439                                            
2440                                            // Set the new length
2441                                            multiPacket.setBufLength(packetLength);
2442                                            
2443                                            // Read the data from the server
2444                                            byte[] byteBuf = multiPacket.getByteBuffer();
2445                                            int lengthToWrite = packetLength;
2446                                            
2447                                            int bytesRead = readFully(this.mysqlInput, byteBuf,
2448                                                            0, packetLength);
2449                                            
2450                                            if (bytesRead != lengthToWrite) {
2451                                                    throw new CommunicationsException(this.connection,
2452                                                                    this.lastPacketSentTimeMs,
2453                                                                    SQLError.createSQLException(Messages.getString(
2454                                                                    "MysqlIO.50") //$NON-NLS-1$
2455                                                                    +lengthToWrite +
2456                                                                    Messages.getString("MysqlIO.51") +
2457                                                                    bytesRead //$NON-NLS-1$
2458                                                                    +".")); //$NON-NLS-1$
2459                                            }
2460                                            
2461                                            reuse.writeBytesNoNull(byteBuf, 0, lengthToWrite);
2462                                            
2463                                            packetEndPoint += lengthToWrite;
2464                                            
2465                                            break; // end of multipacket sequence
2466                                    }
2467                                    
2468                                    byte newPacketSeq = this.packetHeaderBuf[3];
2469                                    
2470                                    if (newPacketSeq != (multiPacketSeq + 1)) {
2471                                            throw new IOException(Messages.getString(
2472                                            "MysqlIO.53")); //$NON-NLS-1$
2473                                    }
2474                                    
2475                                    multiPacketSeq = newPacketSeq;
2476                                    
2477                                    // Set the Buffer to it's original state
2478                                    multiPacket.setPosition(0);
2479                                    
2480                                    // Set the new length
2481                                    multiPacket.setBufLength(packetLength);
2482                                    
2483                                    // Read the data from the server
2484                                    byte[] byteBuf = multiPacket.getByteBuffer();
2485                                    int lengthToWrite = packetLength;
2486                                    
2487                                    int bytesRead = readFully(this.mysqlInput, byteBuf, 0,
2488                                                    packetLength);
2489                                    
2490                                    if (bytesRead != lengthToWrite) {
2491                                            throw new CommunicationsException(this.connection,
2492                                                            this.lastPacketSentTimeMs,
2493                                                            SQLError.createSQLException(Messages.getString(
2494                                                            "MysqlIO.54") //$NON-NLS-1$
2495                                                            +lengthToWrite +
2496                                                            Messages.getString("MysqlIO.55") //$NON-NLS-1$
2497                                                            +bytesRead + ".")); //$NON-NLS-1$
2498                                    }
2499                                    
2500                                    reuse.writeBytesNoNull(byteBuf, 0, lengthToWrite);
2501                                    
2502                                    packetEndPoint += lengthToWrite;
2503                            }
2504                            
2505                            reuse.setPosition(0);
2506                            reuse.setWasMultiPacket(true);
2507                    }
2508                    
2509                    if (!isMultiPacket) {
2510                            reuse.getByteBuffer()[packetLength] = 0; // Null-termination
2511                    }
2512                    
2513                    return reuse;
2514            } catch (IOException ioEx) {
2515                    throw new CommunicationsException(this.connection,
2516                                    this.lastPacketSentTimeMs, ioEx);
2517            } catch (OutOfMemoryError oom) {
2518                    try {
2519                            // _Try_ this
2520                            clearInputStream();
2521                    } finally {
2522                            try {
2523                                    this.connection.realClose(false, false, true, oom);
2524                            } finally {
2525                                    throw oom;
2526                            }
2527                    }
2528            }
2529            
2530    }
2531 
2532    /**
2533         * @param multiPacketSeq
2534         * @throws CommunicationsException
2535         */
2536    private void checkPacketSequencing(byte multiPacketSeq)
2537        throws CommunicationsException {
2538        if ((multiPacketSeq == -128) && (this.readPacketSequence != 127)) {
2539            throw new CommunicationsException(this.connection,
2540                this.lastPacketSentTimeMs,
2541                new IOException("Packets out of order, expected packet # -128, but received packet # " +
2542                    multiPacketSeq));
2543        }
2544 
2545        if ((this.readPacketSequence == -1) && (multiPacketSeq != 0)) {
2546            throw new CommunicationsException(this.connection,
2547                this.lastPacketSentTimeMs,
2548                new IOException("Packets out of order, expected packet # -1, but received packet # " +
2549                    multiPacketSeq));
2550        }
2551 
2552        if ((multiPacketSeq != -128) && (this.readPacketSequence != -1) &&
2553                (multiPacketSeq != (this.readPacketSequence + 1))) {
2554            throw new CommunicationsException(this.connection,
2555                this.lastPacketSentTimeMs,
2556                new IOException("Packets out of order, expected packet # " +
2557                    (this.readPacketSequence + 1) + ", but received packet # " +
2558                    multiPacketSeq));
2559        }
2560    }
2561 
2562    void enableMultiQueries() throws SQLException {
2563            Buffer buf = getSharedSendPacket();
2564            
2565            buf.clear();
2566            buf.writeByte((byte)MysqlDefs.COM_SET_OPTION);
2567            buf.writeInt(0);
2568            sendCommand(MysqlDefs.COM_SET_OPTION, null, buf, false, null);
2569    }
2570    
2571    void disableMultiQueries() throws SQLException {
2572            Buffer buf = getSharedSendPacket();
2573            
2574            buf.clear();
2575            buf.writeByte((byte)MysqlDefs.COM_SET_OPTION);
2576            buf.writeInt(1);
2577            sendCommand(MysqlDefs.COM_SET_OPTION, null, buf, false, null);
2578    }
2579    
2580    private final void send(Buffer packet, int packetLen)
2581        throws SQLException {
2582        try {
2583            if (packetLen > this.maxAllowedPacket) {
2584                throw new PacketTooBigException(packetLen, this.maxAllowedPacket);
2585            }
2586 
2587                        if (this.connection.getMaintainTimeStats()) {
2588                                this.lastPacketSentTimeMs = System.currentTimeMillis();
2589                        }
2590 
2591            if ((this.serverMajorVersion >= 4) &&
2592                    (packetLen >= this.maxThreeBytes)) {
2593                sendSplitPackets(packet);
2594            } else {
2595                this.packetSequence++;
2596 
2597                Buffer packetToSend = packet;
2598 
2599                packetToSend.setPosition(0);
2600 
2601                if (this.useCompression) {
2602                    int originalPacketLen = packetLen;
2603 
2604                    packetToSend = compressPacket(packet, 0, packetLen,
2605                            HEADER_LENGTH);
2606                    packetLen = packetToSend.getPosition();
2607 
2608                    if (this.traceProtocol) {
2609                        StringBuffer traceMessageBuf = new StringBuffer();
2610 
2611                        traceMessageBuf.append(Messages.getString("MysqlIO.57")); //$NON-NLS-1$
2612                        traceMessageBuf.append(getPacketDumpToLog(
2613                                packetToSend, packetLen));
2614                        traceMessageBuf.append(Messages.getString("MysqlIO.58")); //$NON-NLS-1$
2615                        traceMessageBuf.append(getPacketDumpToLog(packet,
2616                                originalPacketLen));
2617 
2618                        this.connection.getLog().logTrace(traceMessageBuf.toString());
2619                    }
2620                } else {
2621                    packetToSend.writeLongInt(packetLen - HEADER_LENGTH);
2622                    packetToSend.writeByte(this.packetSequence);
2623 
2624                    if (this.traceProtocol) {
2625                        StringBuffer traceMessageBuf = new StringBuffer();
2626 
2627                        traceMessageBuf.append(Messages.getString("MysqlIO.59")); //$NON-NLS-1$
2628                        traceMessageBuf.append(packetToSend.dump(packetLen));
2629 
2630                        this.connection.getLog().logTrace(traceMessageBuf.toString());
2631                    }
2632                }
2633 
2634                
2635                this.mysqlOutput.write(packetToSend.getByteBuffer(), 0,
2636                                packetLen);
2637                this.mysqlOutput.flush();   
2638            }
2639 
2640            if (this.enablePacketDebug) {
2641                enqueuePacketForDebugging(true, false, packetLen + 5,
2642                    this.packetHeaderBuf, packet);
2643            }
2644 
2645            // 
2646            // Don't hold on to large packets
2647            //
2648            if (packet == this.sharedSendPacket) {
2649                reclaimLargeSharedSendPacket();
2650            }
2651        } catch (IOException ioEx) {
2652            throw new CommunicationsException(this.connection,
2653                this.lastPacketSentTimeMs, ioEx);
2654        }
2655    }
2656 
2657    /**
2658     * Reads and sends a file to the server for LOAD DATA LOCAL INFILE
2659     *
2660     * @param callingStatement DOCUMENT ME!
2661     * @param fileName the file name to send.
2662     *
2663     * @return DOCUMENT ME!
2664     *
2665     * @throws SQLException DOCUMENT ME!
2666     */
2667    private final ResultSet sendFileToServer(Statement callingStatement,
2668        String fileName) throws SQLException {
2669            
2670        Buffer filePacket = (this.loadFileBufRef == null) ? null
2671                                                          : (Buffer) (this.loadFileBufRef.get());
2672 
2673        int bigPacketLength = Math.min(this.connection.getMaxAllowedPacket() -
2674                (HEADER_LENGTH * 3),
2675                alignPacketSize(this.connection.getMaxAllowedPacket() - 16, 4096) -
2676                (HEADER_LENGTH * 3));
2677        
2678        int oneMeg = 1024 * 1024;
2679        
2680        int smallerPacketSizeAligned = Math.min(oneMeg - (HEADER_LENGTH * 3), 
2681                        alignPacketSize(oneMeg - 16, 4096) - (HEADER_LENGTH * 3));
2682        
2683        int packetLength = Math.min(smallerPacketSizeAligned, bigPacketLength);
2684 
2685        if (filePacket == null) {
2686                try {
2687                        filePacket = new Buffer((packetLength + HEADER_LENGTH));
2688                        this.loadFileBufRef = new SoftReference(filePacket);
2689                } catch (OutOfMemoryError oom) {
2690                        throw SQLError.createSQLException("Could not allocate packet of " + packetLength 
2691                                        + " bytes required for LOAD DATA LOCAL INFILE operation." 
2692                                                + " Try increasing max heap allocation for JVM or decreasing server variable "
2693                                                + "'max_allowed_packet'", SQLError.SQL_STATE_MEMORY_ALLOCATION_FAILURE);
2694                                
2695                }
2696        }
2697 
2698        filePacket.clear();
2699        send(filePacket, 0);
2700 
2701        byte[] fileBuf = new byte[packetLength];
2702 
2703        BufferedInputStream fileIn = null;
2704 
2705        try {
2706            if (!this.connection.getAllowUrlInLocalInfile()) {
2707                fileIn = new BufferedInputStream(new FileInputStream(fileName));
2708            } else {
2709                // First look for ':'
2710                if (fileName.indexOf(":") != -1) {
2711                    try {
2712                        URL urlFromFileName = new URL(fileName);
2713                        fileIn = new BufferedInputStream(urlFromFileName.openStream());
2714                    } catch (MalformedURLException badUrlEx) {
2715                        // we fall back to trying this as a file input stream
2716                        fileIn = new BufferedInputStream(new FileInputStream(
2717                                    fileName));
2718                    }
2719                } else {
2720                    fileIn = new BufferedInputStream(new FileInputStream(
2721                                fileName));
2722                }
2723            }
2724 
2725            int bytesRead = 0;
2726 
2727            while ((bytesRead = fileIn.read(fileBuf)) != -1) {
2728                filePacket.clear();
2729                filePacket.writeBytesNoNull(fileBuf, 0, bytesRead);
2730                send(filePacket, filePacket.getPosition());
2731            }
2732        } catch (IOException ioEx) {
2733            StringBuffer messageBuf = new StringBuffer(Messages.getString(
2734                        "MysqlIO.60")); //$NON-NLS-1$
2735 
2736            if (!this.connection.getParanoid()) {
2737                messageBuf.append("'"); //$NON-NLS-1$
2738 
2739                if (fileName != null) {
2740                    messageBuf.append(fileName);
2741                }
2742 
2743                messageBuf.append("'"); //$NON-NLS-1$
2744            }
2745 
2746            messageBuf.append(Messages.getString("MysqlIO.63")); //$NON-NLS-1$
2747 
2748            if (!this.connection.getParanoid()) {
2749                messageBuf.append(Messages.getString("MysqlIO.64")); //$NON-NLS-1$
2750                messageBuf.append(Util.stackTraceToString(ioEx));
2751            }
2752 
2753            throw SQLError.createSQLException(messageBuf.toString(),
2754                SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
2755        } finally {
2756            if (fileIn != null) {
2757                try {
2758                    fileIn.close();
2759                } catch (Exception ex) {
2760                    throw SQLError.createSQLException(Messages.getString("MysqlIO.65"), //$NON-NLS-1$
2761                        SQLError.SQL_STATE_GENERAL_ERROR);
2762                }
2763 
2764                fileIn = null;
2765            } else {
2766                // file open failed, but server needs one packet
2767                filePacket.clear();
2768                send(filePacket, filePacket.getPosition());
2769                checkErrorPacket(); // to clear response off of queue
2770            }
2771        }
2772 
2773        // send empty packet to mark EOF
2774        filePacket.clear();
2775        send(filePacket, filePacket.getPosition());
2776 
2777        Buffer resultPacket = checkErrorPacket();
2778 
2779        return buildResultSetWithUpdates(callingStatement, resultPacket);
2780    }
2781 
2782    /**
2783     * Checks for errors in the reply packet, and if none, returns the reply
2784     * packet, ready for reading
2785     *
2786     * @param command the command being issued (if used)
2787     *
2788     * @return DOCUMENT ME!
2789     *
2790     * @throws SQLException if an error packet was received
2791     * @throws CommunicationsException DOCUMENT ME!
2792     */
2793    private Buffer checkErrorPacket(int command) throws SQLException {
2794        int statusCode = 0;
2795        Buffer resultPacket = null;
2796        this.serverStatus = 0;
2797 
2798        try {
2799            // Check return value, if we get a java.io.EOFException,
2800            // the server has gone away. We'll pass it on up the
2801            // exception chain and let someone higher up decide
2802            // what to do (barf, reconnect, etc).
2803            resultPacket = reuseAndReadPacket(this.reusablePacket);
2804            statusCode = resultPacket.readByte();
2805        } catch (SQLException sqlEx) {
2806            // Don't wrap SQL Exceptions
2807            throw sqlEx;
2808        } catch (Exception fallThru) {
2809            throw new CommunicationsException(this.connection,
2810                this.lastPacketSentTimeMs, fallThru);
2811        }
2812 
2813        // Error handling
2814        if (statusCode == (byte) 0xff) {
2815            String serverErrorMessage;
2816            int errno = 2000;
2817 
2818            if (this.protocolVersion > 9) {
2819                errno = resultPacket.readInt();
2820 
2821                String xOpen = null;
2822 
2823                serverErrorMessage = 
2824                        resultPacket.readString(this.connection.getErrorMessageEncoding());
2825 
2826                if (serverErrorMessage.startsWith("#")) { //$NON-NLS-1$
2827 
2828                    // we have an SQLState
2829                    if (serverErrorMessage.length() > 6) {
2830                        xOpen = serverErrorMessage.substring(1, 6);
2831                        serverErrorMessage = serverErrorMessage.substring(6);
2832 
2833                        if (xOpen.equals("HY000")) { //$NON-NLS-1$
2834                            xOpen = SQLError.mysqlToSqlState(errno,
2835                                    this.connection.getUseSqlStateCodes());
2836                        }
2837                    } else {
2838                        xOpen = SQLError.mysqlToSqlState(errno,
2839                                this.connection.getUseSqlStateCodes());
2840                    }
2841                } else {
2842                    xOpen = SQLError.mysqlToSqlState(errno,
2843                            this.connection.getUseSqlStateCodes());
2844                }
2845 
2846                clearInputStream();
2847 
2848                StringBuffer errorBuf = new StringBuffer();
2849 
2850                String xOpenErrorMessage = SQLError.get(xOpen);
2851 
2852                if (!this.connection.getUseOnlyServerErrorMessages()) {
2853                    if (xOpenErrorMessage != null) {
2854                        errorBuf.append(xOpenErrorMessage);
2855                        errorBuf.append(Messages.getString("MysqlIO.68")); //$NON-NLS-1$
2856                    }
2857                }
2858 
2859                errorBuf.append(serverErrorMessage);
2860 
2861                if (!this.connection.getUseOnlyServerErrorMessages()) {
2862                    if (xOpenErrorMessage != null) {
2863                        errorBuf.append("\""); //$NON-NLS-1$
2864                    }
2865                }
2866                
2867                if (xOpen != null && xOpen.startsWith("22")) {
2868                        throw new MysqlDataTruncation(errorBuf.toString(), 0, true, false, 0, 0); 
2869                } else {
2870                        throw SQLError.createSQLException(errorBuf.toString(), xOpen, errno);
2871                }
2872            }
2873 
2874            serverErrorMessage = resultPacket.readString(
2875                            this.connection.getErrorMessageEncoding());
2876            clearInputStream();
2877 
2878            if (serverErrorMessage.indexOf(Messages.getString("MysqlIO.70")) != -1) { //$NON-NLS-1$
2879                throw SQLError.createSQLException(SQLError.get(
2880                        SQLError.SQL_STATE_COLUMN_NOT_FOUND) +
2881                    ", " //$NON-NLS-1$
2882                     +serverErrorMessage, SQLError.SQL_STATE_COLUMN_NOT_FOUND,
2883                    -1);
2884            }
2885 
2886            StringBuffer errorBuf = new StringBuffer(Messages.getString(
2887                        "MysqlIO.72")); //$NON-NLS-1$
2888            errorBuf.append(serverErrorMessage);
2889            errorBuf.append("\""); //$NON-NLS-1$
2890 
2891            throw SQLError.createSQLException(SQLError.get(
2892                    SQLError.SQL_STATE_GENERAL_ERROR) + ", " //$NON-NLS-1$
2893                 +errorBuf.toString(), SQLError.SQL_STATE_GENERAL_ERROR, -1);
2894        }
2895 
2896        return resultPacket;
2897    }
2898 
2899    /**
2900     * Sends a large packet to the server as a series of smaller packets
2901     *
2902     * @param packet DOCUMENT ME!
2903     *
2904     * @throws SQLException DOCUMENT ME!
2905     * @throws CommunicationsException DOCUMENT ME!
2906     */
2907    private final void sendSplitPackets(Buffer packet)
2908        throws SQLException {
2909        try {
2910            //
2911            // Big packets are handled by splitting them in packets of MAX_THREE_BYTES
2912            // length. The last packet is always a packet that is < MAX_THREE_BYTES.
2913            // (The last packet may even have a length of 0)
2914            //
2915            //
2916            // NB: Guarded by execSQL. If the driver changes architecture, this
2917            // will need to be synchronized in some other way
2918            //
2919            Buffer headerPacket = (this.splitBufRef == null) ? null
2920                                                             : (Buffer) (this.splitBufRef.get());
2921 
2922            //
2923            // Store this packet in a soft reference...It can be re-used if not GC'd (so clients
2924            // that use it frequently won't have to re-alloc the 16M buffer), but we don't
2925            // penalize infrequent users of large packets by keeping 16M allocated all of the time
2926            //
2927            if (headerPacket == null) {
2928                headerPacket = new Buffer((this.maxThreeBytes +
2929                        HEADER_LENGTH));
2930                this.splitBufRef = new SoftReference(headerPacket);
2931            }
2932 
2933            int len = packet.getPosition();
2934            int splitSize = this.maxThreeBytes;
2935            int originalPacketPos = HEADER_LENGTH;
2936            byte[] origPacketBytes = packet.getByteBuffer();
2937            byte[] headerPacketBytes = headerPacket.getByteBuffer();
2938 
2939            while (len >= this.maxThreeBytes) {
2940                this.packetSequence++;
2941 
2942                headerPacket.setPosition(0);
2943                headerPacket.writeLongInt(splitSize);
2944 
2945                headerPacket.writeByte(this.packetSequence);
2946                System.arraycopy(origPacketBytes, originalPacketPos,
2947                    headerPacketBytes, 4, splitSize);
2948 
2949                int packetLen = splitSize + HEADER_LENGTH;
2950 
2951                //
2952                // Swap a compressed packet in, if we're using
2953                // compression...
2954                //
2955                if (!this.useCompression) {
2956                    this.mysqlOutput.write(headerPacketBytes, 0,
2957                        splitSize + HEADER_LENGTH);
2958                    this.mysqlOutput.flush();
2959                } else {
2960                    Buffer packetToSend;
2961 
2962                    headerPacket.setPosition(0);
2963                    packetToSend = compressPacket(headerPacket, HEADER_LENGTH,
2964                            splitSize, HEADER_LENGTH);
2965                    packetLen = packetToSend.getPosition();
2966 
2967                    this.mysqlOutput.write(packetToSend.getByteBuffer(), 0,
2968                        packetLen);
2969                    this.mysqlOutput.flush();
2970                }
2971 
2972                originalPacketPos += splitSize;
2973                len -= splitSize;
2974            }
2975 
2976            //
2977            // Write last packet
2978            //
2979            headerPacket.clear();
2980            headerPacket.setPosition(0);
2981            headerPacket.writeLongInt(len - HEADER_LENGTH);
2982            this.packetSequence++;
2983            headerPacket.writeByte(this.packetSequence);
2984 
2985            if (len != 0) {
2986                System.arraycopy(origPacketBytes, originalPacketPos,
2987                    headerPacketBytes, 4, len - HEADER_LENGTH);
2988            }
2989 
2990            int packetLen = len - HEADER_LENGTH;
2991 
2992            //
2993            // Swap a compressed packet in, if we're using
2994            // compression...
2995            //
2996            if (!this.useCompression) {
2997                this.mysqlOutput.write(headerPacket.getByteBuffer(), 0, len);
2998                this.mysqlOutput.flush();
2999            } else {
3000                Buffer packetToSend;
3001 
3002                headerPacket.setPosition(0);
3003                packetToSend = compressPacket(headerPacket, HEADER_LENGTH,
3004                        packetLen, HEADER_LENGTH);
3005                packetLen = packetToSend.getPosition();
3006 
3007                this.mysqlOutput.write(packetToSend.getByteBuffer(), 0,
3008                    packetLen);
3009                this.mysqlOutput.flush();
3010            }
3011        } catch (IOException ioEx) {
3012            throw new CommunicationsException(this.connection,
3013                this.lastPacketSentTimeMs, ioEx);
3014        }
3015    }
3016 
3017    private void reclaimLargeSharedSendPacket() {
3018        if ((this.sharedSendPacket != null) &&
3019                (this.sharedSendPacket.getCapacity() > 1048576)) {
3020            this.sharedSendPacket = new Buffer(this.connection.getNetBufferLength());
3021        }
3022    }
3023 
3024    boolean hadWarnings() {
3025            return this.hadWarnings;
3026    }
3027    
3028    void scanForAndThrowDataTruncation() throws SQLException {
3029        if ((this.streamingData == null) && versionMeetsMinimum(4, 1, 0) &&
3030                this.connection.getJdbcCompliantTruncation()) {
3031            SQLError.convertShowWarningsToSQLWarnings(this.connection,
3032                this.warningCount, true);
3033        }
3034    }
3035 
3036    /**
3037     * Secure authentication for 4.1 and newer servers.
3038     *
3039     * @param packet DOCUMENT ME!
3040     * @param packLength
3041     * @param user
3042     * @param password
3043     * @param database DOCUMENT ME!
3044     * @param writeClientParams
3045     *
3046     * @throws SQLException
3047     */
3048    private void secureAuth(Buffer packet, int packLength, String user,
3049        String password, String database, boolean writeClientParams)
3050        throws SQLException {
3051        // Passwords can be 16 chars long
3052        if (packet == null) {
3053            packet = new Buffer(packLength);
3054        }
3055 
3056        if (writeClientParams) {
3057            if (this.use41Extensions) {
3058                if (versionMeetsMinimum(4, 1, 1)) {
3059                    packet.writeLong(this.clientParam);
3060                    packet.writeLong(this.maxThreeBytes);
3061 
3062                    // charset, JDBC will connect as 'latin1',
3063                    // and use 'SET NAMES' to change to the desired
3064                    // charset after the connection is established.
3065                    packet.writeByte((byte) 8);
3066 
3067                    // Set of bytes reserved for future use.
3068                    packet.writeBytesNoNull(new byte[23]);
3069                } else {
3070                    packet.writeLong(this.clientParam);
3071                    packet.writeLong(this.maxThreeBytes);
3072                }
3073            } else {
3074                packet.writeInt((int) this.clientParam);
3075                packet.writeLongInt(this.maxThreeBytes);
3076            }
3077        }
3078 
3079        // User/Password data
3080        packet.writeString(user, "Cp1252", this.connection);
3081 
3082        if (password.length() != 0) {
3083            /* Prepare false scramble  */
3084            packet.writeString(FALSE_SCRAMBLE, "Cp1252", this.connection);
3085        } else {
3086            /* For empty password*/
3087            packet.writeString("", "Cp1252", this.connection); //$NON-NLS-1$
3088        }
3089 
3090        if (this.useConnectWithDb) {
3091            packet.writeString(database, "Cp1252", this.connection);
3092        }
3093 
3094        send(packet, packet.getPosition());
3095 
3096        //
3097        // Don't continue stages if password is empty
3098        //
3099        if (password.length() > 0) {
3100            Buffer b = readPacket();
3101 
3102            b.setPosition(0);
3103 
3104            byte[] replyAsBytes = b.getByteBuffer();
3105 
3106            if ((replyAsBytes.length == 25) && (replyAsBytes[0] != 0)) {
3107                // Old passwords will have '*' at the first byte of hash */
3108                if (replyAsBytes[0] != '*') {
3109                    try {
3110                        /* Build full password hash as it is required to decode scramble */
3111                        byte[] buff = Security.passwordHashStage1(password);
3112 
3113                        /* Store copy as we'll need it later */
3114                        byte[] passwordHash = new byte[buff.length];
3115                        System.arraycopy(buff, 0, passwordHash, 0, buff.length);
3116 
3117                        /* Finally hash complete password using hash we got from server */
3118                        passwordHash = Security.passwordHashStage2(passwordHash,
3119                                replyAsBytes);
3120 
3121                        byte[] packetDataAfterSalt = new byte[replyAsBytes.length -
3122                            5];
3123 
3124                        System.arraycopy(replyAsBytes, 4, packetDataAfterSalt,
3125                            0, replyAsBytes.length - 5);
3126 
3127                        byte[] mysqlScrambleBuff = new byte[20];
3128 
3129                        /* Decypt and store scramble 4 = hash for stage2 */
3130                        Security.passwordCrypt(packetDataAfterSalt,
3131                            mysqlScrambleBuff, passwordHash, 20);
3132 
3133                        /* Encode scramble with password. Recycle buffer */
3134                        Security.passwordCrypt(mysqlScrambleBuff, buff, buff, 20);
3135 
3136                        Buffer packet2 = new Buffer(25);
3137                        packet2.writeBytesNoNull(buff);
3138 
3139                        this.packetSequence++;
3140 
3141                        send(packet2, 24);
3142                    } catch (NoSuchAlgorithmException nse) {
3143                        throw SQLError.createSQLException(Messages.getString("MysqlIO.91") //$NON-NLS-1$
3144                             +Messages.getString("MysqlIO.92"), //$NON-NLS-1$
3145                            SQLError.SQL_STATE_GENERAL_ERROR);
3146                    }
3147                } else {
3148                    try {
3149                        /* Create password to decode scramble */
3150                        byte[] passwordHash = Security.createKeyFromOldPassword(password);
3151 
3152                        /* Decypt and store scramble 4 = hash for stage2 */
3153                        byte[] netReadPos4 = new byte[replyAsBytes.length - 5];
3154 
3155                        System.arraycopy(replyAsBytes, 4, netReadPos4, 0,
3156                            replyAsBytes.length - 5);
3157 
3158                        byte[] mysqlScrambleBuff = new byte[20];
3159 
3160                        /* Decypt and store scramble 4 = hash for stage2 */
3161                        Security.passwordCrypt(netReadPos4, mysqlScrambleBuff,
3162                            passwordHash, 20);
3163 
3164                        /* Finally scramble decoded scramble with password */
3165                        String scrambledPassword = Util.scramble(new String(
3166                                    mysqlScrambleBuff), password);
3167 
3168                        Buffer packet2 = new Buffer(packLength);
3169                        packet2.writeString(scrambledPassword, "Cp1252", this.connection);
3170                        this.packetSequence++;
3171 
3172                        send(packet2, 24);
3173                    } catch (NoSuchAlgorithmException nse) {
3174                        throw SQLError.createSQLException(Messages.getString("MysqlIO.93") //$NON-NLS-1$
3175                             +Messages.getString("MysqlIO.94"), //$NON-NLS-1$
3176                            SQLError.SQL_STATE_GENERAL_ERROR);
3177                    }
3178                }
3179            }
3180        }
3181    }
3182 
3183    /**
3184     * Secure authentication for 4.1.1 and newer servers.
3185     *
3186     * @param packet DOCUMENT ME!
3187     * @param packLength
3188     * @param user
3189     * @param password
3190     * @param database DOCUMENT ME!
3191     * @param writeClientParams
3192     *
3193     * @throws SQLException
3194     */
3195    void secureAuth411(Buffer packet, int packLength, String user,
3196        String password, String database, boolean writeClientParams)
3197        throws SQLException {
3198        //        SERVER:  public_seed=create_random_string()
3199        //                         send(public_seed)
3200        //
3201        //        CLIENT:  recv(public_seed)
3202        //                         hash_stage1=sha1("password")
3203        //                         hash_stage2=sha1(hash_stage1)
3204        //                         reply=xor(hash_stage1, sha1(public_seed,hash_stage2)
3205        //
3206        //                         // this three steps are done in scramble()
3207        //
3208        //                         send(reply)
3209        //
3210        //
3211        //        SERVER:  recv(reply)
3212        //                         hash_stage1=xor(reply, sha1(public_seed,hash_stage2))
3213        //                         candidate_hash2=sha1(hash_stage1)
3214        //                         check(candidate_hash2==hash_stage2)
3215        // Passwords can be 16 chars long
3216        if (packet == null) {
3217            packet = new Buffer(packLength);
3218        }
3219 
3220        if (writeClientParams) {
3221            if (this.use41Extensions) {
3222                if (versionMeetsMinimum(4, 1, 1)) {
3223                    packet.writeLong(this.clientParam);
3224                    packet.writeLong(this.maxThreeBytes);
3225 
3226                    // charset, JDBC will connect as 'latin1',
3227                    // and use 'SET NAMES' to change to the desired
3228                    // charset after the connection is established.
3229                    packet.writeByte((byte) 8);
3230 
3231                    // Set of bytes reserved for future use.
3232                    packet.writeBytesNoNull(new byte[23]);
3233                } else {
3234                    packet.writeLong(this.clientParam);
3235                    packet.writeLong(this.maxThreeBytes);
3236                }
3237            } else {
3238                packet.writeInt((int) this.clientParam);
3239                packet.writeLongInt(this.maxThreeBytes);
3240            }
3241        }
3242 
3243        // User/Password data
3244        packet.writeString(user);
3245 
3246        if (password.length() != 0) {
3247            packet.writeByte((byte) 0x14);
3248 
3249            try {
3250                packet.writeBytesNoNull(Security.scramble411(password, this.seed));
3251            } catch (NoSuchAlgorithmException nse) {
3252                throw SQLError.createSQLException(Messages.getString("MysqlIO.95") //$NON-NLS-1$
3253                     +Messages.getString("MysqlIO.96"), //$NON-NLS-1$
3254                    SQLError.SQL_STATE_GENERAL_ERROR);
3255            }
3256        } else {
3257            /* For empty password*/
3258            packet.writeByte((byte) 0);
3259        }
3260 
3261        if (this.useConnectWithDb) {
3262            packet.writeString(database);
3263        }
3264 
3265        send(packet, packet.getPosition());
3266 
3267        byte savePacketSequence = this.packetSequence++;
3268 
3269        Buffer reply = checkErrorPacket();
3270 
3271        if (reply.isLastDataPacket()) {
3272            /*
3273                  By sending this very specific reply server asks us to send scrambled
3274                  password in old format. The reply contains scramble_323.
3275            */
3276            this.packetSequence = ++savePacketSequence;
3277            packet.clear();
3278 
3279            String seed323 = this.seed.substring(0, 8);
3280            packet.writeString(Util.newCrypt(password, seed323));
3281            send(packet, packet.getPosition());
3282 
3283            /* Read what server thinks about out new auth message report */
3284            checkErrorPacket();
3285        }
3286    }
3287 
3288    /**
3289     * Un-packs binary-encoded result set data for one row
3290     *
3291     * @param fields
3292     * @param binaryData
3293     * @param resultSetConcurrency DOCUMENT ME!
3294     *
3295     * @return byte[][]
3296     *
3297     * @throws SQLException DOCUMENT ME!
3298     */
3299    private final Object[] unpackBinaryResultSetRow(Field[] fields,
3300        Buffer binaryData, int resultSetConcurrency) throws SQLException {
3301        int numFields = fields.length;
3302 
3303        Object[] unpackedRowData = new Object[numFields];
3304 
3305        //
3306        // Unpack the null bitmask, first
3307        //
3308 
3309        /* Reserve place for null-marker bytes */
3310        int nullCount = (numFields + 9) / 8;
3311 
3312        byte[] nullBitMask = new byte[nullCount];
3313 
3314        for (int i = 0; i < nullCount; i++) {
3315            nullBitMask[i] = binaryData.readByte();
3316        }
3317 
3318        int nullMaskPos = 0;
3319        int bit = 4; // first two bits are reserved for future use
3320       
3321        //
3322        // TODO: Benchmark if moving check for updatable result
3323        //       sets out of loop is worthwhile?
3324        //
3325        
3326        for (int i = 0; i < numFields; i++) {
3327            if ((nullBitMask[nullMaskPos] & bit) != 0) {
3328                unpackedRowData[i] = null;
3329            } else {
3330                    if (resultSetConcurrency != ResultSet.CONCUR_UPDATABLE) {
3331                            extractNativeEncodedColumn(binaryData, fields, i, 
3332                                            unpackedRowData);
3333                    } else {
3334                            unpackNativeEncodedColumn(binaryData, fields, i, 
3335                                            unpackedRowData);
3336                    }   
3337            }
3338            
3339                if (((bit <<= 1) & 255) == 0) {
3340                        bit = 1; /* To next byte */
3341 
3342                        nullMaskPos++;
3343                }
3344        }
3345 
3346        return unpackedRowData;
3347    }
3348 
3349        
3350    private final void extractNativeEncodedColumn(Buffer binaryData, 
3351                    Field[] fields, int columnIndex, Object[] unpackedRowData) throws SQLException {
3352            Field curField = fields[columnIndex];
3353            
3354            switch (curField.getMysqlType()) {
3355            case MysqlDefs.FIELD_TYPE_NULL:
3356                    break; // for dummy binds
3357            
3358            case MysqlDefs.FIELD_TYPE_TINY:
3359 
3360                    unpackedRowData[columnIndex] = new byte[] {binaryData.readByte()};
3361                    break;
3362            
3363            case MysqlDefs.FIELD_TYPE_SHORT:
3364            case MysqlDefs.FIELD_TYPE_YEAR:
3365                    
3366                    unpackedRowData[columnIndex] = binaryData.getBytes(2);
3367                    break;
3368            case MysqlDefs.FIELD_TYPE_LONG:
3369            case MysqlDefs.FIELD_TYPE_INT24:
3370                    
3371                    unpackedRowData[columnIndex] = binaryData.getBytes(4);
3372                    break;
3373            case MysqlDefs.FIELD_TYPE_LONGLONG:
3374 
3375                    unpackedRowData[columnIndex] = binaryData.getBytes(8);
3376                    break;
3377            case MysqlDefs.FIELD_TYPE_FLOAT:
3378                    
3379                    unpackedRowData[columnIndex] = binaryData.getBytes(4);
3380                    break;           
3381            case MysqlDefs.FIELD_TYPE_DOUBLE:
3382                    
3383                    unpackedRowData[columnIndex] = binaryData.getBytes(8);
3384                    break;
3385            case MysqlDefs.FIELD_TYPE_TIME:
3386                    
3387                    int length = (int) binaryData.readFieldLength();
3388            
3389                    unpackedRowData[columnIndex] = binaryData.getBytes(length);
3390 
3391                    break;
3392            case MysqlDefs.FIELD_TYPE_DATE:
3393                    
3394                    length = (int) binaryData.readFieldLength();
3395            
3396                    unpackedRowData[columnIndex] = binaryData.getBytes(length);
3397 
3398                    break;
3399            case MysqlDefs.FIELD_TYPE_DATETIME:
3400            case MysqlDefs.FIELD_TYPE_TIMESTAMP:
3401                    length = (int) binaryData.readFieldLength();
3402            
3403                    unpackedRowData[columnIndex] = binaryData.getBytes(length);
3404                    break;
3405            case MysqlDefs.FIELD_TYPE_TINY_BLOB:
3406            case MysqlDefs.FIELD_TYPE_MEDIUM_BLOB:
3407            case MysqlDefs.FIELD_TYPE_LONG_BLOB:
3408            case MysqlDefs.FIELD_TYPE_BLOB:
3409            case MysqlDefs.FIELD_TYPE_VAR_STRING:
3410            case MysqlDefs.FIELD_TYPE_VARCHAR:
3411            case MysqlDefs.FIELD_TYPE_STRING:
3412            case MysqlDefs.FIELD_TYPE_DECIMAL:
3413            case MysqlDefs.FIELD_TYPE_NEW_DECIMAL:
3414            case MysqlDefs.FIELD_TYPE_GEOMETRY:
3415                    unpackedRowData[columnIndex] = binaryData.readLenByteArray(0);
3416            
3417                    break;
3418            case MysqlDefs.FIELD_TYPE_BIT:
3419                    unpackedRowData[columnIndex] = binaryData.readLenByteArray(0);
3420                    
3421                    break;
3422            default:
3423                    throw SQLError.createSQLException(Messages.getString("MysqlIO.97") //$NON-NLS-1$
3424                                    +curField.getMysqlType() +
3425                                        Messages.getString("MysqlIO.98") + columnIndex +
3426                                        Messages.getString("MysqlIO.99") //$NON-NLS-1$ //$NON-NLS-2$
3427                                        + fields.length + Messages.getString("MysqlIO.100"), //$NON-NLS-1$
3428                                        SQLError.SQL_STATE_GENERAL_ERROR);
3429            }
3430    }
3431 
3432    private final void unpackNativeEncodedColumn(Buffer binaryData, 
3433                    Field[] fields, int columnIndex, Object[] unpackedRowData) 
3434    throws SQLException {
3435            Field curField = fields[columnIndex];
3436            
3437            switch (curField.getMysqlType()) {
3438            case MysqlDefs.FIELD_TYPE_NULL:
3439                    break; // for dummy binds
3440                    
3441            case MysqlDefs.FIELD_TYPE_TINY:
3442                    
3443                    byte tinyVal = binaryData.readByte();
3444                    
3445                    if (!curField.isUnsigned()) {                        
3446                            unpackedRowData[columnIndex] = String.valueOf(tinyVal)
3447                            .getBytes();                          
3448                    } else {
3449                            short unsignedTinyVal = (short) (tinyVal & 0xff);
3450 
3451                            unpackedRowData[columnIndex] = String.valueOf(unsignedTinyVal)
3452                            .getBytes();                           
3453                    }
3454                    
3455                    break;
3456                    
3457            case MysqlDefs.FIELD_TYPE_SHORT:
3458            case MysqlDefs.FIELD_TYPE_YEAR:
3459                    
3460                    short shortVal = (short) binaryData.readInt();
3461                    
3462                    if (!curField.isUnsigned()) {
3463                            unpackedRowData[columnIndex] = String.valueOf(shortVal)
3464                            .getBytes();        
3465                    } else {
3466                            int unsignedShortVal = shortVal & 0xffff;
3467 
3468                            unpackedRowData[columnIndex] = String.valueOf(unsignedShortVal)
3469                            .getBytes();        
3470                    }
3471                    
3472                    break;
3473                    
3474            case MysqlDefs.FIELD_TYPE_LONG:
3475            case MysqlDefs.FIELD_TYPE_INT24:
3476                    
3477                    int intVal = (int) binaryData.readLong();
3478                    
3479                    if (!curField.isUnsigned()) {
3480                            unpackedRowData[columnIndex] = String.valueOf(intVal)
3481                            .getBytes();
3482                    } else {
3483                            long longVal = intVal & 0xffffffffL;
3484 
3485                            unpackedRowData[columnIndex] = String.valueOf(longVal)
3486                            .getBytes();        
3487                    }
3488                    
3489                    break;
3490                    
3491            case MysqlDefs.FIELD_TYPE_LONGLONG:
3492                    
3493                    long longVal = binaryData.readLongLong();
3494                    
3495                    if (!curField.isUnsigned()) {
3496                            unpackedRowData[columnIndex] = String.valueOf(longVal)
3497                            .getBytes();
3498                    } else {
3499                            BigInteger asBigInteger = ResultSet.convertLongToUlong(longVal);
3500 
3501                            unpackedRowData[columnIndex] = asBigInteger.toString()
3502                            .getBytes();        
3503                    }
3504                    
3505                    break;
3506                    
3507            case MysqlDefs.FIELD_TYPE_FLOAT:
3508                    
3509                    float floatVal = Float.intBitsToFloat(binaryData.readIntAsLong());
3510                    
3511                    unpackedRowData[columnIndex] = String.valueOf(floatVal).getBytes();
3512 
3513                    break;
3514                    
3515            case MysqlDefs.FIELD_TYPE_DOUBLE:
3516                    
3517                    double doubleVal = Double.longBitsToDouble(binaryData.readLongLong());
3518 
3519                    unpackedRowData[columnIndex] = String.valueOf(doubleVal).getBytes();
3520 
3521                    break;
3522                    
3523            case MysqlDefs.FIELD_TYPE_TIME:
3524                    
3525                    int length = (int) binaryData.readFieldLength();
3526                    
3527                    int hour = 0;
3528                    int minute = 0;
3529                    int seconds = 0;
3530                    
3531                    if (length != 0) {
3532                            binaryData.readByte(); // skip tm->neg
3533                            binaryData.readLong(); // skip daysPart
3534                            hour = binaryData.readByte();
3535                            minute = binaryData.readByte();
3536                            seconds = binaryData.readByte();
3537                            
3538                            if (length > 8) {
3539                                    binaryData.readLong(); // ignore 'secondsPart'
3540                            }
3541                    }
3542                    
3543                    
3544                    byte[] timeAsBytes = new byte[8];
3545                    
3546                    timeAsBytes[0] = (byte) Character.forDigit(hour / 10, 10);
3547                    timeAsBytes[1] = (byte) Character.forDigit(hour % 10, 10);
3548                    
3549                    timeAsBytes[2] = (byte) ':';
3550                    
3551                    timeAsBytes[3] = (byte) Character.forDigit(minute / 10,
3552                                    10);
3553                    timeAsBytes[4] = (byte) Character.forDigit(minute % 10,
3554                                    10);
3555                    
3556                    timeAsBytes[5] = (byte) ':';
3557                    
3558                    timeAsBytes[6] = (byte) Character.forDigit(seconds / 10,
3559                                    10);
3560                    timeAsBytes[7] = (byte) Character.forDigit(seconds % 10,
3561                                    10);
3562                    
3563                    unpackedRowData[columnIndex] = timeAsBytes;
3564                    
3565                    
3566                    break;
3567                    
3568            case MysqlDefs.FIELD_TYPE_DATE:
3569                    length = (int) binaryData.readFieldLength();
3570                    
3571                    int year = 0;
3572                    int month = 0;
3573                    int day = 0;
3574                    
3575                    hour = 0;
3576                    minute = 0;
3577                    seconds = 0;
3578                    
3579                    if (length != 0) {
3580                            year = binaryData.readInt();
3581                            month = binaryData.readByte();
3582                            day = binaryData.readByte();
3583                    }
3584                    
3585                    if ((year == 0) && (month == 0) && (day == 0)) {
3586                            if (ConnectionProperties.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL.equals(
3587                                            this.connection.getZeroDateTimeBehavior())) {
3588                                    unpackedRowData[columnIndex] = null;
3589                                    
3590                                    break;
3591                            } else if (ConnectionProperties.ZERO_DATETIME_BEHAVIOR_EXCEPTION.equals(
3592                                            this.connection.getZeroDateTimeBehavior())) {
3593                                    throw SQLError.createSQLException("Value '0000-00-00' can not be represented as java.sql.Date",
3594                                                    SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
3595                            }
3596                            
3597                            year = 1;
3598                            month = 1;
3599                            day = 1;
3600                    }
3601                    
3602                    
3603                    byte[] dateAsBytes = new byte[10];
3604                    
3605                    dateAsBytes[0] = (byte) Character.forDigit(year / 1000,
3606                                    10);
3607                    
3608                    int after1000 = year % 1000;
3609                    
3610                    dateAsBytes[1] = (byte) Character.forDigit(after1000 / 100,
3611                                    10);
3612                    
3613                    int after100 = after1000 % 100;
3614                    
3615                    dateAsBytes[2] = (byte) Character.forDigit(after100 / 10,
3616                                    10);
3617                    dateAsBytes[3] = (byte) Character.forDigit(after100 % 10,
3618                                    10);
3619                    
3620                    dateAsBytes[4] = (byte) '-';
3621                    
3622                    dateAsBytes[5] = (byte) Character.forDigit(month / 10,
3623                                    10);
3624                    dateAsBytes[6] = (byte) Character.forDigit(month % 10,
3625                                    10);
3626                    
3627                    dateAsBytes[7] = (byte) '-';
3628                    
3629                    dateAsBytes[8] = (byte) Character.forDigit(day / 10, 10);
3630                    dateAsBytes[9] = (byte) Character.forDigit(day % 10, 10);
3631                    
3632                    unpackedRowData[columnIndex] = dateAsBytes;
3633                    
3634                    
3635                    break;
3636                    
3637            case MysqlDefs.FIELD_TYPE_DATETIME:
3638            case MysqlDefs.FIELD_TYPE_TIMESTAMP:
3639                    length = (int) binaryData.readFieldLength();
3640                    
3641                    year = 0;
3642                    month = 0;
3643                    day = 0;
3644                    
3645                    hour = 0;
3646                    minute = 0;
3647                    seconds = 0;
3648                    
3649                    int nanos = 0;
3650                    
3651                    if (length != 0) {
3652                            year = binaryData.readInt();
3653                            month = binaryData.readByte();
3654                            day = binaryData.readByte();
3655                            
3656                            if (length > 4) {
3657                                    hour = binaryData.readByte();
3658                                    minute = binaryData.readByte();
3659                                    seconds = binaryData.readByte();
3660                            }
3661                            
3662                            //if (length > 7) {
3663                            //    nanos = (int)binaryData.readLong();
3664                            //}
3665                    }
3666                    
3667                    if ((year == 0) && (month == 0) && (day == 0)) {
3668                            if (ConnectionProperties.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL.equals(
3669                                            this.connection.getZeroDateTimeBehavior())) {
3670                                    unpackedRowData[columnIndex] = null;
3671                                    
3672                                    break;
3673                            } else if (ConnectionProperties.ZERO_DATETIME_BEHAVIOR_EXCEPTION.equals(
3674                                            this.connection.getZeroDateTimeBehavior())) {
3675                                    throw SQLError.createSQLException("Value '0000-00-00' can not be represented as java.sql.Timestamp",
3676                                                    SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
3677                            }
3678                            
3679                            year = 1;
3680                            month = 1;
3681                            day = 1;
3682                    }
3683                    
3684                    
3685                    int stringLength = 19;
3686                    
3687                    byte[] nanosAsBytes = Integer.toString(nanos).getBytes();
3688                    
3689                    stringLength += (1 + nanosAsBytes.length); // '.' + # of digits
3690                    
3691                    byte[] datetimeAsBytes = new byte[stringLength];
3692                    
3693                    datetimeAsBytes[0] = (byte) Character.forDigit(year / 1000,
3694                                    10);
3695                    
3696                    after1000 = year % 1000;
3697                    
3698                    datetimeAsBytes[1] = (byte) Character.forDigit(after1000 / 100,
3699                                    10);
3700                    
3701                    after100 = after1000 % 100;
3702                    
3703                    datetimeAsBytes[2] = (byte) Character.forDigit(after100 / 10,
3704                                    10);
3705                    datetimeAsBytes[3] = (byte) Character.forDigit(after100 % 10,
3706                                    10);
3707                    
3708                    datetimeAsBytes[4] = (byte) '-';
3709                    
3710                    datetimeAsBytes[5] = (byte) Character.forDigit(month / 10,
3711                                    10);
3712                    datetimeAsBytes[6] = (byte) Character.forDigit(month % 10,
3713                                    10);
3714                    
3715                    datetimeAsBytes[7] = (byte) '-';
3716                    
3717                    datetimeAsBytes[8] = (byte) Character.forDigit(day / 10,
3718                                    10);
3719                    datetimeAsBytes[9] = (byte) Character.forDigit(day % 10,
3720                                    10);
3721                    
3722                    datetimeAsBytes[10] = (byte) ' ';
3723                    
3724                    datetimeAsBytes[11] = (byte) Character.forDigit(hour / 10,
3725                                    10);
3726                    datetimeAsBytes[12] = (byte) Character.forDigit(hour % 10,
3727                                    10);
3728                    
3729                    datetimeAsBytes[13] = (byte) ':';
3730                    
3731                    datetimeAsBytes[14] = (byte) Character.forDigit(minute / 10,
3732                                    10);
3733                    datetimeAsBytes[15] = (byte) Character.forDigit(minute % 10,
3734                                    10);
3735                    
3736                    datetimeAsBytes[16] = (byte) ':';
3737                    
3738                    datetimeAsBytes[17] = (byte) Character.forDigit(seconds / 10,
3739                                    10);
3740                    datetimeAsBytes[18] = (byte) Character.forDigit(seconds % 10,
3741                                    10);
3742                    
3743                    datetimeAsBytes[19] = (byte) '.';
3744                    
3745                    int nanosOffset = 20;
3746                    
3747                    for (int j = 0; j < nanosAsBytes.length; j++) {
3748                            datetimeAsBytes[nanosOffset + j] = nanosAsBytes[j];
3749                    }
3750                    
3751                    unpackedRowData[columnIndex] = datetimeAsBytes;
3752                    
3753                    
3754                    break;
3755                    
3756            case MysqlDefs.FIELD_TYPE_TINY_BLOB:
3757            case MysqlDefs.FIELD_TYPE_MEDIUM_BLOB:
3758            case MysqlDefs.FIELD_TYPE_LONG_BLOB:
3759            case MysqlDefs.FIELD_TYPE_BLOB:
3760            case MysqlDefs.FIELD_TYPE_VAR_STRING:
3761            case MysqlDefs.FIELD_TYPE_STRING:
3762            case MysqlDefs.FIELD_TYPE_VARCHAR:
3763            case MysqlDefs.FIELD_TYPE_DECIMAL:
3764            case MysqlDefs.FIELD_TYPE_NEW_DECIMAL:
3765            case MysqlDefs.FIELD_TYPE_BIT:
3766                    unpackedRowData[columnIndex] = binaryData.readLenByteArray(0);
3767                    
3768                    break;
3769                    
3770            default:
3771                    throw SQLError.createSQLException(Messages.getString("MysqlIO.97") //$NON-NLS-1$
3772                                    +curField.getMysqlType() +
3773                                    Messages.getString("MysqlIO.98") + columnIndex +
3774                                    Messages.getString("MysqlIO.99") //$NON-NLS-1$ //$NON-NLS-2$
3775                                    + fields.length + Messages.getString("MysqlIO.100"), //$NON-NLS-1$
3776                                    SQLError.SQL_STATE_GENERAL_ERROR);
3777            }
3778    }
3779    
3780    /**
3781     * Optimization to only use one calendar per-session, or calculate it
3782     * for each call, depending on user configuration
3783     */
3784    private Calendar getCalendarInstanceForSessionOrNew() {
3785            if (this.connection.getDynamicCalendars()) {
3786                    return Calendar.getInstance();
3787            } else {
3788                    return this.sessionCalendar;
3789            }
3790    }
3791    
3792    /**
3793     * Negotiates the SSL communications channel used when connecting
3794     * to a MySQL server that understands SSL.
3795     *
3796     * @param user
3797     * @param password
3798     * @param database
3799     * @param packLength
3800     * @throws SQLException
3801     * @throws CommunicationsException
3802     */
3803    private void negotiateSSLConnection(String user, String password,
3804        String database, int packLength)
3805        throws SQLException, CommunicationsException {
3806        if (!ExportControlled.enabled()) {
3807            throw new ConnectionFeatureNotAvailableException(this.connection,
3808                this.lastPacketSentTimeMs, null);
3809        }
3810 
3811        boolean doSecureAuth = false;
3812 
3813        if ((this.serverCapabilities & CLIENT_SECURE_CONNECTION) != 0) {
3814            this.clientParam |= CLIENT_SECURE_CONNECTION;
3815            doSecureAuth = true;
3816        }
3817 
3818        this.clientParam |= CLIENT_SSL;
3819 
3820        Buffer packet = new Buffer(packLength);
3821 
3822        if ((this.clientParam & CLIENT_RESERVED) != 0) {
3823            packet.writeLong(this.clientParam);
3824        } else {
3825            packet.writeInt((int) this.clientParam);
3826        }
3827 
3828        send(packet, packet.getPosition());
3829 
3830        ExportControlled.transformSocketToSSLSocket(this);
3831 
3832        packet.clear();
3833 
3834        if (doSecureAuth) {
3835            if (versionMeetsMinimum(4, 1, 1)) {
3836                secureAuth411(null, packLength, user, password, database, true);
3837            } else {
3838                secureAuth411(null, packLength, user, password, database, true);
3839            }
3840        } else {
3841            if ((this.clientParam & CLIENT_RESERVED) != 0) {
3842                packet.writeLong(this.clientParam);
3843                packet.writeLong(this.maxThreeBytes);
3844            } else {
3845                packet.writeInt((int) this.clientParam);
3846                packet.writeLongInt(this.maxThreeBytes);
3847            }
3848 
3849            // User/Password data
3850            packet.writeString(user);
3851 
3852            if (this.protocolVersion > 9) {
3853                packet.writeString(Util.newCrypt(password, this.seed));
3854            } else {
3855                packet.writeString(Util.oldCrypt(password, this.seed));
3856            }
3857 
3858            if (((this.serverCapabilities & CLIENT_CONNECT_WITH_DB) != 0) &&
3859                    (database != null) && (database.length() > 0)) {
3860                packet.writeString(database);
3861            }
3862 
3863            send(packet, packet.getPosition());
3864        }
3865    }
3866 
3867        protected int getServerStatus() {
3868                return serverStatus;
3869        }
3870 
3871        protected List fetchRowsViaCursor(List fetchedRows, long statementId,
3872                        Field[] columnTypes, int fetchSize) throws SQLException {
3873                
3874                if (fetchedRows == null) {
3875                        fetchedRows = new ArrayList(fetchSize);
3876                } else {
3877                        fetchedRows.clear();
3878                }
3879        
3880                this.sharedSendPacket.clear();
3881        
3882                this.sharedSendPacket.writeByte((byte) MysqlDefs.COM_FETCH);
3883                this.sharedSendPacket.writeLong(statementId);
3884                this.sharedSendPacket.writeLong(fetchSize);
3885        
3886                sendCommand(MysqlDefs.COM_FETCH, null, this.sharedSendPacket, true,
3887                                null);
3888        
3889                Object[] row = null;
3890        
3891                while ((row = nextRow(columnTypes, columnTypes.length, true,
3892                                ResultSet.CONCUR_READ_ONLY)) != null) {
3893                        fetchedRows.add(row);
3894                }
3895        
3896                return fetchedRows;
3897        }
3898 
3899        protected long getThreadId() {
3900                return threadId;
3901        }
3902}

[all classes][com.mysql.jdbc]
EMMA 2.0.4217 (C) Vladimir Roubtsov