2
Copyright (C) 2002-2007 MySQL AB
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.
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.
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.
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
25
package com.mysql.jdbc;
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;
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;
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;
59
* This class is used by Connection for communicating with the MySQL server.
61
* @author Mark Matthews
62
* @version $Id: MysqlIO.java 6483 2007-07-18 03:19:18Z mmatthews $
64
* @see java.sql.Connection
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
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
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
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;
104
* We store the platform 'encoding' here, only used to avoid munging
105
* filenames for LOAD DATA LOCAL INFILE...
107
private static String jvmPlatformCharset = null;
110
* Are we using packed or unpacked binary result set rows?
112
private boolean binaryResultsAreUnpacked = true;
115
* We need to have a 'marker' for all-zero datetimes so that ResultSet
116
* can decide what to do based on connection setting
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";
122
OutputStreamWriter outWriter = null;
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?)
130
outWriter = new OutputStreamWriter(new ByteArrayOutputStream());
131
jvmPlatformCharset = outWriter.getEncoding();
134
if (outWriter != null) {
137
} catch (IOException ioEx) {
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;
149
// Use this when reading in rows to avoid thousands of new()
150
// calls, because the byte arrays just get copied out of the
153
private Buffer reusablePacket = null;
154
private Buffer sendPacket = null;
155
private Buffer sharedSendPacket = null;
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;
165
/** The connection to the server */
166
protected Socket mysqlConnection = null;
167
private SocketFactory socketFactory = null;
170
// Packet used for 'LOAD DATA LOCAL INFILE'
172
// We use a SoftReference, so that we don't penalize intermittent
173
// use of this feature
175
private SoftReference loadFileBufRef;
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
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;
192
/** Does the server support long column info? */
193
private boolean hasLongColumnInfo = false;
194
private boolean isInteractiveClient = false;
195
private boolean logSlowQueries = false;
198
* Does the character set of this connection match the character set of the
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;
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;
238
* Constructor: Connect to the MySQL server and setup a stream connection.
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
248
* @throws IOException if an IOException occurs during connect.
249
* @throws SQLException if a database access error occurs.
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;
256
if (this.connection.getEnablePacketDebug()) {
257
this.packetDebugRingBuffer = new LinkedList();
260
this.logSlowQueries = this.connection.getLogSlowQueries();
262
this.reusablePacket = new Buffer(INITIAL_PACKET_SIZE);
263
this.sendPacket = new Buffer(INITIAL_PACKET_SIZE);
268
this.socketFactoryClassName = socketFactoryClassName;
269
this.socketFactory = createSocketFactory();
271
this.mysqlConnection = this.socketFactory.connect(this.host, this.port,
274
if (socketTimeout != 0) {
276
this.mysqlConnection.setSoTimeout(socketTimeout);
277
} catch (Exception ex) {
278
/* Ignore if the platform does not support it */
283
this.mysqlConnection = this.socketFactory.beforeHandshake();
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();
292
this.mysqlInput = new BufferedInputStream(this.mysqlConnection
293
.getInputStream(), 16384);
296
this.mysqlOutput = new BufferedOutputStream(this.mysqlConnection
297
.getOutputStream(), 16384);
299
this.isInteractiveClient = this.connection.getInteractiveClient();
300
this.profileSql = this.connection.getProfileSql();
301
this.sessionCalendar = Calendar.getInstance();
302
this.autoGenerateTestcaseScript = this.connection
303
.getAutoGenerateTestcaseScript();
305
this.needToGrabQueryFromPacket = (this.profileSql
306
|| this.logSlowQueries || this.autoGenerateTestcaseScript);
308
if (this.connection.getUseNanosForElapsedTime()
309
&& Util.nanoTimeAvailable()) {
310
this.useNanosForElapsedTime = true;
312
this.queryTimingUnits = Messages.getString("Nanoseconds");
314
this.queryTimingUnits = Messages.getString("Milliseconds");
317
if (this.connection.getLogSlowQueries()) {
318
calculateSlowQueryThreshold();
323
* Does the server send back extra column info?
327
public boolean hasLongColumnInfo() {
328
return this.hasLongColumnInfo;
331
protected boolean isDataAvailable() throws SQLException {
333
return this.mysqlInput.available() > 0;
334
} catch (IOException ioEx) {
335
throw new CommunicationsException(this.connection,
336
this.lastPacketSentTimeMs, ioEx);
343
* @return Returns the lastPacketSentTimeMs.
345
protected long getLastPacketSentTimeMs() {
346
return this.lastPacketSentTimeMs;
350
* Build a result set. Delegates to buildResultSetWithRows() to build a
351
* JDBC-version-specific ResultSet, given rows as byte data, and field
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
360
* @param streamResults should the result set be read all at once, or
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)?
366
* @return a result set
368
* @throws SQLException if a database access error occurs
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;
378
// Read in the column information
380
if (unpackFieldInfo) {
381
fields = new Field[(int) columnCount];
383
for (int i = 0; i < columnCount; i++) {
384
Buffer fieldPacket = null;
386
fieldPacket = readPacket();
387
fields[i] = unpackField(fieldPacket, false);
390
for (int i = 0; i < columnCount; i++) {
394
//this.reusablePacket.clear();
397
packet = reuseAndReadPacket(this.reusablePacket);
399
readServerStatusForResultSets(packet);
402
// Handle cursor-based fetch first
405
if (this.connection.versionMeetsMinimum(5, 0, 2)
406
&& this.connection.getUseCursorFetch()
408
&& callingStatement != null
409
&& callingStatement.getFetchSize() != 0
410
&& callingStatement.getResultSetType() == ResultSet.TYPE_FORWARD_ONLY) {
411
ServerPreparedStatement prepStmt = (com.mysql.jdbc.ServerPreparedStatement) callingStatement;
413
Field[] fieldMetadata = ((com.mysql.jdbc.ResultSetMetaData) prepStmt.getMetaData()).fields;
415
boolean usingCursor = true;
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
423
if (this.connection.versionMeetsMinimum(5, 0, 5)) {
424
usingCursor = (this.serverStatus &
425
SERVER_STATUS_CURSOR_EXISTS) != 0;
429
RowData rows = new CursorRowProvider(
434
ResultSet rs = buildResultSetWithRows(
438
rows, resultSetType, resultSetConcurrency, isBinaryEncoded);
441
rs.setFetchSize(callingStatement.getFetchSize());
448
RowData rowData = null;
450
if (!streamResults) {
451
rowData = readSingleRowSet(columnCount, maxRows,
452
resultSetConcurrency, isBinaryEncoded, unpackFieldInfo ? fields : metadataFromCache);
454
rowData = new RowDataDynamic(this, (int) columnCount, unpackFieldInfo ? fields : metadataFromCache,
456
this.streamingData = rowData;
459
ResultSet rs = buildResultSetWithRows(callingStatement, catalog, fields,
460
rowData, resultSetType, resultSetConcurrency, isBinaryEncoded);
468
* Forcibly closes the underlying socket to MySQL.
470
protected final void forceClose() {
472
if (this.mysqlInput != null) {
473
this.mysqlInput.close();
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;
482
if (this.mysqlOutput != null) {
483
this.mysqlOutput.close();
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;
492
if (this.mysqlConnection != null) {
493
this.mysqlConnection.close();
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;
503
* Reads and discards a single MySQL packet from the input stream.
505
* @throws SQLException if the network fails while skipping the
508
protected final void skipPacket() throws SQLException {
511
int lengthRead = readFully(this.mysqlInput, this.packetHeaderBuf,
514
if (lengthRead < 4) {
516
throw new IOException(Messages.getString("MysqlIO.1")); //$NON-NLS-1$
519
int packetLength = (this.packetHeaderBuf[0] & 0xff)
520
+ ((this.packetHeaderBuf[1] & 0xff) << 8)
521
+ ((this.packetHeaderBuf[2] & 0xff) << 16);
523
if (this.traceProtocol) {
524
StringBuffer traceMessageBuf = new StringBuffer();
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));
532
this.connection.getLog().logTrace(traceMessageBuf.toString());
535
byte multiPacketSeq = this.packetHeaderBuf[3];
537
if (!this.packetSequenceReset) {
538
if (this.enablePacketDebug && this.checkPacketSequence) {
539
checkPacketSequencing(multiPacketSeq);
542
this.packetSequenceReset = false;
545
this.readPacketSequence = multiPacketSeq;
547
skipFully(this.mysqlInput, packetLength);
548
} catch (IOException ioEx) {
549
throw new CommunicationsException(this.connection,
550
this.lastPacketSentTimeMs, ioEx);
551
} catch (OutOfMemoryError oom) {
553
this.connection.realClose(false, false, true, oom);
561
* Read one packet from the MySQL server
563
* @return the packet from the server.
565
* @throws SQLException
567
* @throws CommunicationsException
570
protected final Buffer readPacket() throws SQLException {
573
int lengthRead = readFully(this.mysqlInput,
574
this.packetHeaderBuf, 0, 4);
576
if (lengthRead < 4) {
578
throw new IOException(Messages.getString("MysqlIO.1")); //$NON-NLS-1$
581
int packetLength = (this.packetHeaderBuf[0] & 0xff) +
582
((this.packetHeaderBuf[1] & 0xff) << 8) +
583
((this.packetHeaderBuf[2] & 0xff) << 16);
585
if (this.traceProtocol) {
586
StringBuffer traceMessageBuf = new StringBuffer();
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));
594
this.connection.getLog().logTrace(traceMessageBuf.toString());
597
byte multiPacketSeq = this.packetHeaderBuf[3];
599
if (!this.packetSequenceReset) {
600
if (this.enablePacketDebug && this.checkPacketSequence) {
601
checkPacketSequencing(multiPacketSeq);
604
this.packetSequenceReset = false;
607
this.readPacketSequence = multiPacketSeq;
610
byte[] buffer = new byte[packetLength + 1];
611
int numBytesRead = readFully(this.mysqlInput, buffer, 0,
614
if (numBytesRead != packetLength) {
615
throw new IOException("Short read, expected " +
616
packetLength + " bytes, only read " + numBytesRead);
619
buffer[packetLength] = 0;
621
Buffer packet = new Buffer(buffer);
622
packet.setBufLength(packetLength + 1);
624
if (this.traceProtocol) {
625
StringBuffer traceMessageBuf = new StringBuffer();
627
traceMessageBuf.append(Messages.getString("MysqlIO.4")); //$NON-NLS-1$
628
traceMessageBuf.append(getPacketDumpToLog(packet,
631
this.connection.getLog().logTrace(traceMessageBuf.toString());
634
if (this.enablePacketDebug) {
635
enqueuePacketForDebugging(false, false, 0,
636
this.packetHeaderBuf, packet);
640
} catch (IOException ioEx) {
641
throw new CommunicationsException(this.connection,
642
this.lastPacketSentTimeMs, ioEx);
643
} catch (OutOfMemoryError oom) {
645
this.connection.realClose(false, false, true, oom);
653
* Unpacks the Field information from the given packet. Understands pre 4.1
654
* and post 4.1 server version field packet structures.
656
* @param packet the packet containing the field information
657
* @param extractDefaultValues should default values be extracted?
659
* @return the unpacked field
661
* @throws SQLException DOCUMENT ME!
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);
675
int databaseNameStart = packet.getPosition() + 1;
676
int databaseNameLength = packet.fastSkipLenString();
677
databaseNameStart = adjustStartForFieldLength(databaseNameStart, databaseNameLength);
679
int tableNameStart = packet.getPosition() + 1;
680
int tableNameLength = packet.fastSkipLenString();
681
tableNameStart = adjustStartForFieldLength(tableNameStart, tableNameLength);
683
// orgTableName is never used so skip
684
int originalTableNameStart = packet.getPosition() + 1;
685
int originalTableNameLength = packet.fastSkipLenString();
686
originalTableNameStart = adjustStartForFieldLength(originalTableNameStart, originalTableNameLength);
688
// we only store the position again...
689
int nameStart = packet.getPosition() + 1;
690
int nameLength = packet.fastSkipLenString();
692
nameStart = adjustStartForFieldLength(nameStart, nameLength);
694
// orgColName is not required so skip...
695
int originalColumnNameStart = packet.getPosition() + 1;
696
int originalColumnNameLength = packet.fastSkipLenString();
697
originalColumnNameStart = adjustStartForFieldLength(originalColumnNameStart, originalColumnNameLength);
701
short charSetNumber = (short) packet.readInt();
705
if (this.has41NewNewProt) {
706
colLength = packet.readLong();
708
colLength = packet.readLongInt();
711
int colType = packet.readByte() & 0xff;
715
if (this.hasLongColumnInfo) {
716
colFlag = (short) packet.readInt();
718
colFlag = (short) (packet.readByte() & 0xff);
721
int colDecimals = packet.readByte() & 0xff;
723
int defaultValueStart = -1;
724
int defaultValueLength = -1;
726
if (extractDefaultValues) {
727
defaultValueStart = packet.getPosition() + 1;
728
defaultValueLength = packet.fastSkipLenString();
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);
742
int tableNameStart = packet.getPosition() + 1;
743
int tableNameLength = packet.fastSkipLenString();
744
tableNameStart = adjustStartForFieldLength(tableNameStart, tableNameLength);
746
int nameStart = packet.getPosition() + 1;
747
int nameLength = packet.fastSkipLenString();
748
nameStart = adjustStartForFieldLength(nameStart, nameLength);
750
int colLength = packet.readnBytes();
751
int colType = packet.readnBytes();
752
packet.readByte(); // We know it's currently 2
756
if (this.hasLongColumnInfo) {
757
colFlag = (short) (packet.readInt());
759
colFlag = (short) (packet.readByte() & 0xff);
762
int colDecimals = (packet.readByte() & 0xff);
764
if (this.colDecimalNeedsBump) {
768
Field field = new Field(this.connection, packet.getByteBuffer(),
769
nameStart, nameLength, tableNameStart, tableNameLength,
770
colLength, colType, colFlag, colDecimals);
775
private int adjustStartForFieldLength(int nameStart, int nameLength) {
776
if (nameLength < 251) {
780
if (nameLength >= 251 && nameLength < 65536) {
781
return nameStart + 2;
784
if (nameLength >= 65536 && nameLength < 16777216) {
785
return nameStart + 3;
788
return nameStart + 8;
791
protected boolean isSetNeededForAutoCommitMode(boolean autoCommitFlag) {
792
if (this.use41Extensions && this.connection.getElideSetAutoCommits()) {
793
boolean autoCommitModeOnServer = ((this.serverStatus &
794
SERVER_STATUS_AUTOCOMMIT) != 0);
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);
803
return !inTransactionOnServer;
806
return autoCommitModeOnServer != autoCommitFlag;
812
protected boolean inTransactionOnServer() {
813
return (this.serverStatus & SERVER_STATUS_IN_TRANS) != 0;
817
* Re-authenticates as the given user and password
819
* @param userName DOCUMENT ME!
820
* @param password DOCUMENT ME!
821
* @param database DOCUMENT ME!
823
* @throws SQLException DOCUMENT ME!
825
protected void changeUser(String userName, String password, String database)
826
throws SQLException {
827
this.packetSequence = -1;
829
int passwordLength = 16;
830
int userLength = (userName != null) ? userName.length() : 0;
831
int databaseLength = (database != null) ? database.length() : 0;
833
int packLength = ((userLength + passwordLength + databaseLength) * 2) + 7 + HEADER_LENGTH + AUTH_411_OVERHEAD;
835
if ((this.serverCapabilities & CLIENT_SECURE_CONNECTION) != 0) {
836
Buffer changeUserPacket = new Buffer(packLength + 1);
837
changeUserPacket.writeByte((byte) MysqlDefs.COM_CHANGE_USER);
839
if (versionMeetsMinimum(4, 1, 1)) {
840
secureAuth411(changeUserPacket, packLength, userName, password,
843
secureAuth(changeUserPacket, packLength, userName, password,
847
// Passwords can be 16 chars long
848
Buffer packet = new Buffer(packLength);
849
packet.writeByte((byte) MysqlDefs.COM_CHANGE_USER);
851
// User/Password data
852
packet.writeString(userName);
854
if (this.protocolVersion > 9) {
855
packet.writeString(Util.newCrypt(password, this.seed));
857
packet.writeString(Util.oldCrypt(password, this.seed));
860
boolean localUseConnectWithDb = this.useConnectWithDb &&
861
(database != null && database.length() > 0);
863
if (localUseConnectWithDb) {
864
packet.writeString(database);
867
send(packet, packet.getPosition());
870
if (!localUseConnectWithDb) {
871
changeDatabaseTo(database);
877
* Checks for errors in the reply packet, and if none, returns the reply
878
* packet, ready for reading
880
* @return a packet ready for reading.
882
* @throws SQLException is the packet is an error packet
884
protected Buffer checkErrorPacket() throws SQLException {
885
return checkErrorPacket(-1);
889
* Determines if the database charset is the same as the platform charset
891
protected void checkForCharsetMismatch() {
892
if (this.connection.getUseUnicode() &&
893
(this.connection.getEncoding() != null)) {
894
String encodingToCheck = jvmPlatformCharset;
896
if (encodingToCheck == null) {
897
encodingToCheck = System.getProperty("file.encoding"); //$NON-NLS-1$
900
if (encodingToCheck == null) {
901
this.platformDbCharsetMatches = false;
903
this.platformDbCharsetMatches = encodingToCheck.equals(this.connection.getEncoding());
908
protected void clearInputStream() throws SQLException {
911
int len = this.mysqlInput.available();
914
this.mysqlInput.skip(len);
915
len = this.mysqlInput.available();
917
} catch (IOException ioEx) {
918
throw new CommunicationsException(this.connection,
919
this.lastPacketSentTimeMs, ioEx);
923
protected void resetReadPacketSequence() {
924
this.readPacketSequence = 0;
927
protected void dumpPacketRingBuffer() throws SQLException {
928
if ((this.packetDebugRingBuffer != null) &&
929
this.connection.getEnablePacketDebug()) {
930
StringBuffer dumpBuffer = new StringBuffer();
932
dumpBuffer.append("Last " + this.packetDebugRingBuffer.size() +
933
" packets received from server, from oldest->newest:\n");
934
dumpBuffer.append("\n");
936
for (Iterator ringBufIter = this.packetDebugRingBuffer.iterator();
937
ringBufIter.hasNext();) {
938
dumpBuffer.append((StringBuffer) ringBufIter.next());
939
dumpBuffer.append("\n");
942
this.connection.getLog().logTrace(dumpBuffer.toString());
947
* Runs an 'EXPLAIN' on the given query and dumps the results to the log
949
* @param querySQL DOCUMENT ME!
950
* @param truncatedQuery DOCUMENT ME!
952
* @throws SQLException DOCUMENT ME!
954
protected void explainSlowQuery(byte[] querySQL, String truncatedQuery)
955
throws SQLException {
956
if (StringUtils.startsWithIgnoreCaseAndWs(truncatedQuery, "SELECT")) { //$NON-NLS-1$
958
PreparedStatement stmt = null;
959
java.sql.ResultSet rs = null;
962
stmt = this.connection.clientPrepareStatement("EXPLAIN ?"); //$NON-NLS-1$
963
stmt.setBytesNoEscapeNoQuotes(1, querySQL);
964
rs = stmt.executeQuery();
966
StringBuffer explainResults = new StringBuffer(Messages.getString(
967
"MysqlIO.8") + truncatedQuery //$NON-NLS-1$
968
+Messages.getString("MysqlIO.9")); //$NON-NLS-1$
970
ResultSetUtil.appendResultSetSlashGStyle(explainResults, rs);
972
this.connection.getLog().logWarn(explainResults.toString());
973
} catch (SQLException sqlEx) {
987
static int getMaxBuf() {
988
return maxBufferSize;
992
* Get the major version of the MySQL server we are talking to.
994
* @return DOCUMENT ME!
996
final int getServerMajorVersion() {
997
return this.serverMajorVersion;
1001
* Get the minor version of the MySQL server we are talking to.
1003
* @return DOCUMENT ME!
1005
final int getServerMinorVersion() {
1006
return this.serverMinorVersion;
1010
* Get the sub-minor version of the MySQL server we are talking to.
1012
* @return DOCUMENT ME!
1014
final int getServerSubMinorVersion() {
1015
return this.serverSubMinorVersion;
1019
* Get the version string of the server we are talking to
1021
* @return DOCUMENT ME!
1023
String getServerVersion() {
1024
return this.serverVersion;
1028
* Initialize communications with the MySQL server. Handles logging on, and
1029
* handling initial connection errors.
1031
* @param user DOCUMENT ME!
1032
* @param password DOCUMENT ME!
1033
* @param database DOCUMENT ME!
1035
* @throws SQLException DOCUMENT ME!
1036
* @throws CommunicationsException DOCUMENT ME!
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;
1044
Buffer buf = readPacket();
1046
// Get the protocol version
1047
this.protocolVersion = buf.readByte();
1049
if (this.protocolVersion == -1) {
1051
this.mysqlConnection.close();
1052
} catch (Exception e) {
1058
errno = buf.readInt();
1060
String serverErrorMessage = buf.readString();
1062
StringBuffer errorBuf = new StringBuffer(Messages.getString(
1063
"MysqlIO.10")); //$NON-NLS-1$
1064
errorBuf.append(serverErrorMessage);
1065
errorBuf.append("\""); //$NON-NLS-1$
1067
String xOpen = SQLError.mysqlToSqlState(errno,
1068
this.connection.getUseSqlStateCodes());
1070
throw SQLError.createSQLException(SQLError.get(xOpen) + ", " //$NON-NLS-1$
1071
+errorBuf.toString(), xOpen, errno);
1074
this.serverVersion = buf.readString();
1076
// Parse the server version into major/minor/subminor
1077
int point = this.serverVersion.indexOf("."); //$NON-NLS-1$
1081
int n = Integer.parseInt(this.serverVersion.substring(0, point));
1082
this.serverMajorVersion = n;
1083
} catch (NumberFormatException NFE1) {
1087
String remaining = this.serverVersion.substring(point + 1,
1088
this.serverVersion.length());
1089
point = remaining.indexOf("."); //$NON-NLS-1$
1093
int n = Integer.parseInt(remaining.substring(0, point));
1094
this.serverMinorVersion = n;
1095
} catch (NumberFormatException nfe) {
1099
remaining = remaining.substring(point + 1, remaining.length());
1103
while (pos < remaining.length()) {
1104
if ((remaining.charAt(pos) < '0') ||
1105
(remaining.charAt(pos) > '9')) {
1113
int n = Integer.parseInt(remaining.substring(0, pos));
1114
this.serverSubMinorVersion = n;
1115
} catch (NumberFormatException nfe) {
1121
if (versionMeetsMinimum(4, 0, 8)) {
1122
this.maxThreeBytes = (256 * 256 * 256) - 1;
1123
this.useNewLargePackets = true;
1125
this.maxThreeBytes = 255 * 255 * 255;
1126
this.useNewLargePackets = false;
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);
1133
threadId = buf.readLong();
1134
this.seed = buf.readString();
1136
this.serverCapabilities = 0;
1138
if (buf.getPosition() < buf.getBufLength()) {
1139
this.serverCapabilities = buf.readInt();
1142
if (versionMeetsMinimum(4, 1, 1)) {
1143
int position = buf.getPosition();
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);
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();
1157
if (((this.serverCapabilities & CLIENT_COMPRESS) != 0) &&
1158
this.connection.getUseCompression()) {
1159
this.clientParam |= CLIENT_COMPRESS;
1162
this.useConnectWithDb = (database != null) &&
1163
(database.length() > 0) &&
1164
!this.connection.getCreateDatabaseIfNotExist();
1166
if (this.useConnectWithDb) {
1167
this.clientParam |= CLIENT_CONNECT_WITH_DB;
1170
if (((this.serverCapabilities & CLIENT_SSL) == 0) &&
1171
this.connection.getUseSSL()) {
1172
if (this.connection.getRequireSSL()) {
1173
this.connection.close();
1175
throw SQLError.createSQLException(Messages.getString("MysqlIO.15"), //$NON-NLS-1$
1176
SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE);
1179
this.connection.setUseSSL(false);
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;
1188
// return FOUND rows
1189
this.clientParam |= CLIENT_FOUND_ROWS;
1191
if (this.connection.getAllowLoadLocalInfile()) {
1192
this.clientParam |= CLIENT_LOCAL_FILES;
1195
if (this.isInteractiveClient) {
1196
this.clientParam |= CLIENT_INTERACTIVE;
1200
if (this.protocolVersion > 9) {
1201
this.clientParam |= CLIENT_LONG_PASSWORD; // for long passwords
1203
this.clientParam &= ~CLIENT_LONG_PASSWORD;
1207
// 4.1 has some differences in the protocol
1209
if (versionMeetsMinimum(4, 1, 0)) {
1210
if (versionMeetsMinimum(4, 1, 1)) {
1211
this.clientParam |= CLIENT_PROTOCOL_41;
1212
this.has41NewNewProt = true;
1214
// Need this to get server status values
1215
this.clientParam |= CLIENT_TRANSACTIONS;
1217
// We always allow multiple result sets
1218
this.clientParam |= CLIENT_MULTI_RESULTS;
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;
1227
this.clientParam |= CLIENT_RESERVED;
1228
this.has41NewNewProt = false;
1231
this.use41Extensions = true;
1234
int passwordLength = 16;
1235
int userLength = (user != null) ? user.length() : 0;
1236
int databaseLength = (database != null) ? database.length() : 0;
1238
int packLength = ((userLength + passwordLength + databaseLength) * 2) + 7 + HEADER_LENGTH + AUTH_411_OVERHEAD;
1240
Buffer packet = null;
1242
if (!this.connection.getUseSSL()) {
1243
if ((this.serverCapabilities & CLIENT_SECURE_CONNECTION) != 0) {
1244
this.clientParam |= CLIENT_SECURE_CONNECTION;
1246
if (versionMeetsMinimum(4, 1, 1)) {
1247
secureAuth411(null, packLength, user, password, database,
1250
secureAuth(null, packLength, user, password, database, true);
1253
// Passwords can be 16 chars long
1254
packet = new Buffer(packLength);
1256
if ((this.clientParam & CLIENT_RESERVED) != 0) {
1257
if (versionMeetsMinimum(4, 1, 1)) {
1258
packet.writeLong(this.clientParam);
1259
packet.writeLong(this.maxThreeBytes);
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);
1266
// Set of bytes reserved for future use.
1267
packet.writeBytesNoNull(new byte[23]);
1269
packet.writeLong(this.clientParam);
1270
packet.writeLong(this.maxThreeBytes);
1273
packet.writeInt((int) this.clientParam);
1274
packet.writeLongInt(this.maxThreeBytes);
1277
// User/Password data
1278
packet.writeString(user, "Cp1252", this.connection);
1280
if (this.protocolVersion > 9) {
1281
packet.writeString(Util.newCrypt(password, this.seed), "Cp1252", this.connection);
1283
packet.writeString(Util.oldCrypt(password, this.seed), "Cp1252", this.connection);
1286
if (this.useConnectWithDb) {
1287
packet.writeString(database, "Cp1252", this.connection);
1290
send(packet, packet.getPosition());
1293
negotiateSSLConnection(user, password, database, packLength);
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)) {
1304
// Can't enable compression until after handshake
1306
if (((this.serverCapabilities & CLIENT_COMPRESS) != 0) &&
1307
this.connection.getUseCompression()) {
1308
// The following matches with ZLIB's
1310
this.deflater = new Deflater();
1311
this.useCompression = true;
1312
this.mysqlInput = new CompressedInputStream(this.connection,
1316
if (!this.useConnectWithDb) {
1317
changeDatabaseTo(database);
1321
private void changeDatabaseTo(String database) throws SQLException, CommunicationsException {
1322
if (database == null || database.length() == 0) {
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 " +
1333
sendCommand(MysqlDefs.INIT_DB, database, null, false, null);
1335
throw new CommunicationsException(this.connection,
1336
this.lastPacketSentTimeMs, ex);
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.
1346
* @param fields DOCUMENT ME!
1347
* @param columnCount DOCUMENT ME!
1348
* @param isBinaryEncoded DOCUMENT ME!
1349
* @param resultSetConcurrency DOCUMENT ME!
1351
* @return DOCUMENT ME!
1353
* @throws SQLException DOCUMENT ME!
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();
1362
if (!isBinaryEncoded) {
1364
// Didn't read an error, so re-position to beginning
1365
// of packet in order to read result set data
1367
rowPacket.setPosition(rowPacket.getPosition() - 1);
1369
if (!rowPacket.isLastDataPacket()) {
1370
byte[][] rowData = new byte[columnCount][];
1374
for (int i = 0; i < columnCount; i++) {
1375
rowData[i] = rowPacket.readLenByteArray(offset);
1381
readServerStatusForResultSets(rowPacket);
1387
// Handle binary-encoded data for server-side
1388
// PreparedStatements...
1390
if (!rowPacket.isLastDataPacket()) {
1391
return unpackBinaryResultSetRow(fields, rowPacket,
1392
resultSetConcurrency);
1395
rowPacket.setPosition(rowPacket.getPosition() - 1);
1396
readServerStatusForResultSets(rowPacket);
1402
* Log-off of the MySQL server and close the socket.
1404
* @throws SQLException DOCUMENT ME!
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());
1415
* Returns the packet used for sending data (used by PreparedStatement)
1416
* Guarded by external synchronization on a mutex.
1418
* @return A packet to send data with
1420
Buffer getSharedSendPacket() {
1421
if (this.sharedSendPacket == null) {
1422
this.sharedSendPacket = new Buffer(INITIAL_PACKET_SIZE);
1425
return this.sharedSendPacket;
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$
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$
1441
this.streamingData = null;
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);
1451
ResultSet topLevelResultSet = readResultsForQueryOrUpdate(callingStatement,
1452
maxRows, resultSetType, resultSetConcurrency, streamResults,
1453
catalog, resultPacket, isBinaryEncoded, preSentColumnCount,
1454
unpackFieldInfo, metadataFromCache);
1456
ResultSet currentResultSet = topLevelResultSet;
1458
boolean checkForMoreResults = ((this.clientParam &
1459
CLIENT_MULTI_RESULTS) != 0);
1461
boolean serverHasMoreResults = (this.serverStatus &
1462
SERVER_MORE_RESULTS_EXISTS) != 0;
1465
// TODO: We need to support streaming of multiple result sets
1467
if (serverHasMoreResults && streamResults) {
1470
throw SQLError.createSQLException(Messages.getString("MysqlIO.23"), //$NON-NLS-1$
1471
SQLError.SQL_STATE_DRIVER_NOT_CAPABLE);
1474
boolean moreRowSetsExist = checkForMoreResults & serverHasMoreResults;
1476
while (moreRowSetsExist) {
1477
Buffer fieldPacket = checkErrorPacket();
1478
fieldPacket.setPosition(0);
1480
ResultSet newResultSet = readResultsForQueryOrUpdate(callingStatement,
1481
maxRows, resultSetType, resultSetConcurrency,
1482
streamResults, catalog, fieldPacket, isBinaryEncoded,
1483
preSentColumnCount, unpackFieldInfo, metadataFromCache);
1485
currentResultSet.setNextResultSet(newResultSet);
1487
currentResultSet = newResultSet;
1489
moreRowSetsExist = (this.serverStatus & SERVER_MORE_RESULTS_EXISTS) != 0;
1492
if (!streamResults) {
1496
reclaimLargeReusablePacket();
1498
return topLevelResultSet;
1502
* Sets the buffer size to max-buf
1504
void resetMaxBuf() {
1505
this.maxAllowedPacket = this.connection.getMaxAllowedPacket();
1509
* Send a command to the MySQL server If data is to be sent with command,
1510
* it should be put in extraData.
1512
* Raw packets can be sent by setting queryPacket to something other
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
1523
* @return the response packet from the server
1525
* @throws SQLException if an I/O error or SQL error occurs
1528
final Buffer sendCommand(int command, String extraData, Buffer queryPacket,
1529
boolean skipCheck, String extraDataCharEncoding)
1530
throws SQLException {
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...
1536
this.enablePacketDebug = this.connection.getEnablePacketDebug();
1537
this.traceProtocol = this.connection.getTraceProtocol();
1538
this.readPacketSequence = 0;
1542
checkForOutstandingStreamingData();
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;
1551
this.queryNoIndexUsed = false;
1552
this.queryBadIndexUsed = false;
1555
// Compressed input stream needs cleared at beginning
1556
// of each command execution...
1558
if (this.useCompression) {
1559
int bytesLeft = this.mysqlInput.available();
1561
if (bytesLeft > 0) {
1562
this.mysqlInput.skip(bytesLeft);
1570
// PreparedStatements construct their own packets,
1571
// for efficiency's sake.
1573
// If this is a generic query, we need to re-use
1574
// the sending packet.
1576
if (queryPacket == null) {
1577
int packLength = HEADER_LENGTH + COMP_HEADER_LENGTH + 1 +
1578
((extraData != null) ? extraData.length() : 0) + 2;
1580
if (this.sendPacket == null) {
1581
this.sendPacket = new Buffer(packLength);
1584
this.packetSequence = -1;
1585
this.readPacketSequence = 0;
1586
this.checkPacketSequence = true;
1587
this.sendPacket.clear();
1589
this.sendPacket.writeByte((byte) command);
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);
1599
this.sendPacket.writeStringNoNull(extraData,
1600
extraDataCharEncoding,
1601
this.connection.getServerCharacterEncoding(),
1602
this.connection.parserKnowsUnicode(), this.connection);
1604
} else if (command == MysqlDefs.PROCESS_KILL) {
1605
long id = new Long(extraData).longValue();
1606
this.sendPacket.writeLong(id);
1609
send(this.sendPacket, this.sendPacket.getPosition());
1611
this.packetSequence = -1;
1612
send(queryPacket, queryPacket.getPosition()); // packet passed by PreparedStatement
1614
} catch (SQLException sqlEx) {
1615
// don't wrap SQLExceptions
1617
} catch (Exception ex) {
1618
throw new CommunicationsException(this.connection,
1619
this.lastPacketSentTimeMs, ex);
1622
Buffer returnPacket = null;
1625
if ((command == MysqlDefs.COM_EXECUTE) ||
1626
(command == MysqlDefs.COM_RESET_STMT)) {
1627
this.readPacketSequence = 0;
1628
this.packetSequenceReset = true;
1631
returnPacket = checkErrorPacket(command);
1634
return returnPacket;
1635
} catch (IOException ioEx) {
1636
throw new CommunicationsException(this.connection,
1637
this.lastPacketSentTimeMs, ioEx);
1642
* Send a query stored in a packet directly to the server.
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)?
1656
* @return DOCUMENT ME!
1658
* @throws Exception DOCUMENT ME!
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)
1665
long queryStartTime = 0;
1666
long queryEndTime = 0;
1668
if (query != null) {
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;
1676
if (this.sendPacket == null) {
1677
this.sendPacket = new Buffer(packLength);
1679
this.sendPacket.clear();
1682
this.sendPacket.writeByte((byte) MysqlDefs.QUERY);
1684
if (characterEncoding != null) {
1685
if (this.platformDbCharsetMatches) {
1686
this.sendPacket.writeStringNoNull(query, characterEncoding,
1687
this.connection.getServerCharacterEncoding(),
1688
this.connection.parserKnowsUnicode(),
1691
if (StringUtils.startsWithIgnoreCaseAndWs(query, "LOAD DATA")) { //$NON-NLS-1$
1692
this.sendPacket.writeBytesNoNull(query.getBytes());
1694
this.sendPacket.writeStringNoNull(query,
1696
this.connection.getServerCharacterEncoding(),
1697
this.connection.parserKnowsUnicode(),
1702
this.sendPacket.writeStringNoNull(query);
1705
queryPacket = this.sendPacket;
1708
byte[] queryBuf = null;
1709
int oldPacketPosition = 0;
1713
if (needToGrabQueryFromPacket) {
1714
queryBuf = queryPacket.getByteBuffer();
1716
// save the packet position
1717
oldPacketPosition = queryPacket.getPosition();
1719
queryStartTime = getCurrentTimeNanosOrMillis();
1722
// Send query command and sql query string
1723
Buffer resultPacket = sendCommand(MysqlDefs.QUERY, null, queryPacket,
1726
long fetchBeginTime = 0;
1727
long fetchEndTime = 0;
1729
String profileQueryToLog = null;
1731
boolean queryWasSlow = false;
1733
if (this.profileSql || this.logSlowQueries) {
1734
queryEndTime = getCurrentTimeNanosOrMillis();
1736
boolean shouldExtractQuery = false;
1738
if (this.profileSql) {
1739
shouldExtractQuery = true;
1740
} else if (this.logSlowQueries) {
1742
if ((queryEndTime - queryStartTime) > this.slowQueryThreshold) {
1743
shouldExtractQuery = true;
1744
queryWasSlow = true;
1748
if (shouldExtractQuery) {
1749
// Extract the actual query from the network packet
1750
boolean truncated = false;
1752
int extractPosition = oldPacketPosition;
1754
if (oldPacketPosition > this.connection.getMaxQuerySizeToLog()) {
1755
extractPosition = this.connection.getMaxQuerySizeToLog() + 5;
1759
profileQueryToLog = new String(queryBuf, 5,
1760
(extractPosition - 5));
1763
profileQueryToLog += Messages.getString("MysqlIO.25"); //$NON-NLS-1$
1767
fetchBeginTime = queryEndTime;
1770
if (this.autoGenerateTestcaseScript) {
1771
String testcaseQuery = null;
1773
if (query != null) {
1774
testcaseQuery = query;
1776
testcaseQuery = new String(queryBuf, 5,
1777
(oldPacketPosition - 5));
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());
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 */);
1792
StringBuffer mesgBuf = new StringBuffer(48 +
1793
profileQueryToLog.length());
1795
mesgBuf.append(Messages.getString("MysqlIO.SlowQuery",
1796
new Object[] {new Long(this.slowQueryThreshold),
1798
new Long(queryEndTime - queryStartTime)}));
1799
mesgBuf.append(profileQueryToLog);
1801
ProfileEventSink eventSink = ProfileEventSink.getInstance(this.connection);
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()));
1810
if (this.connection.getExplainSlowQueries()) {
1811
if (oldPacketPosition < MAX_QUERY_SIZE_TO_EXPLAIN) {
1812
explainSlowQuery(queryPacket.getBytes(5,
1813
(oldPacketPosition - 5)), profileQueryToLog);
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$
1823
if (this.profileSql) {
1824
fetchEndTime = getCurrentTimeNanosOrMillis();
1826
ProfileEventSink eventSink = ProfileEventSink.getInstance(this.connection);
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,
1834
new Throwable(), profileQueryToLog));
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,
1842
new Throwable(), null));
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()
1850
System.currentTimeMillis(),
1851
(queryEndTime - queryStartTime), this.queryTimingUnits,
1854
Messages.getString("MysqlIO.33") //$NON-NLS-1$
1855
+profileQueryToLog));
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()
1864
System.currentTimeMillis(),
1865
(queryEndTime - queryStartTime), this.queryTimingUnits,
1868
Messages.getString("MysqlIO.35") //$NON-NLS-1$
1869
+profileQueryToLog));
1873
if (this.hadWarnings) {
1874
scanForAndThrowDataTruncation();
1880
private void calculateSlowQueryThreshold() {
1881
this.slowQueryThreshold = this.connection.getSlowQueryThresholdMillis();
1883
if (this.connection.getUseNanosForElapsedTime()) {
1884
long nanosThreshold = this.connection.getSlowQueryThresholdNanos();
1886
if (nanosThreshold != 0) {
1887
this.slowQueryThreshold = nanosThreshold;
1889
this.slowQueryThreshold *= 1000000; // 1 million millis in a nano
1894
protected long getCurrentTimeNanosOrMillis() {
1895
if (this.useNanosForElapsedTime) {
1896
return Util.getCurrentTimeNanosOrMillis();
1899
return System.currentTimeMillis();
1903
* Returns the host this IO is connected to
1905
* @return DOCUMENT ME!
1912
* Is the version of the MySQL server we are connected to the given
1915
* @param major the major version
1916
* @param minor the minor version
1917
* @param subminor the subminor version
1919
* @return true if the version of the MySQL server we are connected is the
1922
boolean isVersion(int major, int minor, int subminor) {
1923
return ((major == getServerMajorVersion()) &&
1924
(minor == getServerMinorVersion()) &&
1925
(subminor == getServerSubMinorVersion()));
1929
* Does the version of the MySQL server we are connected to meet the given
1932
* @param major DOCUMENT ME!
1933
* @param minor DOCUMENT ME!
1934
* @param subminor DOCUMENT ME!
1936
* @return DOCUMENT ME!
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);
1946
// newer than major.minor
1950
// older than major.minor
1962
* Returns the hex dump of the given packet, truncated to
1963
* MAX_PACKET_DUMP_LENGTH if packetLength exceeds that value.
1965
* @param packetToDump the packet to dump in hex
1966
* @param packetLength the number of bytes to dump
1968
* @return the hex dump of the given packet
1970
private final static String getPacketDumpToLog(Buffer packetToDump,
1972
if (packetLength < MAX_PACKET_DUMP_LENGTH) {
1973
return packetToDump.dump(packetLength);
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$
1982
return packetDumpBuf.toString();
1985
private final int readFully(InputStream in, byte[] b, int off, int len)
1986
throws IOException {
1988
throw new IndexOutOfBoundsException();
1994
int count = in.read(b, off + n, len - n);
1997
throw new EOFException(Messages.getString("MysqlIO.EOF",
1998
new Object[] {new Integer(len), new Integer(n)}));
2007
private final long skipFully(InputStream in, long len) throws IOException {
2009
throw new IOException("Negative skip length not allowed");
2015
long count = in.skip(len - n);
2018
throw new EOFException(Messages.getString("MysqlIO.EOF",
2019
new Object[] {new Long(len), new Long(n)}));
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.
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?
2044
* @return a result set that either represents the rows, or an update count
2046
* @throws SQLException if an error occurs while reading the rows
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();
2055
if (columnCount == 0) {
2056
return buildResultSetWithUpdates(callingStatement, resultPacket);
2057
} else if (columnCount == Buffer.NULL_LENGTH) {
2058
String charEncoding = null;
2060
if (this.connection.getUseUnicode()) {
2061
charEncoding = this.connection.getEncoding();
2064
String fileName = null;
2066
if (this.platformDbCharsetMatches) {
2067
fileName = ((charEncoding != null)
2068
? resultPacket.readString(charEncoding)
2069
: resultPacket.readString());
2071
fileName = resultPacket.readString();
2074
return sendFileToServer(callingStatement, fileName);
2076
com.mysql.jdbc.ResultSet results = getResultSet(callingStatement,
2077
columnCount, maxRows, resultSetType, resultSetConcurrency,
2078
streamResults, catalog, isBinaryEncoded, unpackFieldInfo,
2085
private int alignPacketSize(int a, int l) {
2086
return ((((a) + (l)) - 1) & ~((l) - 1));
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;
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);
2101
if (isBinaryEncoded) {
2102
rs.setBinaryEncoded();
2107
case java.sql.ResultSet.CONCUR_UPDATABLE:
2108
rs = new com.mysql.jdbc.UpdatableResultSet(catalog, fields, rows,
2109
this.connection, callingStatement);
2114
return new com.mysql.jdbc.ResultSet(catalog, fields, rows,
2115
this.connection, callingStatement);
2118
rs.setResultSetType(resultSetType);
2119
rs.setResultSetConcurrency(resultSetConcurrency);
2124
private com.mysql.jdbc.ResultSet buildResultSetWithUpdates(
2125
Statement callingStatement, Buffer resultPacket)
2126
throws SQLException {
2127
long updateCount = -1;
2132
if (this.useNewUpdateCounts) {
2133
updateCount = resultPacket.newReadLength();
2134
updateID = resultPacket.newReadLength();
2136
updateCount = resultPacket.readLength();
2137
updateID = resultPacket.readLength();
2140
if (this.use41Extensions) {
2141
this.serverStatus = resultPacket.readInt();
2143
this.warningCount = resultPacket.readInt();
2145
if (this.warningCount > 0) {
2146
this.hadWarnings = true; // this is a 'latch', it's reset by sendCommand()
2149
resultPacket.readByte(); // advance pointer
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;
2159
if (this.connection.isReadInfoMsgEnabled()) {
2160
info = resultPacket.readString();
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);
2168
ResultSet updateRs = new com.mysql.jdbc.ResultSet(updateCount,
2169
updateID, this.connection, callingStatement);
2172
updateRs.setServerInfo(info);
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$
2188
// Close the result set
2189
this.streamingData.getOwner().realClose(false);
2191
// clear any pending data....
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.
2201
int lengthToWrite = 0;
2202
int compressedLength = 0;
2203
byte[] bytesToCompress = packet.getByteBuffer();
2204
byte[] compressedBytes = null;
2205
int offsetWrite = 0;
2207
if (packetLen < MIN_COMPRESS_LEN) {
2208
lengthToWrite = packetLen;
2209
compressedBytes = packet.getByteBuffer();
2210
compressedLength = 0;
2211
offsetWrite = offset;
2213
compressedBytes = new byte[bytesToCompress.length * 2];
2215
this.deflater.reset();
2216
this.deflater.setInput(bytesToCompress, offset, packetLen);
2217
this.deflater.finish();
2219
int compLen = this.deflater.deflate(compressedBytes);
2221
if (compLen > packetLen) {
2222
lengthToWrite = packetLen;
2223
compressedBytes = packet.getByteBuffer();
2224
compressedLength = 0;
2225
offsetWrite = offset;
2227
lengthToWrite = compLen;
2228
headerLength += COMP_HEADER_LENGTH;
2229
compressedLength = packetLen;
2233
Buffer compressedPacket = new Buffer(packetLen + headerLength);
2235
compressedPacket.setPosition(0);
2236
compressedPacket.writeLongInt(lengthToWrite);
2237
compressedPacket.writeByte(this.packetSequence);
2238
compressedPacket.writeLongInt(compressedLength);
2239
compressedPacket.writeBytesNoNull(compressedBytes, offsetWrite,
2242
return compressedPacket;
2245
private final void readServerStatusForResultSets(Buffer rowPacket)
2246
throws SQLException {
2247
if (this.use41Extensions) {
2248
rowPacket.readByte(); // skips the 'last packet' flag
2250
this.warningCount = rowPacket.readInt();
2252
if (this.warningCount > 0) {
2253
this.hadWarnings = true; // this is a 'latch', it's reset by sendCommand()
2256
this.serverStatus = rowPacket.readInt();
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;
2267
private SocketFactory createSocketFactory() throws SQLException {
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);
2274
return (SocketFactory) (Class.forName(this.socketFactoryClassName)
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);
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();
2293
StringBuffer packetDump = null;
2295
if (!isPacketBeingSent) {
2296
int bytesToDump = Math.min(MAX_PACKET_DUMP_LENGTH,
2297
packet.getBufLength());
2299
Buffer packetToDump = new Buffer(4 + bytesToDump);
2301
packetToDump.setPosition(0);
2302
packetToDump.writeBytesNoNull(header);
2303
packetToDump.writeBytesNoNull(packet.getBytes(0, bytesToDump));
2305
String packetPayload = packetToDump.dump(bytesToDump);
2307
packetDump = new StringBuffer(96 + packetPayload.length());
2309
packetDump.append("Server ");
2311
if (isPacketReused) {
2312
packetDump.append("(re-used)");
2314
packetDump.append("(new)");
2317
packetDump.append(" ");
2318
packetDump.append(packet.toSuperString());
2319
packetDump.append(" --------------------> Client\n");
2320
packetDump.append("\nPacket payload:\n\n");
2321
packetDump.append(packetPayload);
2323
if (bytesToDump == MAX_PACKET_DUMP_LENGTH) {
2324
packetDump.append("\nNote: Packet of " + packet.getBufLength() +
2325
" bytes truncated to " + MAX_PACKET_DUMP_LENGTH +
2329
int bytesToDump = Math.min(MAX_PACKET_DUMP_LENGTH, sendLength);
2331
String packetPayload = packet.dump(bytesToDump);
2333
packetDump = new StringBuffer(64 + 4 + packetPayload.length());
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);
2341
if (bytesToDump == MAX_PACKET_DUMP_LENGTH) {
2342
packetDump.append("\nNote: Packet of " + sendLength +
2343
" bytes truncated to " + MAX_PACKET_DUMP_LENGTH +
2348
this.packetDebugRingBuffer.addLast(packetDump);
2351
private RowData readSingleRowSet(long columnCount, int maxRows,
2352
int resultSetConcurrency, boolean isBinaryEncoded, Field[] fields)
2353
throws SQLException {
2355
ArrayList rows = new ArrayList();
2357
// Now read the data
2358
Object rowBytes = nextRow(fields, (int) columnCount, isBinaryEncoded,
2359
resultSetConcurrency);
2363
if (rowBytes != null) {
2368
while (rowBytes != null) {
2369
rowBytes = nextRow(fields, (int) columnCount, isBinaryEncoded,
2370
resultSetConcurrency);
2372
if (rowBytes != null) {
2373
if ((maxRows == -1) || (rowCount < maxRows)) {
2380
rowData = new RowDataStatic(rows);
2386
* Don't hold on to overly-large packets
2388
private void reclaimLargeReusablePacket() {
2389
if ((this.reusablePacket != null) &&
2390
(this.reusablePacket.getCapacity() > 1048576)) {
2391
this.reusablePacket = new Buffer(INITIAL_PACKET_SIZE);
2396
* Re-use a packet to read from the MySQL server
2398
* @param reuse DOCUMENT ME!
2400
* @return DOCUMENT ME!
2402
* @throws SQLException DOCUMENT ME!
2403
* @throws SQLException DOCUMENT ME!
2405
private final Buffer reuseAndReadPacket(Buffer reuse)
2406
throws SQLException {
2409
reuse.setWasMultiPacket(false);
2411
int lengthRead = readFully(this.mysqlInput,
2412
this.packetHeaderBuf, 0, 4);
2414
if (lengthRead < 4) {
2416
throw new IOException(Messages.getString("MysqlIO.43")); //$NON-NLS-1$
2419
int packetLength = (this.packetHeaderBuf[0] & 0xff) +
2420
((this.packetHeaderBuf[1] & 0xff) << 8) +
2421
((this.packetHeaderBuf[2] & 0xff) << 16);
2423
if (this.traceProtocol) {
2424
StringBuffer traceMessageBuf = new StringBuffer();
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));
2432
this.connection.getLog().logTrace(traceMessageBuf.toString());
2435
byte multiPacketSeq = this.packetHeaderBuf[3];
2437
if (!this.packetSequenceReset) {
2438
if (this.enablePacketDebug && this.checkPacketSequence) {
2439
checkPacketSequencing(multiPacketSeq);
2442
this.packetSequenceReset = false;
2445
this.readPacketSequence = multiPacketSeq;
2447
// Set the Buffer to it's original state
2448
reuse.setPosition(0);
2450
// Do we need to re-alloc the byte buffer?
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]);
2460
// Set the new length
2461
reuse.setBufLength(packetLength);
2463
// Read the data from the server
2464
int numBytesRead = readFully(this.mysqlInput,
2465
reuse.getByteBuffer(), 0, packetLength);
2467
if (numBytesRead != packetLength) {
2468
throw new IOException("Short read, expected " +
2469
packetLength + " bytes, only read " + numBytesRead);
2472
if (this.traceProtocol) {
2473
StringBuffer traceMessageBuf = new StringBuffer();
2475
traceMessageBuf.append(Messages.getString("MysqlIO.46")); //$NON-NLS-1$
2476
traceMessageBuf.append(getPacketDumpToLog(reuse,
2479
this.connection.getLog().logTrace(traceMessageBuf.toString());
2482
if (this.enablePacketDebug) {
2483
enqueuePacketForDebugging(false, true, 0,
2484
this.packetHeaderBuf, reuse);
2487
boolean isMultiPacket = false;
2489
if (packetLength == this.maxThreeBytes) {
2490
reuse.setPosition(this.maxThreeBytes);
2492
int packetEndPoint = packetLength;
2494
// it's multi-packet
2495
isMultiPacket = true;
2497
lengthRead = readFully(this.mysqlInput,
2498
this.packetHeaderBuf, 0, 4);
2500
if (lengthRead < 4) {
2502
throw new IOException(Messages.getString("MysqlIO.47")); //$NON-NLS-1$
2505
packetLength = (this.packetHeaderBuf[0] & 0xff) +
2506
((this.packetHeaderBuf[1] & 0xff) << 8) +
2507
((this.packetHeaderBuf[2] & 0xff) << 16);
2509
Buffer multiPacket = new Buffer(packetLength);
2510
boolean firstMultiPkt = true;
2513
if (!firstMultiPkt) {
2514
lengthRead = readFully(this.mysqlInput,
2515
this.packetHeaderBuf, 0, 4);
2517
if (lengthRead < 4) {
2519
throw new IOException(Messages.getString(
2520
"MysqlIO.48")); //$NON-NLS-1$
2523
packetLength = (this.packetHeaderBuf[0] & 0xff) +
2524
((this.packetHeaderBuf[1] & 0xff) << 8) +
2525
((this.packetHeaderBuf[2] & 0xff) << 16);
2527
firstMultiPkt = false;
2530
if (!this.useNewLargePackets && (packetLength == 1)) {
2534
} else if (packetLength < this.maxThreeBytes) {
2535
byte newPacketSeq = this.packetHeaderBuf[3];
2537
if (newPacketSeq != (multiPacketSeq + 1)) {
2538
throw new IOException(Messages.getString(
2539
"MysqlIO.49")); //$NON-NLS-1$
2542
multiPacketSeq = newPacketSeq;
2544
// Set the Buffer to it's original state
2545
multiPacket.setPosition(0);
2547
// Set the new length
2548
multiPacket.setBufLength(packetLength);
2550
// Read the data from the server
2551
byte[] byteBuf = multiPacket.getByteBuffer();
2552
int lengthToWrite = packetLength;
2554
int bytesRead = readFully(this.mysqlInput, byteBuf,
2557
if (bytesRead != lengthToWrite) {
2558
throw new CommunicationsException(this.connection,
2559
this.lastPacketSentTimeMs,
2560
SQLError.createSQLException(Messages.getString(
2561
"MysqlIO.50") //$NON-NLS-1$
2563
Messages.getString("MysqlIO.51") +
2564
bytesRead //$NON-NLS-1$
2565
+".")); //$NON-NLS-1$
2568
reuse.writeBytesNoNull(byteBuf, 0, lengthToWrite);
2570
packetEndPoint += lengthToWrite;
2572
break; // end of multipacket sequence
2575
byte newPacketSeq = this.packetHeaderBuf[3];
2577
if (newPacketSeq != (multiPacketSeq + 1)) {
2578
throw new IOException(Messages.getString(
2579
"MysqlIO.53")); //$NON-NLS-1$
2582
multiPacketSeq = newPacketSeq;
2584
// Set the Buffer to it's original state
2585
multiPacket.setPosition(0);
2587
// Set the new length
2588
multiPacket.setBufLength(packetLength);
2590
// Read the data from the server
2591
byte[] byteBuf = multiPacket.getByteBuffer();
2592
int lengthToWrite = packetLength;
2594
int bytesRead = readFully(this.mysqlInput, byteBuf, 0,
2597
if (bytesRead != lengthToWrite) {
2598
throw new CommunicationsException(this.connection,
2599
this.lastPacketSentTimeMs,
2600
SQLError.createSQLException(Messages.getString(
2601
"MysqlIO.54") //$NON-NLS-1$
2603
Messages.getString("MysqlIO.55") //$NON-NLS-1$
2604
+bytesRead + ".")); //$NON-NLS-1$
2607
reuse.writeBytesNoNull(byteBuf, 0, lengthToWrite);
2609
packetEndPoint += lengthToWrite;
2612
reuse.setPosition(0);
2613
reuse.setWasMultiPacket(true);
2616
if (!isMultiPacket) {
2617
reuse.getByteBuffer()[packetLength] = 0; // Null-termination
2621
} catch (IOException ioEx) {
2622
throw new CommunicationsException(this.connection,
2623
this.lastPacketSentTimeMs, ioEx);
2624
} catch (OutOfMemoryError oom) {
2630
this.connection.realClose(false, false, true, oom);
2640
* @param multiPacketSeq
2641
* @throws CommunicationsException
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 # " +
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 # " +
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 # " +
2669
void enableMultiQueries() throws SQLException {
2670
Buffer buf = getSharedSendPacket();
2673
buf.writeByte((byte)MysqlDefs.COM_SET_OPTION);
2675
sendCommand(MysqlDefs.COM_SET_OPTION, null, buf, false, null);
2678
void disableMultiQueries() throws SQLException {
2679
Buffer buf = getSharedSendPacket();
2682
buf.writeByte((byte)MysqlDefs.COM_SET_OPTION);
2684
sendCommand(MysqlDefs.COM_SET_OPTION, null, buf, false, null);
2687
private final void send(Buffer packet, int packetLen)
2688
throws SQLException {
2690
if (packetLen > this.maxAllowedPacket) {
2691
throw new PacketTooBigException(packetLen, this.maxAllowedPacket);
2694
if (this.connection.getMaintainTimeStats()) {
2695
this.lastPacketSentTimeMs = System.currentTimeMillis();
2698
if ((this.serverMajorVersion >= 4) &&
2699
(packetLen >= this.maxThreeBytes)) {
2700
sendSplitPackets(packet);
2702
this.packetSequence++;
2704
Buffer packetToSend = packet;
2706
packetToSend.setPosition(0);
2708
if (this.useCompression) {
2709
int originalPacketLen = packetLen;
2711
packetToSend = compressPacket(packet, 0, packetLen,
2713
packetLen = packetToSend.getPosition();
2715
if (this.traceProtocol) {
2716
StringBuffer traceMessageBuf = new StringBuffer();
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));
2725
this.connection.getLog().logTrace(traceMessageBuf.toString());
2728
packetToSend.writeLongInt(packetLen - HEADER_LENGTH);
2729
packetToSend.writeByte(this.packetSequence);
2731
if (this.traceProtocol) {
2732
StringBuffer traceMessageBuf = new StringBuffer();
2734
traceMessageBuf.append(Messages.getString("MysqlIO.59")); //$NON-NLS-1$
2735
traceMessageBuf.append(packetToSend.dump(packetLen));
2737
this.connection.getLog().logTrace(traceMessageBuf.toString());
2742
this.mysqlOutput.write(packetToSend.getByteBuffer(), 0,
2744
this.mysqlOutput.flush();
2747
if (this.enablePacketDebug) {
2748
enqueuePacketForDebugging(true, false, packetLen + 5,
2749
this.packetHeaderBuf, packet);
2753
// Don't hold on to large packets
2755
if (packet == this.sharedSendPacket) {
2756
reclaimLargeSharedSendPacket();
2758
} catch (IOException ioEx) {
2759
throw new CommunicationsException(this.connection,
2760
this.lastPacketSentTimeMs, ioEx);
2765
* Reads and sends a file to the server for LOAD DATA LOCAL INFILE
2767
* @param callingStatement DOCUMENT ME!
2768
* @param fileName the file name to send.
2770
* @return DOCUMENT ME!
2772
* @throws SQLException DOCUMENT ME!
2774
private final ResultSet sendFileToServer(Statement callingStatement,
2775
String fileName) throws SQLException {
2777
Buffer filePacket = (this.loadFileBufRef == null) ? null
2778
: (Buffer) (this.loadFileBufRef.get());
2780
int bigPacketLength = Math.min(this.connection.getMaxAllowedPacket() -
2781
(HEADER_LENGTH * 3),
2782
alignPacketSize(this.connection.getMaxAllowedPacket() - 16, 4096) -
2783
(HEADER_LENGTH * 3));
2785
int oneMeg = 1024 * 1024;
2787
int smallerPacketSizeAligned = Math.min(oneMeg - (HEADER_LENGTH * 3),
2788
alignPacketSize(oneMeg - 16, 4096) - (HEADER_LENGTH * 3));
2790
int packetLength = Math.min(smallerPacketSizeAligned, bigPacketLength);
2792
if (filePacket == null) {
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);
2806
send(filePacket, 0);
2808
byte[] fileBuf = new byte[packetLength];
2810
BufferedInputStream fileIn = null;
2813
if (!this.connection.getAllowLoadLocalInfile()) {
2814
throw SQLError.createSQLException(
2815
Messages.getString("MysqlIO.LoadDataLocalNotAllowed"),
2816
SQLError.SQL_STATE_GENERAL_ERROR);
2819
if (!this.connection.getAllowUrlInLocalInfile()) {
2820
fileIn = new BufferedInputStream(new FileInputStream(fileName));
2822
// First look for ':'
2823
if (fileName.indexOf(":") != -1) {
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(
2833
fileIn = new BufferedInputStream(new FileInputStream(
2840
while ((bytesRead = fileIn.read(fileBuf)) != -1) {
2842
filePacket.writeBytesNoNull(fileBuf, 0, bytesRead);
2843
send(filePacket, filePacket.getPosition());
2845
} catch (IOException ioEx) {
2846
StringBuffer messageBuf = new StringBuffer(Messages.getString(
2847
"MysqlIO.60")); //$NON-NLS-1$
2849
if (!this.connection.getParanoid()) {
2850
messageBuf.append("'"); //$NON-NLS-1$
2852
if (fileName != null) {
2853
messageBuf.append(fileName);
2856
messageBuf.append("'"); //$NON-NLS-1$
2859
messageBuf.append(Messages.getString("MysqlIO.63")); //$NON-NLS-1$
2861
if (!this.connection.getParanoid()) {
2862
messageBuf.append(Messages.getString("MysqlIO.64")); //$NON-NLS-1$
2863
messageBuf.append(Util.stackTraceToString(ioEx));
2866
throw SQLError.createSQLException(messageBuf.toString(),
2867
SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
2869
if (fileIn != null) {
2872
} catch (Exception ex) {
2873
throw SQLError.createSQLException(Messages.getString("MysqlIO.65"), //$NON-NLS-1$
2874
SQLError.SQL_STATE_GENERAL_ERROR);
2879
// file open failed, but server needs one packet
2881
send(filePacket, filePacket.getPosition());
2882
checkErrorPacket(); // to clear response off of queue
2886
// send empty packet to mark EOF
2888
send(filePacket, filePacket.getPosition());
2890
Buffer resultPacket = checkErrorPacket();
2892
return buildResultSetWithUpdates(callingStatement, resultPacket);
2896
* Checks for errors in the reply packet, and if none, returns the reply
2897
* packet, ready for reading
2899
* @param command the command being issued (if used)
2901
* @return DOCUMENT ME!
2903
* @throws SQLException if an error packet was received
2904
* @throws CommunicationsException DOCUMENT ME!
2906
private Buffer checkErrorPacket(int command) throws SQLException {
2908
Buffer resultPacket = null;
2909
this.serverStatus = 0;
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
2921
} catch (Exception fallThru) {
2922
throw new CommunicationsException(this.connection,
2923
this.lastPacketSentTimeMs, fallThru);
2927
if (statusCode == (byte) 0xff) {
2928
String serverErrorMessage;
2931
if (this.protocolVersion > 9) {
2932
errno = resultPacket.readInt();
2934
String xOpen = null;
2936
serverErrorMessage =
2937
resultPacket.readString(this.connection.getErrorMessageEncoding());
2939
if (serverErrorMessage.startsWith("#")) { //$NON-NLS-1$
2941
// we have an SQLState
2942
if (serverErrorMessage.length() > 6) {
2943
xOpen = serverErrorMessage.substring(1, 6);
2944
serverErrorMessage = serverErrorMessage.substring(6);
2946
if (xOpen.equals("HY000")) { //$NON-NLS-1$
2947
xOpen = SQLError.mysqlToSqlState(errno,
2948
this.connection.getUseSqlStateCodes());
2951
xOpen = SQLError.mysqlToSqlState(errno,
2952
this.connection.getUseSqlStateCodes());
2955
xOpen = SQLError.mysqlToSqlState(errno,
2956
this.connection.getUseSqlStateCodes());
2961
StringBuffer errorBuf = new StringBuffer();
2963
String xOpenErrorMessage = SQLError.get(xOpen);
2965
if (!this.connection.getUseOnlyServerErrorMessages()) {
2966
if (xOpenErrorMessage != null) {
2967
errorBuf.append(xOpenErrorMessage);
2968
errorBuf.append(Messages.getString("MysqlIO.68")); //$NON-NLS-1$
2972
errorBuf.append(serverErrorMessage);
2974
if (!this.connection.getUseOnlyServerErrorMessages()) {
2975
if (xOpenErrorMessage != null) {
2976
errorBuf.append("\""); //$NON-NLS-1$
2980
appendInnodbStatusInformation(xOpen, errorBuf);
2982
if (xOpen != null && xOpen.startsWith("22")) {
2983
throw new MysqlDataTruncation(errorBuf.toString(), 0, true, false, 0, 0);
2985
throw SQLError.createSQLException(errorBuf.toString(), xOpen, errno);
2989
serverErrorMessage = resultPacket.readString(
2990
this.connection.getErrorMessageEncoding());
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) +
2997
+serverErrorMessage, SQLError.SQL_STATE_COLUMN_NOT_FOUND,
3001
StringBuffer errorBuf = new StringBuffer(Messages.getString(
3002
"MysqlIO.72")); //$NON-NLS-1$
3003
errorBuf.append(serverErrorMessage);
3004
errorBuf.append("\""); //$NON-NLS-1$
3006
throw SQLError.createSQLException(SQLError.get(
3007
SQLError.SQL_STATE_GENERAL_ERROR) + ", " //$NON-NLS-1$
3008
+errorBuf.toString(), SQLError.SQL_STATE_GENERAL_ERROR, -1);
3011
return resultPacket;
3014
private void appendInnodbStatusInformation(String xOpen,
3015
StringBuffer errorBuf) throws SQLException {
3016
if (this.connection.getIncludeInnodbStatusInDeadlockExceptions()
3018
&& (xOpen.startsWith("40") || xOpen.startsWith("41"))
3019
&& this.streamingData == null) {
3020
ResultSet rs = null;
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);
3029
errorBuf.append("\n\n");
3030
errorBuf.append(rs.getString(1));
3032
errorBuf.append(Messages
3033
.getString("MysqlIO.NoInnoDBStatusFound"));
3035
} catch (Exception ex) {
3036
errorBuf.append(Messages
3037
.getString("MysqlIO.InnoDBStatusFailed"));
3038
errorBuf.append("\n\n");
3039
errorBuf.append(Util.stackTraceToString(ex));
3049
* Sends a large packet to the server as a series of smaller packets
3054
* @throws SQLException
3056
* @throws CommunicationsException
3059
private final void sendSplitPackets(Buffer packet)
3060
throws SQLException {
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)
3068
// NB: Guarded by execSQL. If the driver changes architecture, this
3069
// will need to be synchronized in some other way
3071
Buffer headerPacket = (this.splitBufRef == null) ? null
3072
: (Buffer) (this.splitBufRef.get());
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
3079
if (headerPacket == null) {
3080
headerPacket = new Buffer((this.maxThreeBytes +
3082
this.splitBufRef = new SoftReference(headerPacket);
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();
3091
while (len >= this.maxThreeBytes) {
3092
this.packetSequence++;
3094
headerPacket.setPosition(0);
3095
headerPacket.writeLongInt(splitSize);
3097
headerPacket.writeByte(this.packetSequence);
3098
System.arraycopy(origPacketBytes, originalPacketPos,
3099
headerPacketBytes, 4, splitSize);
3101
int packetLen = splitSize + HEADER_LENGTH;
3104
// Swap a compressed packet in, if we're using
3107
if (!this.useCompression) {
3108
this.mysqlOutput.write(headerPacketBytes, 0,
3109
splitSize + HEADER_LENGTH);
3110
this.mysqlOutput.flush();
3112
Buffer packetToSend;
3114
headerPacket.setPosition(0);
3115
packetToSend = compressPacket(headerPacket, HEADER_LENGTH,
3116
splitSize, HEADER_LENGTH);
3117
packetLen = packetToSend.getPosition();
3119
this.mysqlOutput.write(packetToSend.getByteBuffer(), 0,
3121
this.mysqlOutput.flush();
3124
originalPacketPos += splitSize;
3129
// Write last packet
3131
headerPacket.clear();
3132
headerPacket.setPosition(0);
3133
headerPacket.writeLongInt(len - HEADER_LENGTH);
3134
this.packetSequence++;
3135
headerPacket.writeByte(this.packetSequence);
3138
System.arraycopy(origPacketBytes, originalPacketPos,
3139
headerPacketBytes, 4, len - HEADER_LENGTH);
3142
int packetLen = len - HEADER_LENGTH;
3145
// Swap a compressed packet in, if we're using
3148
if (!this.useCompression) {
3149
this.mysqlOutput.write(headerPacket.getByteBuffer(), 0, len);
3150
this.mysqlOutput.flush();
3152
Buffer packetToSend;
3154
headerPacket.setPosition(0);
3155
packetToSend = compressPacket(headerPacket, HEADER_LENGTH,
3156
packetLen, HEADER_LENGTH);
3157
packetLen = packetToSend.getPosition();
3159
this.mysqlOutput.write(packetToSend.getByteBuffer(), 0,
3161
this.mysqlOutput.flush();
3163
} catch (IOException ioEx) {
3164
throw new CommunicationsException(this.connection,
3165
this.lastPacketSentTimeMs, ioEx);
3169
private void reclaimLargeSharedSendPacket() {
3170
if ((this.sharedSendPacket != null) &&
3171
(this.sharedSendPacket.getCapacity() > 1048576)) {
3172
this.sharedSendPacket = new Buffer(INITIAL_PACKET_SIZE);
3176
boolean hadWarnings() {
3177
return this.hadWarnings;
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);
3189
* Secure authentication for 4.1 and newer servers.
3191
* @param packet DOCUMENT ME!
3195
* @param database DOCUMENT ME!
3196
* @param writeClientParams
3198
* @throws SQLException
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);
3208
if (writeClientParams) {
3209
if (this.use41Extensions) {
3210
if (versionMeetsMinimum(4, 1, 1)) {
3211
packet.writeLong(this.clientParam);
3212
packet.writeLong(this.maxThreeBytes);
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);
3219
// Set of bytes reserved for future use.
3220
packet.writeBytesNoNull(new byte[23]);
3222
packet.writeLong(this.clientParam);
3223
packet.writeLong(this.maxThreeBytes);
3226
packet.writeInt((int) this.clientParam);
3227
packet.writeLongInt(this.maxThreeBytes);
3231
// User/Password data
3232
packet.writeString(user, "Cp1252", this.connection);
3234
if (password.length() != 0) {
3235
/* Prepare false scramble */
3236
packet.writeString(FALSE_SCRAMBLE, "Cp1252", this.connection);
3238
/* For empty password*/
3239
packet.writeString("", "Cp1252", this.connection); //$NON-NLS-1$
3242
if (this.useConnectWithDb) {
3243
packet.writeString(database, "Cp1252", this.connection);
3246
send(packet, packet.getPosition());
3249
// Don't continue stages if password is empty
3251
if (password.length() > 0) {
3252
Buffer b = readPacket();
3256
byte[] replyAsBytes = b.getByteBuffer();
3258
if ((replyAsBytes.length == 25) && (replyAsBytes[0] != 0)) {
3259
// Old passwords will have '*' at the first byte of hash */
3260
if (replyAsBytes[0] != '*') {
3262
/* Build full password hash as it is required to decode scramble */
3263
byte[] buff = Security.passwordHashStage1(password);
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);
3269
/* Finally hash complete password using hash we got from server */
3270
passwordHash = Security.passwordHashStage2(passwordHash,
3273
byte[] packetDataAfterSalt = new byte[replyAsBytes.length -
3276
System.arraycopy(replyAsBytes, 4, packetDataAfterSalt,
3277
0, replyAsBytes.length - 5);
3279
byte[] mysqlScrambleBuff = new byte[20];
3281
/* Decypt and store scramble 4 = hash for stage2 */
3282
Security.passwordCrypt(packetDataAfterSalt,
3283
mysqlScrambleBuff, passwordHash, 20);
3285
/* Encode scramble with password. Recycle buffer */
3286
Security.passwordCrypt(mysqlScrambleBuff, buff, buff, 20);
3288
Buffer packet2 = new Buffer(25);
3289
packet2.writeBytesNoNull(buff);
3291
this.packetSequence++;
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);
3301
/* Create password to decode scramble */
3302
byte[] passwordHash = Security.createKeyFromOldPassword(password);
3304
/* Decypt and store scramble 4 = hash for stage2 */
3305
byte[] netReadPos4 = new byte[replyAsBytes.length - 5];
3307
System.arraycopy(replyAsBytes, 4, netReadPos4, 0,
3308
replyAsBytes.length - 5);
3310
byte[] mysqlScrambleBuff = new byte[20];
3312
/* Decypt and store scramble 4 = hash for stage2 */
3313
Security.passwordCrypt(netReadPos4, mysqlScrambleBuff,
3316
/* Finally scramble decoded scramble with password */
3317
String scrambledPassword = Util.scramble(new String(
3318
mysqlScrambleBuff), password);
3320
Buffer packet2 = new Buffer(packLength);
3321
packet2.writeString(scrambledPassword, "Cp1252", this.connection);
3322
this.packetSequence++;
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);
3336
* Secure authentication for 4.1.1 and newer servers.
3338
* @param packet DOCUMENT ME!
3342
* @param database DOCUMENT ME!
3343
* @param writeClientParams
3345
* @throws SQLException
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)
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)
3358
// // this three steps are done in scramble()
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);
3372
if (writeClientParams) {
3373
if (this.use41Extensions) {
3374
if (versionMeetsMinimum(4, 1, 1)) {
3375
packet.writeLong(this.clientParam);
3376
packet.writeLong(this.maxThreeBytes);
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);
3383
// Set of bytes reserved for future use.
3384
packet.writeBytesNoNull(new byte[23]);
3386
packet.writeLong(this.clientParam);
3387
packet.writeLong(this.maxThreeBytes);
3390
packet.writeInt((int) this.clientParam);
3391
packet.writeLongInt(this.maxThreeBytes);
3395
// User/Password data
3396
packet.writeString(user);
3398
if (password.length() != 0) {
3399
packet.writeByte((byte) 0x14);
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);
3409
/* For empty password*/
3410
packet.writeByte((byte) 0);
3413
if (this.useConnectWithDb) {
3414
packet.writeString(database);
3417
send(packet, packet.getPosition());
3419
byte savePacketSequence = this.packetSequence++;
3421
Buffer reply = checkErrorPacket();
3423
if (reply.isLastDataPacket()) {
3425
By sending this very specific reply server asks us to send scrambled
3426
password in old format. The reply contains scramble_323.
3428
this.packetSequence = ++savePacketSequence;
3431
String seed323 = this.seed.substring(0, 8);
3432
packet.writeString(Util.newCrypt(password, seed323));
3433
send(packet, packet.getPosition());
3435
/* Read what server thinks about out new auth message report */
3441
* Un-packs binary-encoded result set data for one row
3445
* @param resultSetConcurrency DOCUMENT ME!
3449
* @throws SQLException DOCUMENT ME!
3451
private final Object[] unpackBinaryResultSetRow(Field[] fields,
3452
Buffer binaryData, int resultSetConcurrency) throws SQLException {
3453
int numFields = fields.length;
3455
Object[] unpackedRowData = new Object[numFields];
3458
// Unpack the null bitmask, first
3461
/* Reserve place for null-marker bytes */
3462
int nullCount = (numFields + 9) / 8;
3464
byte[] nullBitMask = new byte[nullCount];
3466
for (int i = 0; i < nullCount; i++) {
3467
nullBitMask[i] = binaryData.readByte();
3470
int nullMaskPos = 0;
3471
int bit = 4; // first two bits are reserved for future use
3474
// TODO: Benchmark if moving check for updatable result
3475
// sets out of loop is worthwhile?
3478
for (int i = 0; i < numFields; i++) {
3479
if ((nullBitMask[nullMaskPos] & bit) != 0) {
3480
unpackedRowData[i] = null;
3482
if (resultSetConcurrency != ResultSet.CONCUR_UPDATABLE) {
3483
extractNativeEncodedColumn(binaryData, fields, i,
3486
unpackNativeEncodedColumn(binaryData, fields, i,
3491
if (((bit <<= 1) & 255) == 0) {
3492
bit = 1; /* To next byte */
3498
return unpackedRowData;
3502
private final void extractNativeEncodedColumn(Buffer binaryData,
3503
Field[] fields, int columnIndex, Object[] unpackedRowData) throws SQLException {
3504
Field curField = fields[columnIndex];
3506
switch (curField.getMysqlType()) {
3507
case MysqlDefs.FIELD_TYPE_NULL:
3508
break; // for dummy binds
3510
case MysqlDefs.FIELD_TYPE_TINY:
3512
unpackedRowData[columnIndex] = new byte[] {binaryData.readByte()};
3515
case MysqlDefs.FIELD_TYPE_SHORT:
3516
case MysqlDefs.FIELD_TYPE_YEAR:
3518
unpackedRowData[columnIndex] = binaryData.getBytes(2);
3520
case MysqlDefs.FIELD_TYPE_LONG:
3521
case MysqlDefs.FIELD_TYPE_INT24:
3523
unpackedRowData[columnIndex] = binaryData.getBytes(4);
3525
case MysqlDefs.FIELD_TYPE_LONGLONG:
3527
unpackedRowData[columnIndex] = binaryData.getBytes(8);
3529
case MysqlDefs.FIELD_TYPE_FLOAT:
3531
unpackedRowData[columnIndex] = binaryData.getBytes(4);
3533
case MysqlDefs.FIELD_TYPE_DOUBLE:
3535
unpackedRowData[columnIndex] = binaryData.getBytes(8);
3537
case MysqlDefs.FIELD_TYPE_TIME:
3539
int length = (int) binaryData.readFieldLength();
3541
unpackedRowData[columnIndex] = binaryData.getBytes(length);
3544
case MysqlDefs.FIELD_TYPE_DATE:
3546
length = (int) binaryData.readFieldLength();
3548
unpackedRowData[columnIndex] = binaryData.getBytes(length);
3551
case MysqlDefs.FIELD_TYPE_DATETIME:
3552
case MysqlDefs.FIELD_TYPE_TIMESTAMP:
3553
length = (int) binaryData.readFieldLength();
3555
unpackedRowData[columnIndex] = binaryData.getBytes(length);
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);
3570
case MysqlDefs.FIELD_TYPE_BIT:
3571
unpackedRowData[columnIndex] = binaryData.readLenByteArray(0);
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);
3584
private final void unpackNativeEncodedColumn(Buffer binaryData,
3585
Field[] fields, int columnIndex, Object[] unpackedRowData)
3586
throws SQLException {
3587
Field curField = fields[columnIndex];
3589
switch (curField.getMysqlType()) {
3590
case MysqlDefs.FIELD_TYPE_NULL:
3591
break; // for dummy binds
3593
case MysqlDefs.FIELD_TYPE_TINY:
3595
byte tinyVal = binaryData.readByte();
3597
if (!curField.isUnsigned()) {
3598
unpackedRowData[columnIndex] = String.valueOf(tinyVal)
3601
short unsignedTinyVal = (short) (tinyVal & 0xff);
3603
unpackedRowData[columnIndex] = String.valueOf(unsignedTinyVal)
3609
case MysqlDefs.FIELD_TYPE_SHORT:
3610
case MysqlDefs.FIELD_TYPE_YEAR:
3612
short shortVal = (short) binaryData.readInt();
3614
if (!curField.isUnsigned()) {
3615
unpackedRowData[columnIndex] = String.valueOf(shortVal)
3618
int unsignedShortVal = shortVal & 0xffff;
3620
unpackedRowData[columnIndex] = String.valueOf(unsignedShortVal)
3626
case MysqlDefs.FIELD_TYPE_LONG:
3627
case MysqlDefs.FIELD_TYPE_INT24:
3629
int intVal = (int) binaryData.readLong();
3631
if (!curField.isUnsigned()) {
3632
unpackedRowData[columnIndex] = String.valueOf(intVal)
3635
long longVal = intVal & 0xffffffffL;
3637
unpackedRowData[columnIndex] = String.valueOf(longVal)
3643
case MysqlDefs.FIELD_TYPE_LONGLONG:
3645
long longVal = binaryData.readLongLong();
3647
if (!curField.isUnsigned()) {
3648
unpackedRowData[columnIndex] = String.valueOf(longVal)
3651
BigInteger asBigInteger = ResultSet.convertLongToUlong(longVal);
3653
unpackedRowData[columnIndex] = asBigInteger.toString()
3659
case MysqlDefs.FIELD_TYPE_FLOAT:
3661
float floatVal = Float.intBitsToFloat(binaryData.readIntAsLong());
3663
unpackedRowData[columnIndex] = String.valueOf(floatVal).getBytes();
3667
case MysqlDefs.FIELD_TYPE_DOUBLE:
3669
double doubleVal = Double.longBitsToDouble(binaryData.readLongLong());
3671
unpackedRowData[columnIndex] = String.valueOf(doubleVal).getBytes();
3675
case MysqlDefs.FIELD_TYPE_TIME:
3677
int length = (int) binaryData.readFieldLength();
3684
binaryData.readByte(); // skip tm->neg
3685
binaryData.readLong(); // skip daysPart
3686
hour = binaryData.readByte();
3687
minute = binaryData.readByte();
3688
seconds = binaryData.readByte();
3691
binaryData.readLong(); // ignore 'secondsPart'
3696
byte[] timeAsBytes = new byte[8];
3698
timeAsBytes[0] = (byte) Character.forDigit(hour / 10, 10);
3699
timeAsBytes[1] = (byte) Character.forDigit(hour % 10, 10);
3701
timeAsBytes[2] = (byte) ':';
3703
timeAsBytes[3] = (byte) Character.forDigit(minute / 10,
3705
timeAsBytes[4] = (byte) Character.forDigit(minute % 10,
3708
timeAsBytes[5] = (byte) ':';
3710
timeAsBytes[6] = (byte) Character.forDigit(seconds / 10,
3712
timeAsBytes[7] = (byte) Character.forDigit(seconds % 10,
3715
unpackedRowData[columnIndex] = timeAsBytes;
3720
case MysqlDefs.FIELD_TYPE_DATE:
3721
length = (int) binaryData.readFieldLength();
3732
year = binaryData.readInt();
3733
month = binaryData.readByte();
3734
day = binaryData.readByte();
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;
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);
3755
byte[] dateAsBytes = new byte[10];
3757
dateAsBytes[0] = (byte) Character.forDigit(year / 1000,
3760
int after1000 = year % 1000;
3762
dateAsBytes[1] = (byte) Character.forDigit(after1000 / 100,
3765
int after100 = after1000 % 100;
3767
dateAsBytes[2] = (byte) Character.forDigit(after100 / 10,
3769
dateAsBytes[3] = (byte) Character.forDigit(after100 % 10,
3772
dateAsBytes[4] = (byte) '-';
3774
dateAsBytes[5] = (byte) Character.forDigit(month / 10,
3776
dateAsBytes[6] = (byte) Character.forDigit(month % 10,
3779
dateAsBytes[7] = (byte) '-';
3781
dateAsBytes[8] = (byte) Character.forDigit(day / 10, 10);
3782
dateAsBytes[9] = (byte) Character.forDigit(day % 10, 10);
3784
unpackedRowData[columnIndex] = dateAsBytes;
3789
case MysqlDefs.FIELD_TYPE_DATETIME:
3790
case MysqlDefs.FIELD_TYPE_TIMESTAMP:
3791
length = (int) binaryData.readFieldLength();
3804
year = binaryData.readInt();
3805
month = binaryData.readByte();
3806
day = binaryData.readByte();
3809
hour = binaryData.readByte();
3810
minute = binaryData.readByte();
3811
seconds = binaryData.readByte();
3815
// nanos = (int)binaryData.readLong();
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;
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);
3837
int stringLength = 19;
3839
byte[] nanosAsBytes = Integer.toString(nanos).getBytes();
3841
stringLength += (1 + nanosAsBytes.length); // '.' + # of digits
3843
byte[] datetimeAsBytes = new byte[stringLength];
3845
datetimeAsBytes[0] = (byte) Character.forDigit(year / 1000,
3848
after1000 = year % 1000;
3850
datetimeAsBytes[1] = (byte) Character.forDigit(after1000 / 100,
3853
after100 = after1000 % 100;
3855
datetimeAsBytes[2] = (byte) Character.forDigit(after100 / 10,
3857
datetimeAsBytes[3] = (byte) Character.forDigit(after100 % 10,
3860
datetimeAsBytes[4] = (byte) '-';
3862
datetimeAsBytes[5] = (byte) Character.forDigit(month / 10,
3864
datetimeAsBytes[6] = (byte) Character.forDigit(month % 10,
3867
datetimeAsBytes[7] = (byte) '-';
3869
datetimeAsBytes[8] = (byte) Character.forDigit(day / 10,
3871
datetimeAsBytes[9] = (byte) Character.forDigit(day % 10,
3874
datetimeAsBytes[10] = (byte) ' ';
3876
datetimeAsBytes[11] = (byte) Character.forDigit(hour / 10,
3878
datetimeAsBytes[12] = (byte) Character.forDigit(hour % 10,
3881
datetimeAsBytes[13] = (byte) ':';
3883
datetimeAsBytes[14] = (byte) Character.forDigit(minute / 10,
3885
datetimeAsBytes[15] = (byte) Character.forDigit(minute % 10,
3888
datetimeAsBytes[16] = (byte) ':';
3890
datetimeAsBytes[17] = (byte) Character.forDigit(seconds / 10,
3892
datetimeAsBytes[18] = (byte) Character.forDigit(seconds % 10,
3895
datetimeAsBytes[19] = (byte) '.';
3897
int nanosOffset = 20;
3899
for (int j = 0; j < nanosAsBytes.length; j++) {
3900
datetimeAsBytes[nanosOffset + j] = nanosAsBytes[j];
3903
unpackedRowData[columnIndex] = datetimeAsBytes;
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);
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);
3933
* Optimization to only use one calendar per-session, or calculate it
3934
* for each call, depending on user configuration
3936
private Calendar getCalendarInstanceForSessionOrNew() {
3937
if (this.connection.getDynamicCalendars()) {
3938
return Calendar.getInstance();
3940
return this.sessionCalendar;
3945
* Negotiates the SSL communications channel used when connecting
3946
* to a MySQL server that understands SSL.
3952
* @throws SQLException
3953
* @throws CommunicationsException
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);
3963
boolean doSecureAuth = false;
3965
if ((this.serverCapabilities & CLIENT_SECURE_CONNECTION) != 0) {
3966
this.clientParam |= CLIENT_SECURE_CONNECTION;
3967
doSecureAuth = true;
3970
this.clientParam |= CLIENT_SSL;
3972
Buffer packet = new Buffer(packLength);
3974
if (this.use41Extensions) {
3975
packet.writeLong(this.clientParam);
3977
packet.writeInt((int) this.clientParam);
3980
send(packet, packet.getPosition());
3982
ExportControlled.transformSocketToSSLSocket(this);
3987
if (versionMeetsMinimum(4, 1, 1)) {
3988
secureAuth411(null, packLength, user, password, database, true);
3990
secureAuth411(null, packLength, user, password, database, true);
3993
if (this.use41Extensions) {
3994
packet.writeLong(this.clientParam);
3995
packet.writeLong(this.maxThreeBytes);
3997
packet.writeInt((int) this.clientParam);
3998
packet.writeLongInt(this.maxThreeBytes);
4001
// User/Password data
4002
packet.writeString(user);
4004
if (this.protocolVersion > 9) {
4005
packet.writeString(Util.newCrypt(password, this.seed));
4007
packet.writeString(Util.oldCrypt(password, this.seed));
4010
if (((this.serverCapabilities & CLIENT_CONNECT_WITH_DB) != 0) &&
4011
(database != null) && (database.length() > 0)) {
4012
packet.writeString(database);
4015
send(packet, packet.getPosition());
4019
protected int getServerStatus() {
4020
return this.serverStatus;
4023
protected List fetchRowsViaCursor(List fetchedRows, long statementId,
4024
Field[] columnTypes, int fetchSize) throws SQLException {
4026
if (fetchedRows == null) {
4027
fetchedRows = new ArrayList(fetchSize);
4029
fetchedRows.clear();
4032
this.sharedSendPacket.clear();
4034
this.sharedSendPacket.writeByte((byte) MysqlDefs.COM_FETCH);
4035
this.sharedSendPacket.writeLong(statementId);
4036
this.sharedSendPacket.writeLong(fetchSize);
4038
sendCommand(MysqlDefs.COM_FETCH, null, this.sharedSendPacket, true,
4041
Object[] row = null;
4043
while ((row = nextRow(columnTypes, columnTypes.length, true,
4044
ResultSet.CONCUR_READ_ONLY)) != null) {
4045
fetchedRows.add(row);
4051
protected long getThreadId() {
4052
return this.threadId;
4055
protected boolean useNanosForElapsedTime() {
4056
return this.useNanosForElapsedTime;
4059
protected long getSlowQueryThreshold() {
4060
return this.slowQueryThreshold;
4063
protected String getQueryTimingUnits() {
4064
return this.queryTimingUnits;