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

Save This Page
Home » MySQL-JDBC-5.1.11 » com.mysql.jdbc » [javadoc | source]