~pbms-core/pbms/5.11-beta

« back to all changes in this revision

Viewing changes to mybs/java/src/com/mysql/jdbc/MysqlIO.java

  • Committer: paul-mccullagh
  • Date: 2008-03-26 11:35:17 UTC
  • Revision ID: paul-mccullagh-afb1610c21464a577ae428d72fc725eb986c05a5
Initial import

Show diffs side-by-side

added added

removed removed

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