EMMA Coverage Report (generated Fri May 26 15:35:26 CDT 2006)
[all classes][com.mysql.jdbc]

COVERAGE SUMMARY FOR SOURCE FILE [MysqlIO.java]

nameclass, %method, %block, %line, %
MysqlIO.java100% (1/1)85%  (62/73)62%  (4737/7697)65%  (1100.3/1696)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class MysqlIO100% (1/1)85%  (62/73)62%  (4737/7697)65%  (1100.3/1696)
changeDatabaseTo (String): void 0%   (0/1)0%   (0/54)0%   (0/10)
checkPacketSequencing (byte): void 0%   (0/1)0%   (0/91)0%   (0/7)
compressPacket (Buffer, int, int, int): Buffer 0%   (0/1)0%   (0/100)0%   (0/32)
dumpPacketRingBuffer (): void 0%   (0/1)0%   (0/55)0%   (0/10)
enqueuePacketForDebugging (boolean, boolean, int, byte [], Buffer): void 0%   (0/1)0%   (0/172)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/23)0%   (0/5)
negotiateSSLConnection (String, String, String, int): void 0%   (0/1)0%   (0/150)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/253)0%   (0/56)
createSocketFactory (): SocketFactory 100% (1/1)19%  (9/47)40%  (2/5)
getPacketDumpToLog (Buffer, int): String 100% (1/1)20%  (7/35)25%  (2/8)
isSetNeededForAutoCommitMode (boolean): boolean 100% (1/1)22%  (9/41)29%  (2/7)
closeStreamer (RowData): void 100% (1/1)22%  (11/49)67%  (4/6)
reuseAndReadViaChannel (Buffer): Buffer 100% (1/1)24%  (89/371)24%  (23/95)
unpackNativeEncodedColumn (Buffer, Field [], int, Object []): void 100% (1/1)40%  (281/700)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)54%  (76/141)62%  (21/34)
clearInputStream (): void 100% (1/1)56%  (50/90)68%  (15.7/23)
checkErrorPacket (int): Buffer 100% (1/1)56%  (118/212)66%  (31/47)
forceClose (): void 100% (1/1)65%  (22/34)62%  (10/16)
send (Buffer, int): void 100% (1/1)68%  (126/184)73%  (30/41)
sendSplitPacketsViaChannel (Buffer): void 100% (1/1)69%  (152/220)72%  (36.7/51)
readPacket (): Buffer 100% (1/1)70%  (150/215)71%  (28.5/40)
sendSplitPackets (Buffer): void 100% (1/1)70%  (142/202)73%  (33.7/46)
sqlQueryDirect (Statement, String, String, Buffer, int, Connection, int, int,... 100% (1/1)71%  (343/484)79%  (66/83)
doHandshake (String, String, String): void 100% (1/1)71%  (460/644)70%  (98/139)
reuseAndReadPacket (Buffer): Buffer 100% (1/1)72%  (380/529)77%  (76/99)
buildResultSetWithUpdates (Statement, Buffer): ResultSet 100% (1/1)72%  (79/109)83%  (21.7/26)
sendCommand (int, String, Buffer, boolean, String): Buffer 100% (1/1)75%  (143/190)79%  (37/47)
checkForCharsetMismatch (): void 100% (1/1)76%  (22/29)75%  (6/8)
extractNativeEncodedColumn (Buffer, Field [], int, Object []): void 100% (1/1)77%  (101/131)93%  (28/30)
<static initializer> 100% (1/1)84%  (26/31)88%  (11.4/13)
changeUser (String, String, String): void 100% (1/1)84%  (98/116)89%  (23/26)
readChannelFully (ByteBuffer, int): void 100% (1/1)85%  (22/26)88%  (7/8)
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)
sendFileToServer (Statement, String): ResultSet 100% (1/1)89%  (229/257)93%  (51/55)
explainSlowQuery (byte [], String): void 100% (1/1)89%  (59/66)88%  (14.9/17)
unpackField (Buffer, boolean): Field 100% (1/1)90%  (224/249)89%  (51/57)
MysqlIO (String, int, Properties, String, Connection, int): void 100% (1/1)91%  (302/332)92%  (77/84)
readViaChannel (): Buffer 100% (1/1)91%  (81/89)90%  (19/21)
readAllResults (Statement, int, int, int, boolean, String, Buffer, boolean, l... 100% (1/1)92%  (106/115)91%  (20/22)
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)
versionMeetsMinimum (int, int, int): boolean 100% (1/1)97%  (31/32)99%  (8.9/9)
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% (38/38)100% (6/6)
disableMultiQueries (): void 100% (1/1)100% (20/20)100% (6/6)
enableMultiQueries (): void 100% (1/1)100% (20/20)100% (6/6)
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% (90/90)100% (20/20)
getServerMajorVersion (): int 100% (1/1)100% (3/3)100% (1/1)
getServerMinorVersion (): 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% (24/24)100% (5/5)
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% (17/17)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)
send (Buffer): void 100% (1/1)100% (14/14)100% (5/5)
sendViaChannel (Buffer, int): void 100% (1/1)100% (66/66)100% (16/16)
unpackBinaryResultSetRow (Field [], Buffer, int): Object [] 100% (1/1)100% (78/78)100% (18/18)

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

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