MysqlIO.java
上传用户:sxlinghang
上传日期:2022-07-20
资源大小:1405k
文件大小:86k
源码类别:

数据库编程

开发平台:

Java

  1. /*
  2.    Copyright (C) 2002 MySQL AB
  3.       This program is free software; you can redistribute it and/or modify
  4.       it under the terms of the GNU General Public License as published by
  5.       the Free Software Foundation; either version 2 of the License, or
  6.       (at your option) any later version.
  7.       This program is distributed in the hope that it will be useful,
  8.       but WITHOUT ANY WARRANTY; without even the implied warranty of
  9.       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  10.       GNU General Public License for more details.
  11.       You should have received a copy of the GNU General Public License
  12.       along with this program; if not, write to the Free Software
  13.       Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  14.  */
  15. package com.mysql.jdbc;
  16. import java.io.BufferedInputStream;
  17. import java.io.BufferedOutputStream;
  18. import java.io.ByteArrayOutputStream;
  19. import java.io.EOFException;
  20. import java.io.FileInputStream;
  21. import java.io.IOException;
  22. import java.io.InputStream;
  23. import java.io.OutputStreamWriter;
  24. import java.lang.ref.SoftReference;
  25. import java.net.Socket;
  26. import java.security.NoSuchAlgorithmException;
  27. import java.sql.SQLException;
  28. import java.sql.SQLWarning;
  29. import java.util.ArrayList;
  30. import java.util.Properties;
  31. import java.util.zip.Deflater;
  32. import java.util.zip.Inflater;
  33. /**
  34.  * This class is used by Connection for communicating with the MySQL server.
  35.  *
  36.  * @author Mark Matthews
  37.  * @version $Id: MysqlIO.java,v 1.32.2.46 2004/02/06 00:54:42 mmatthew Exp $
  38.  *
  39.  * @see java.sql.Connection
  40.  */
  41. public class MysqlIO {
  42.     static final int NULL_LENGTH = ~0;
  43.     static final int COMP_HEADER_LENGTH = 3;
  44.     static final int MIN_COMPRESS_LEN = 50;
  45.     static final int HEADER_LENGTH = 4;
  46.     private static int maxBufferSize = 65535;
  47.     private static final int CLIENT_COMPRESS = 32; /* Can use compression
  48.     protcol */
  49.     private static final int CLIENT_CONNECT_WITH_DB = 8;
  50.     private static final int CLIENT_FOUND_ROWS = 2;
  51.     private static final int CLIENT_IGNORE_SPACE = 256; /* Ignore spaces
  52.     before '(' */
  53.     private static final int CLIENT_LOCAL_FILES = 128; /* Can use LOAD DATA
  54.     LOCAL */
  55.     /* Found instead of
  56.        affected rows */
  57.     private static final int CLIENT_LONG_FLAG = 4; /* Get all column flags */
  58.     private static final int CLIENT_LONG_PASSWORD = 1; /* new more secure
  59.     passwords */
  60.     private static final int CLIENT_PROTOCOL_41 = 512; // for > 4.1.1
  61.     private static final int CLIENT_INTERACTIVE = 1024;
  62.     private static final int CLIENT_SSL = 2048;
  63.     private static final int CLIENT_RESERVED = 16384; // for 4.1.0 only
  64.     private static final int CLIENT_SECURE_CONNECTION = 32768;
  65.     private static final String FALSE_SCRAMBLE = "xxxxxxxx";
  66.     /**
  67.      * We store the platform 'encoding' here, only used to avoid munging
  68.      * filenames for LOAD DATA LOCAL INFILE...
  69.      */
  70.     private static String jvmPlatformCharset = null;
  71.     static {
  72.         OutputStreamWriter outWriter = null;
  73.         //
  74.         // Use the I/O system to get the encoding (if possible), to avoid
  75.         // security restrictions on System.getProperty("file.encoding") in
  76.         // applets (why is that restricted?)
  77.         //
  78.         try {
  79.             outWriter = new OutputStreamWriter(new ByteArrayOutputStream());
  80.             jvmPlatformCharset = outWriter.getEncoding();
  81.         } finally {
  82.             try {
  83.                 outWriter.close();
  84.             } catch (IOException ioEx) {
  85.                 // ignore
  86.             }
  87.         }
  88.     }
  89.     //
  90.     // Use this when reading in rows to avoid thousands of new()
  91.     // calls, because the byte arrays just get copied out of the
  92.     // packet anyway
  93.     //
  94.     private Buffer reusablePacket = null;
  95.     private Buffer sendPacket = null;
  96.     private Buffer sharedSendPacket = null;
  97.     /** Data to the server */
  98.     //private DataOutputStream     _Mysql_Output             = null;
  99.     private BufferedOutputStream mysqlOutput = null;
  100.     private com.mysql.jdbc.Connection connection;
  101.     private Deflater deflater = null;
  102.     private Inflater inflater = null;
  103.     /** Buffered data from the server */
  104.     //private BufferedInputStream  _Mysql_Buf_Input          = null;
  105.     /** Buffered data to the server */
  106.     //private BufferedOutputStream _Mysql_Buf_Output         = null;
  107.     /** Data from the server */
  108.     //private DataInputStream      _Mysql_Input              = null;
  109.     private InputStream mysqlInput = null;
  110.     private RowData streamingData = null;
  111.     //
  112.     // For SQL Warnings
  113.     //
  114.     private SQLWarning warningChain = null;
  115.     /** The connection to the server */
  116.     private Socket mysqlConnection = null;
  117.     private SocketFactory socketFactory = null;
  118.     //
  119.     // Packet used for 'LOAD DATA LOCAL INFILE'
  120.     //
  121.     // We use a SoftReference, so that we don't penalize intermittent
  122.     // use of this feature
  123.     //
  124.     private SoftReference loadFileBufRef;
  125.     //
  126.     // Used to send large packets to the server versions 4+
  127.     // We use a SoftReference, so that we don't penalize intermittent
  128.     // use of this feature
  129.     //
  130.     private SoftReference splitBufRef;
  131.     private String host = null;
  132.     private String seed;
  133.     private String serverVersion = null;
  134.     private String socketFactoryClassName = null;
  135.     private byte[] packetHeaderBuf = new byte[4];
  136.     private boolean clearStreamBeforeEachQuery = false;
  137.     private boolean colDecimalNeedsBump = false; // do we need to increment the colDecimal flag?
  138.     private boolean has41NewNewProt = false;
  139.     /** Does the server support long column info? */
  140.     private boolean hasLongColumnInfo = false;
  141.     private boolean isInteractiveClient = false;
  142.     /**
  143.      * Does the character set of this connection match the character set of the
  144.      * platform
  145.      */
  146.     private boolean platformDbCharsetMatches = true;
  147.     private boolean profileSql = false;
  148.     /** Should we use 4.1 protocol extensions? */
  149.     private boolean use41Extensions = false;
  150.     private boolean useCompression = false;
  151.     private boolean useNewLargePackets = false;
  152.     private boolean useNewUpdateCounts = false; // should we use the new larger update counts?
  153.     private byte packetSequence = 0;
  154.     private byte protocolVersion = 0;
  155.     private int clientParam = 0;
  156.     // changed once we've connected.
  157.     private int maxAllowedPacket = 1024 * 1024;
  158.     private int maxThreeBytes = 255 * 255 * 255;
  159.     private int port = 3306;
  160.     private int serverMajorVersion = 0;
  161.     private int serverMinorVersion = 0;
  162.     private int serverSubMinorVersion = 0;
  163.     /**
  164.      * Constructor:  Connect to the MySQL server and setup a stream connection.
  165.      *
  166.      * @param host the hostname to connect to
  167.      * @param port the port number that the server is listening on
  168.      * @param socketFactoryClassName the socket factory to use
  169.      * @param props the Properties from DriverManager.getConnection()
  170.      * @param conn the Connection that is creating us
  171.      * @param socketTimeout the timeout to set for the socket (0 means no
  172.      *        timeout)
  173.      *
  174.      * @throws IOException if an IOException occurs during connect.
  175.      * @throws java.sql.SQLException if a database access error occurs.
  176.      */
  177.     protected MysqlIO(String host, int port, String socketFactoryClassName,
  178.         Properties props, com.mysql.jdbc.Connection conn, int socketTimeout)
  179.         throws IOException, java.sql.SQLException {
  180.         this.connection = conn;
  181.         this.reusablePacket = new Buffer(this.connection.getNetBufferLength());
  182.         this.port = port;
  183.         this.host = host;
  184.         this.socketFactoryClassName = socketFactoryClassName;
  185.         this.socketFactory = createSocketFactory();
  186.         this.mysqlConnection = socketFactory.connect(this.host, props);
  187.         this.clearStreamBeforeEachQuery = this.connection.alwaysClearStream();
  188.         if (socketTimeout != 0) {
  189.             try {
  190.                 this.mysqlConnection.setSoTimeout(socketTimeout);
  191.             } catch (Exception ex) {
  192.                 /* Ignore if the platform does not support it */
  193.             }
  194.         }
  195.         this.mysqlConnection = this.socketFactory.beforeHandshake();
  196.         if (!this.connection.isUsingUnbufferedInput()) {
  197.             this.mysqlInput = new BufferedInputStream(this.mysqlConnection
  198.                     .getInputStream(), 16384);
  199.         } else {
  200.             this.mysqlInput = this.mysqlConnection.getInputStream();
  201.         }
  202.         this.mysqlOutput = new BufferedOutputStream(this.mysqlConnection
  203.                 .getOutputStream(), 16384);
  204.         this.isInteractiveClient = this.connection.isInteractiveClient();
  205.     }
  206.     /**
  207.      * Should the driver generate SQL statement profiles?
  208.      *
  209.      * @param flag should the driver enable profiling?
  210.      */
  211.     protected void setProfileSql(boolean flag) {
  212.         this.profileSql = flag;
  213.     }
  214.     /**
  215.      * Build a result set. Delegates to buildResultSetWithRows() to build a
  216.      * JDBC-version-specific ResultSet, given rows as byte data, and field
  217.      * information.
  218.      *
  219.      * @param columnCount the number of columns in the result set
  220.      * @param maxRows the maximum number of rows to read (-1 means all rows)
  221.      * @param resultSetType the type of result set (CONCUR_UPDATABLE or
  222.      *        READ_ONLY)
  223.      * @param streamResults should the result set be read all at once, or
  224.      *        streamed?
  225.      * @param catalog the database name in use when the result set was created
  226.      *
  227.      * @return a result set
  228.      *
  229.      * @throws Exception if a database access error occurs
  230.      */
  231.     protected ResultSet getResultSet(long columnCount, int maxRows,
  232.         int resultSetType, boolean streamResults, String catalog)
  233.         throws Exception {
  234.         Buffer packet; // The packet from the server
  235.         Field[] fields = new Field[(int) columnCount];
  236.         // Read in the column information
  237.         for (int i = 0; i < columnCount; i++) {
  238.             packet = readPacket();
  239.             fields[i] = unpackField(packet, false);
  240.         }
  241.         packet = reuseAndReadPacket(this.reusablePacket);
  242.         RowData rowData = null;
  243.         if (!streamResults) {
  244.             ArrayList rows = new ArrayList();
  245.             // Now read the data
  246.             byte[][] rowBytes = nextRow((int) columnCount);
  247.             int rowCount = 0;
  248.             if (rowBytes != null) {
  249.                 rows.add(rowBytes);
  250.                 rowCount = 1;
  251.             }
  252.             while ((rowBytes != null) && (rowCount < maxRows)) {
  253.                 rowBytes = nextRow((int) columnCount);
  254.                 if (rowBytes != null) {
  255.                     rows.add(rowBytes);
  256.                     rowCount++;
  257.                 } else {
  258.                     if (Driver.TRACE) {
  259.                         Debug.msg(this, "* NULL Row *");
  260.                     }
  261.                 }
  262.             }
  263.             //
  264.             // Clear any outstanding data left on the wire
  265.             // when we've artifically limited the number of 
  266.             // rows we retrieve (fix for BUG#1695)
  267.             //
  268.             if (rowCount <= maxRows) {
  269.                 clearInputStream();
  270.             }
  271.             if (Driver.TRACE) {
  272.                 Debug.msg(this,
  273.                     "* Fetched " + rows.size() + " rows from server *");
  274.             }
  275.             rowData = new RowDataStatic(rows);
  276.             reclaimLargeReusablePacket();
  277.         } else {
  278.             rowData = new RowDataDynamic(this, (int) columnCount);
  279.             this.streamingData = rowData;
  280.         }
  281.         return buildResultSetWithRows(catalog, fields, rowData, resultSetType);
  282.     }
  283.     /**
  284.      * Forcibly closes the underlying socket to MySQL.
  285.      */
  286.     protected final void forceClose() {
  287.         try {
  288.             if (this.mysqlInput != null) {
  289.                 this.mysqlInput.close();
  290.             }
  291.         } catch (IOException ioEx) {
  292.             // we can't do anything constructive about this
  293.             // Let the JVM clean it up later
  294.             this.mysqlInput = null;
  295.         }
  296.         try {
  297.             if (this.mysqlOutput != null) {
  298.                 this.mysqlOutput.close();
  299.             }
  300.         } catch (IOException ioEx) {
  301.             // we can't do anything constructive about this
  302.             // Let the JVM clean it up later
  303.             this.mysqlOutput = null;
  304.         }
  305.         try {
  306.             if (this.mysqlConnection != null) {
  307.                 this.mysqlConnection.close();
  308.             }
  309.         } catch (IOException ioEx) {
  310.             // we can't do anything constructive about this
  311.             // Let the JVM clean it up later
  312.             this.mysqlConnection = null;
  313.         }
  314.     }
  315.     /**
  316.      * Does the server send back extra column info?
  317.      *
  318.      * @return true if so
  319.      */
  320.     protected boolean hasLongColumnInfo() {
  321.         return this.hasLongColumnInfo;
  322.     }
  323.     /**
  324.      * Unpacks the Field information from the given packet. Understands pre 4.1
  325.      * and post 4.1 server version field packet structures.
  326.      *
  327.      * @param packet the packet containing the field information
  328.      * @param extractDefaultValues should default values be extracted?
  329.      *
  330.      * @return the unpacked field
  331.      */
  332.     protected final Field unpackField(Buffer packet,
  333.         boolean extractDefaultValues) {
  334.         if (this.use41Extensions) {
  335.             // we only store the position of the string and
  336.             // materialize only if needed...
  337.             if (this.has41NewNewProt) {
  338.                 int catalogNameStart = packet.getPosition() + 1;
  339.                 int catalogNameLength = packet.fastSkipLenString();
  340.             }
  341.             int databaseNameStart = packet.getPosition() + 1;
  342.             int databaseNameLength = packet.fastSkipLenString();
  343.             int tableNameStart = packet.getPosition() + 1;
  344.             int tableNameLength = packet.fastSkipLenString();
  345.             // orgTableName is never used so skip
  346.             int originalTableNameStart = packet.getPosition() + 1;
  347.             int originalTableNameLength = packet.fastSkipLenString();
  348.             // we only store the position again...
  349.             int nameStart = packet.getPosition() + 1;
  350.             int nameLength = packet.fastSkipLenString();
  351.             // orgColName is not required so skip...
  352.             int originalColumnNameStart = packet.getPosition() + 1;
  353.             int originalColumnNameLength = packet.fastSkipLenString();
  354.             packet.readByte();
  355.             int charSetNumber = packet.readInt();
  356.             int colLength = 0;
  357.             if (this.has41NewNewProt) {
  358.                 // fixme
  359.                 colLength = (int) packet.readLong();
  360.             } else {
  361.                 colLength = packet.readLongInt();
  362.             }
  363.             int colType = packet.readByte() & 0xff;
  364.             short colFlag = 0;
  365.             if (this.hasLongColumnInfo) {
  366.                 colFlag = (short) (packet.readInt());
  367.             } else {
  368.                 colFlag = (short) (packet.readByte() & 0xff);
  369.             }
  370.             int colDecimals = packet.readByte() & 0xff;
  371.             int defaultValueStart = -1;
  372.             int defaultValueLength = -1;
  373.             if (extractDefaultValues) {
  374.                 defaultValueStart = packet.getPosition() + 1;
  375.                 defaultValueLength = packet.fastSkipLenString();
  376.             }
  377.             Field field = new Field(this.connection, packet.getByteBuffer(),
  378.                     databaseNameStart, databaseNameLength, tableNameStart,
  379.                     tableNameLength, originalTableNameStart,
  380.                     originalTableNameLength, nameStart, nameLength,
  381.                     originalColumnNameStart, originalColumnNameLength,
  382.                     colLength, colType, colFlag, colDecimals,
  383.                     defaultValueStart, defaultValueLength, charSetNumber);
  384.             return field;
  385.         } else {
  386.             int tableNameStart = packet.getPosition() + 1;
  387.             int tableNameLength = packet.fastSkipLenString();
  388.             int nameStart = packet.getPosition() + 1;
  389.             int nameLength = packet.fastSkipLenString();
  390.             int colLength = packet.readnBytes();
  391.             int colType = packet.readnBytes();
  392.             packet.readByte(); // We know it's currently 2
  393.             short colFlag = 0;
  394.             if (this.hasLongColumnInfo) {
  395.                 colFlag = (short) (packet.readInt());
  396.             } else {
  397.                 colFlag = (short) (packet.readByte() & 0xff);
  398.             }
  399.             int colDecimals = (packet.readByte() & 0xff);
  400.             if (this.colDecimalNeedsBump) {
  401.                 colDecimals++;
  402.             }
  403.             Field field = new Field(this.connection, packet.getBufferSource(),
  404.                     nameStart, nameLength, tableNameStart, tableNameLength,
  405.                     colLength, colType, colFlag, colDecimals);
  406.             return field;
  407.         }
  408.     }
  409.     /**
  410.      * Determines if the database charset is the same as the platform charset
  411.      */
  412.     protected void checkForCharsetMismatch() {
  413.         if (this.connection.useUnicode()
  414.                 && (this.connection.getEncoding() != null)) {
  415.             String encodingToCheck = jvmPlatformCharset;
  416.             if (encodingToCheck == null) {
  417.                 encodingToCheck = System.getProperty("file.encoding");
  418.             }
  419.             if (encodingToCheck == null) {
  420.                 this.platformDbCharsetMatches = false;
  421.             } else {
  422.                 this.platformDbCharsetMatches = encodingToCheck.equals(this.connection
  423.                         .getEncoding());
  424.             }
  425.         }
  426.     }
  427.     static int getMaxBuf() {
  428.         return maxBufferSize;
  429.     }
  430.     /**
  431.      * Get the major version of the MySQL server we are talking to.
  432.      *
  433.      * @return DOCUMENT ME!
  434.      */
  435.     final int getServerMajorVersion() {
  436.         return this.serverMajorVersion;
  437.     }
  438.     /**
  439.      * Get the minor version of the MySQL server we are talking to.
  440.      *
  441.      * @return DOCUMENT ME!
  442.      */
  443.     final int getServerMinorVersion() {
  444.         return this.serverMinorVersion;
  445.     }
  446.     /**
  447.      * Get the sub-minor version of the MySQL server we are talking to.
  448.      *
  449.      * @return DOCUMENT ME!
  450.      */
  451.     final int getServerSubMinorVersion() {
  452.         return this.serverSubMinorVersion;
  453.     }
  454.     /**
  455.      * Get the version string of the server we are talking to
  456.      *
  457.      * @return DOCUMENT ME!
  458.      */
  459.     String getServerVersion() {
  460.         return this.serverVersion;
  461.     }
  462.     /**
  463.      * Initialize communications with the MySQL server. Handles logging on, and
  464.      * handling initial connection errors.
  465.      *
  466.      * @param user DOCUMENT ME!
  467.      * @param password DOCUMENT ME!
  468.      * @param database DOCUMENT ME!
  469.      *
  470.      * @throws java.sql.SQLException DOCUMENT ME!
  471.      * @throws SQLException DOCUMENT ME!
  472.      */
  473.     void doHandshake(String user, String password, String database)
  474.         throws java.sql.SQLException {
  475.         // Read the first packet
  476.         Buffer buf = readPacket();
  477.         // Get the protocol version
  478.         this.protocolVersion = buf.readByte();
  479.         if (this.protocolVersion == -1) {
  480.             try {
  481.                 this.mysqlConnection.close();
  482.             } catch (Exception e) {
  483.                 ; // ignore
  484.             }
  485.             int errno = 2000;
  486.             errno = buf.readInt();
  487.             String serverErrorMessage = buf.readString();
  488.             StringBuffer errorBuf = new StringBuffer(" message from server: "");
  489.             errorBuf.append(serverErrorMessage);
  490.             errorBuf.append(""");
  491.             String xOpen = SQLError.mysqlToXOpen(errno);
  492.             throw new SQLException(SQLError.get(xOpen) + ", "
  493.                 + errorBuf.toString(), xOpen, errno);
  494.         }
  495.         this.serverVersion = buf.readString();
  496.         // Parse the server version into major/minor/subminor
  497.         int point = this.serverVersion.indexOf(".");
  498.         if (point != -1) {
  499.             try {
  500.                 int n = Integer.parseInt(this.serverVersion.substring(0, point));
  501.                 this.serverMajorVersion = n;
  502.             } catch (NumberFormatException NFE1) {
  503.                 ;
  504.             }
  505.             String remaining = this.serverVersion.substring(point + 1,
  506.                     this.serverVersion.length());
  507.             point = remaining.indexOf(".");
  508.             if (point != -1) {
  509.                 try {
  510.                     int n = Integer.parseInt(remaining.substring(0, point));
  511.                     this.serverMinorVersion = n;
  512.                 } catch (NumberFormatException nfe) {
  513.                     ;
  514.                 }
  515.                 remaining = remaining.substring(point + 1, remaining.length());
  516.                 int pos = 0;
  517.                 while (pos < remaining.length()) {
  518.                     if ((remaining.charAt(pos) < '0')
  519.                             || (remaining.charAt(pos) > '9')) {
  520.                         break;
  521.                     }
  522.                     pos++;
  523.                 }
  524.                 try {
  525.                     int n = Integer.parseInt(remaining.substring(0, pos));
  526.                     this.serverSubMinorVersion = n;
  527.                 } catch (NumberFormatException nfe) {
  528.                     ;
  529.                 }
  530.             }
  531.         }
  532.         if (versionMeetsMinimum(4, 0, 8)) {
  533.             this.maxThreeBytes = (256 * 256 * 256) - 1;
  534.             this.useNewLargePackets = true;
  535.         } else {
  536.             this.maxThreeBytes = 255 * 255 * 255;
  537.             this.useNewLargePackets = false;
  538.         }
  539.         this.colDecimalNeedsBump = versionMeetsMinimum(3, 23, 0);
  540.         this.colDecimalNeedsBump = !versionMeetsMinimum(3, 23, 15); // guess? Not noted in changelog
  541.         this.useNewUpdateCounts = versionMeetsMinimum(3, 22, 5);
  542.         long threadId = buf.readLong();
  543.         seed = buf.readString();
  544.         if (Driver.TRACE) {
  545.             Debug.msg(this, "Protocol Version: " + (int) this.protocolVersion);
  546.             Debug.msg(this, "Server Version: " + this.serverVersion);
  547.             Debug.msg(this, "Thread ID: " + threadId);
  548.             Debug.msg(this, "Crypt Seed: " + seed);
  549.         }
  550.         int serverCapabilities = 0;
  551.         if (buf.getPosition() < buf.getBufLength()) {
  552.             serverCapabilities = buf.readInt();
  553.         }
  554.         if (versionMeetsMinimum(4, 1, 1)) {
  555.             int position = buf.getPosition();
  556.             /* New protocol with 16 bytes to describe server characteristics */
  557.             int serverLanguage = buf.readInt(); // 2 bytes
  558.             buf.readInt();
  559.             buf.setPosition(position + 16);
  560.             String seedPart2 = buf.readString();
  561.             StringBuffer newSeed = new StringBuffer(20);
  562.             newSeed.append(seed);
  563.             newSeed.append(seedPart2);
  564.             this.seed = newSeed.toString();
  565.         }
  566.         if (((serverCapabilities & CLIENT_COMPRESS) != 0)
  567.                 && this.connection.useCompression()) {
  568.             clientParam |= CLIENT_COMPRESS;
  569.         }
  570.         if ((database != null) && (database.length() > 0)) {
  571.             clientParam |= CLIENT_CONNECT_WITH_DB;
  572.         }
  573.         if (((serverCapabilities & CLIENT_SSL) == 0)
  574.                 && this.connection.useSSL()) {
  575.             this.connection.setUseSSL(false);
  576.         }
  577.         if ((serverCapabilities & CLIENT_LONG_FLAG) != 0) {
  578.             // We understand other column flags, as well
  579.             clientParam |= CLIENT_LONG_FLAG;
  580.             this.hasLongColumnInfo = true;
  581.         }
  582.         // return FOUND rows
  583.         clientParam |= CLIENT_FOUND_ROWS;
  584.         if (this.connection.allowLoadLocalInfile()) {
  585.             clientParam |= CLIENT_LOCAL_FILES;
  586.         }
  587.         if (isInteractiveClient) {
  588.             clientParam |= CLIENT_INTERACTIVE;
  589.         }
  590.         // Authenticate
  591.         if (this.protocolVersion > 9) {
  592.             clientParam |= CLIENT_LONG_PASSWORD; // for long passwords
  593.         } else {
  594.             clientParam &= ~CLIENT_LONG_PASSWORD;
  595.         }
  596.         //
  597.         // 4.1 has some differences in the protocol
  598.         //
  599.         if (versionMeetsMinimum(4, 1, 0)) {
  600.             if (versionMeetsMinimum(4, 1, 1)) {
  601.                 clientParam |= CLIENT_PROTOCOL_41;
  602.                 this.has41NewNewProt = true;
  603.             } else {
  604.                 clientParam |= CLIENT_RESERVED;
  605.                 this.has41NewNewProt = false;
  606.             }
  607.             this.use41Extensions = true;
  608.         }
  609.         int passwordLength = 16;
  610.         int userLength = 0;
  611.         int databaseLength = 0;
  612.         if (user != null) {
  613.             userLength = user.length();
  614.         }
  615.         if (database != null) {
  616.             databaseLength = database.length();
  617.         }
  618.         int packLength = (userLength + passwordLength + databaseLength) + 7
  619.             + HEADER_LENGTH;
  620.         Buffer packet = null;
  621.         if (!connection.useSSL()) {
  622.             if ((serverCapabilities & CLIENT_SECURE_CONNECTION) != 0) {
  623.                 clientParam |= CLIENT_SECURE_CONNECTION;
  624.                 if (versionMeetsMinimum(4, 1, 1)) {
  625.                     secureAuth411(packLength, serverCapabilities, clientParam,
  626.                         user, password, database);
  627.                 } else {
  628.                     secureAuth(packLength, serverCapabilities, clientParam,
  629.                         user, password, database);
  630.                 }
  631.             } else {
  632.                 packet = new Buffer(packLength);
  633.                 if ((clientParam & CLIENT_RESERVED) != 0) {
  634.                     if (versionMeetsMinimum(4, 1, 1)) {
  635.                         packet.writeLong(clientParam);
  636.                         packet.writeLong(this.maxThreeBytes);
  637.                         // charset, JDBC will connect as 'latin1',
  638.                         // and use 'SET NAMES' to change to the desired
  639.                         // charset after the connection is established.
  640.                         packet.writeByte((byte) 8);
  641.                         // Set of bytes reserved for future use.
  642.                         packet.writeBytesNoNull(new byte[23]);
  643.                     } else {
  644.                         packet.writeLong(clientParam);
  645.                         packet.writeLong(this.maxThreeBytes);
  646.                     }
  647.                 } else {
  648.                     packet.writeInt((int) clientParam);
  649.                     packet.writeLongInt(this.maxThreeBytes);
  650.                 }
  651.                 // User/Password data
  652.                 packet.writeString(user);
  653.                 if (this.protocolVersion > 9) {
  654.                     packet.writeString(Util.newCrypt(password, this.seed));
  655.                 } else {
  656.                     packet.writeString(Util.oldCrypt(password, this.seed));
  657.                 }
  658.                 if (((serverCapabilities & CLIENT_CONNECT_WITH_DB) != 0)
  659.                         && (database != null) && (database.length() > 0)) {
  660.                     packet.writeString(database);
  661.                 }
  662.                 send(packet);
  663.             }
  664.         } else {
  665.             boolean doSecureAuth = false;
  666.             if ((serverCapabilities & CLIENT_SECURE_CONNECTION) != 0) {
  667.                 clientParam |= CLIENT_SECURE_CONNECTION;
  668.                 doSecureAuth = true;
  669.             }
  670.             clientParam |= CLIENT_SSL;
  671.             packet = new Buffer(packLength);
  672.             if ((clientParam & CLIENT_RESERVED) != 0) {
  673.                 packet.writeLong(clientParam);
  674.             } else {
  675.                 packet.writeInt((int) clientParam);
  676.             }
  677.             send(packet);
  678.             javax.net.ssl.SSLSocketFactory sslFact = (javax.net.ssl.SSLSocketFactory) javax.net.ssl.SSLSocketFactory
  679.                 .getDefault();
  680.             try {
  681.                 this.mysqlConnection = sslFact.createSocket(this.mysqlConnection,
  682.                         this.host, this.port, true);
  683.                 // need to force TLSv1, or else JSSE tries to do a SSLv2 handshake
  684.                 // which MySQL doesn't understand
  685.                 ((javax.net.ssl.SSLSocket) this.mysqlConnection)
  686.                 .setEnabledProtocols(new String[] { "TLSv1" });
  687.                 ((javax.net.ssl.SSLSocket) this.mysqlConnection).startHandshake();
  688.                 this.mysqlInput = new BufferedInputStream(this.mysqlConnection
  689.                         .getInputStream(), 16384);
  690.                 this.mysqlOutput = new BufferedOutputStream(this.mysqlConnection
  691.                         .getOutputStream(), 16384);
  692.                 this.mysqlOutput.flush();
  693.             } catch (IOException ioEx) {
  694.                 StringBuffer message = new StringBuffer(SQLError.get(
  695.                             SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE));
  696.                 message.append(": ");
  697.                 message.append(ioEx.getClass().getName());
  698.                 message.append(", underlying cause: ");
  699.                 message.append(ioEx.getMessage());
  700.                 if (!this.connection.useParanoidErrorMessages()) {
  701.                     message.append(Util.stackTraceToString(ioEx));
  702.                 }
  703.                 throw new java.sql.SQLException(message.toString(),
  704.                     SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE, 0);
  705.             }
  706.             packet.clear();
  707.             if (doSecureAuth) {
  708.                 if (versionMeetsMinimum(4, 1, 1)) {
  709.                     secureAuth411(packLength, serverCapabilities, clientParam,
  710.                         user, password, database);
  711.                 } else {
  712.                     secureAuth(packLength, serverCapabilities, clientParam,
  713.                         user, password, database);
  714.                 }
  715.             } else {
  716.                 if ((clientParam & CLIENT_RESERVED) != 0) {
  717.                     packet.writeLong(clientParam);
  718.                     packet.writeLong(this.maxThreeBytes);
  719.                 } else {
  720.                     packet.writeInt((int) clientParam);
  721.                     packet.writeLongInt(this.maxThreeBytes);
  722.                 }
  723.                 // User/Password data
  724.                 packet.writeString(user);
  725.                 if (this.protocolVersion > 9) {
  726.                     packet.writeString(Util.newCrypt(password, seed));
  727.                 } else {
  728.                     packet.writeString(Util.oldCrypt(password, seed));
  729.                 }
  730.                 if (((serverCapabilities & CLIENT_CONNECT_WITH_DB) != 0)
  731.                         && (database != null) && (database.length() > 0)) {
  732.                     packet.writeString(database);
  733.                 }
  734.                 send(packet);
  735.             }
  736.         }
  737.         // Check for errors, not for 4.1.1 or newer,
  738.         // as the new auth protocol doesn't work that way
  739.         // (see secureAuth411() for more details...)
  740.         if (!versionMeetsMinimum(4, 1, 1)) {
  741.             checkErrorPacket();
  742.         }
  743.         //
  744.         // Can't enable compression until after handshake
  745.         //
  746.         if (((serverCapabilities & CLIENT_COMPRESS) != 0)
  747.                 && this.connection.useCompression()) {
  748.             // The following matches with ZLIB's
  749.             // compress()
  750.             this.deflater = new Deflater();
  751.             this.useCompression = true;
  752.             this.mysqlInput = new CompressedInputStream(this.mysqlInput);
  753.         }
  754.         if (((serverCapabilities & CLIENT_CONNECT_WITH_DB) == 0)
  755.                 && (database != null) && (database.length() > 0)) {
  756.             try {
  757.                 sendCommand(MysqlDefs.INIT_DB, database, null);
  758.             } catch (Exception ex) {
  759.                 throw new SQLException(ex.toString(),
  760.                     SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE);
  761.             }
  762.         }
  763.     }
  764.     /**
  765.      * Retrieve one row from the MySQL server. Note: this method is not
  766.      * thread-safe, but it is only called from methods that are guarded by
  767.      * synchronizing on this object.
  768.      *
  769.      * @param columnCount DOCUMENT ME!
  770.      *
  771.      * @return DOCUMENT ME!
  772.      *
  773.      * @throws Exception DOCUMENT ME!
  774.      */
  775.     final byte[][] nextRow(int columnCount) throws Exception {
  776.         // Get the next incoming packet, re-using the packet because
  777.         // all the data we need gets copied out of it.
  778.         Buffer rowPacket = checkErrorPacket();
  779.         //
  780.         // Didn't read an error, so re-position to beginning
  781.         // of packet in order to read result set data
  782.         //
  783.         int offset = 0;
  784.         //if (rowPacket.wasMultiPacket()) {
  785.         //    if (this.useNewLargePackets) {
  786.         //        offset = HEADER_LENGTH;
  787.         //    } else {
  788.         //        offset = HEADER_LENGTH + 1;
  789.         //   }
  790.         //}
  791.         rowPacket.setPosition(rowPacket.getPosition() - 1);
  792.         byte[][] rowData = new byte[columnCount][];
  793.         if (!rowPacket.isLastDataPacket()) {
  794.             for (int i = 0; i < columnCount; i++) {
  795.                 rowData[i] = rowPacket.readLenByteArray(offset);
  796.                 if (Driver.TRACE) {
  797.                     if (rowData[i] == null) {
  798.                         Debug.msg(this, "Field value: NULL");
  799.                     } else {
  800.                         Debug.msg(this, "Field value: " + rowData[i].toString());
  801.                     }
  802.                 }
  803.             }
  804.             return rowData;
  805.         }
  806.         return null;
  807.     }
  808.     /**
  809.      * Log-off of the MySQL server and close the socket.
  810.      *
  811.      * @throws SQLException DOCUMENT ME!
  812.      */
  813.     final void quit() throws SQLException {
  814.         Buffer packet = new Buffer(6);
  815.         this.packetSequence = -1;
  816.         packet.writeByte((byte) MysqlDefs.QUIT);
  817.         send(packet);
  818.         forceClose();
  819.     }
  820.     /**
  821.      * Returns the packet used for sending data (used by PreparedStatement)
  822.      * Guarded by external synchronization on a mutex.
  823.      *
  824.      * @return A packet to send data with
  825.      */
  826.     Buffer getSharedSendPacket() {
  827.         if (this.sharedSendPacket == null) {
  828.             this.sharedSendPacket = new Buffer(this.connection
  829.                     .getNetBufferLength());
  830.         }
  831.         return this.sharedSendPacket;
  832.     }
  833.     void closeStreamer(RowData streamer) throws SQLException {
  834.         if (this.streamingData == null) {
  835.             throw new SQLException("Attempt to close streaming result set "
  836.                 + streamer
  837.                 + " when no streaming  result set was registered. This is an internal error.");
  838.         }
  839.         if (streamer != this.streamingData) {
  840.             throw new SQLException("Attempt to close streaming result set "
  841.                 + streamer + " that was not registered."
  842.                 + " Only one streaming result set may be open and in use per-connection. Ensure that you have called .close() on "
  843.                 + " any active result sets before attempting more queries.");
  844.         }
  845.         this.streamingData = null;
  846.     }
  847.     /**
  848.      * Sets the buffer size to max-buf
  849.      */
  850.     void resetMaxBuf() {
  851.         this.maxAllowedPacket = this.connection.getMaxAllowedPacket();
  852.     }
  853.     /**
  854.      * Send a command to the MySQL server If data is to be sent with command,
  855.      * it should be put in ExtraData Raw packets can be sent by setting
  856.      * QueryPacket to something other than null.
  857.      *
  858.      * @param command DOCUMENT ME!
  859.      * @param extraData DOCUMENT ME!
  860.      * @param queryPacket DOCUMENT ME!
  861.      *
  862.      * @return DOCUMENT ME!
  863.      *
  864.      * @throws Exception DOCUMENT ME!
  865.      * @throws java.sql.SQLException DOCUMENT ME!
  866.      */
  867.     final Buffer sendCommand(int command, String extraData, Buffer queryPacket)
  868.         throws Exception {
  869.         checkForOutstandingStreamingData();
  870.         try {
  871.             if (this.clearStreamBeforeEachQuery) {
  872.                 clearInputStream();
  873.             }
  874.             //
  875.             // PreparedStatements construct their own packets,
  876.             // for efficiency's sake.
  877.             //
  878.             // If this is a generic query, we need to re-use
  879.             // the sending packet.
  880.             //
  881.             if (queryPacket == null) {
  882.                 int packLength = HEADER_LENGTH + COMP_HEADER_LENGTH + 1
  883.                     + ((extraData != null) ? extraData.length() : 0) + 2;
  884.                 if (this.sendPacket == null) {
  885.                     this.sendPacket = new Buffer(packLength);
  886.                 }
  887.                 this.packetSequence = -1;
  888.                 this.sendPacket.clear();
  889.                 // Offset different for compression
  890.                 if (this.useCompression) {
  891.                     this.sendPacket.setPosition(this.sendPacket.getPosition()
  892.                         + COMP_HEADER_LENGTH);
  893.                 }
  894.                 this.sendPacket.writeByte((byte) command);
  895.                 if ((command == MysqlDefs.INIT_DB)
  896.                         || (command == MysqlDefs.CREATE_DB)
  897.                         || (command == MysqlDefs.DROP_DB)
  898.                         || (command == MysqlDefs.QUERY)) {
  899.                     this.sendPacket.writeStringNoNull(extraData);
  900.                 } else if (command == MysqlDefs.PROCESS_KILL) {
  901.                     long id = new Long(extraData).longValue();
  902.                     this.sendPacket.writeLong(id);
  903.                 } else if ((command == MysqlDefs.RELOAD)
  904.                         && (this.protocolVersion > 9)) {
  905.                     Debug.msg(this, "Reload");
  906.                     //Packet.writeByte(reloadParam);
  907.                 }
  908.                 send(this.sendPacket);
  909.             } else {
  910.                 this.packetSequence = -1;
  911.                 send(queryPacket); // packet passed by PreparedStatement
  912.             }
  913.         } catch (SQLException sqlEx) {
  914.             // don't wrap SQLExceptions
  915.             throw sqlEx;
  916.         } catch (Exception ex) {
  917.             String underlyingMessage = ex.getMessage();
  918.             throw new java.sql.SQLException(SQLError.get(
  919.                     SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE) + ": "
  920.                 + ex.getClass().getName() + ", "
  921.                 + ((underlyingMessage != null) ? underlyingMessage
  922.                                                : "no message given by JVM"),
  923.                 SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE, 0);
  924.         }
  925.         return checkErrorPacket(command);
  926.     }
  927.     /**
  928.      * Send a query specified in the String "Query" to the MySQL server. This
  929.      * method uses the specified character encoding to get the bytes from the
  930.      * query string.
  931.      *
  932.      * @param query DOCUMENT ME!
  933.      * @param maxRows DOCUMENT ME!
  934.      * @param characterEncoding DOCUMENT ME!
  935.      * @param conn DOCUMENT ME!
  936.      * @param resultSetType DOCUMENT ME!
  937.      * @param streamResults DOCUMENT ME!
  938.      * @param catalog DOCUMENT ME!
  939.      *
  940.      * @return DOCUMENT ME!
  941.      *
  942.      * @throws Exception DOCUMENT ME!
  943.      */
  944.     final ResultSet sqlQuery(String query, int maxRows,
  945.         String characterEncoding, Connection conn, int resultSetType,
  946.         boolean streamResults, String catalog) throws Exception {
  947.         // We don't know exactly how many bytes we're going to get
  948.         // from the query. Since we're dealing with Unicode, the
  949.         // max is 2, so pad it (2 * query) + space for headers
  950.         int packLength = HEADER_LENGTH + 1 + (query.length() * 2) + 2;
  951.         if (this.sendPacket == null) {
  952.             this.sendPacket = new Buffer(packLength);
  953.         } else {
  954.             this.sendPacket.clear();
  955.         }
  956.         this.sendPacket.writeByte((byte) MysqlDefs.QUERY);
  957.         if (characterEncoding != null) {
  958.             SingleByteCharsetConverter converter = this.connection
  959.                 .getCharsetConverter(characterEncoding);
  960.             if (this.platformDbCharsetMatches) {
  961.                 this.sendPacket.writeStringNoNull(query, characterEncoding,
  962.                     converter);
  963.             } else {
  964.                 if (StringUtils.startsWithIgnoreCaseAndWs(query, "LOAD DATA")) {
  965.                     this.sendPacket.writeBytesNoNull(query.getBytes());
  966.                 } else {
  967.                     this.sendPacket.writeStringNoNull(query, characterEncoding,
  968.                         converter);
  969.                 }
  970.             }
  971.         } else {
  972.             this.sendPacket.writeStringNoNull(query);
  973.         }
  974.         return sqlQueryDirect(this.sendPacket, maxRows, conn, resultSetType,
  975.             streamResults, catalog);
  976.     }
  977.     /**
  978.      * Send a query stored in a packet directly to the server.
  979.      *
  980.      * @param queryPacket DOCUMENT ME!
  981.      * @param maxRows DOCUMENT ME!
  982.      * @param conn DOCUMENT ME!
  983.      * @param resultSetType DOCUMENT ME!
  984.      * @param streamResults DOCUMENT ME!
  985.      * @param catalog DOCUMENT ME!
  986.      *
  987.      * @return DOCUMENT ME!
  988.      *
  989.      * @throws Exception DOCUMENT ME!
  990.      */
  991.     final ResultSet sqlQueryDirect(Buffer queryPacket, int maxRows,
  992.         Connection conn, int resultSetType, boolean streamResults,
  993.         String catalog) throws Exception {
  994.         StringBuffer profileMsgBuf = null; // used if profiling
  995.         long queryStartTime = 0;
  996.         if (this.profileSql) {
  997.             profileMsgBuf = new StringBuffer();
  998.             queryStartTime = System.currentTimeMillis();
  999.             byte[] queryBuf = queryPacket.getByteBuffer();
  1000.             // Extract the actual query from the network packet
  1001.             String query = new String(queryBuf, 5,
  1002.                     (queryPacket.getPosition() - 5));
  1003.             profileMsgBuf.append("Queryt"");
  1004.             profileMsgBuf.append(query);
  1005.             profileMsgBuf.append(""texecution time:t");
  1006.         }
  1007.         // Send query command and sql query string
  1008.         Buffer resultPacket = sendCommand(MysqlDefs.QUERY, null, queryPacket);
  1009.         if (this.profileSql) {
  1010.             long executionTime = System.currentTimeMillis() - queryStartTime;
  1011.             profileMsgBuf.append(executionTime);
  1012.             profileMsgBuf.append("t");
  1013.         }
  1014.         resultPacket.setPosition(resultPacket.getPosition() - 1);
  1015.         long columnCount = resultPacket.readFieldLength();
  1016.         if (Driver.TRACE) {
  1017.             Debug.msg(this, "Column count: " + columnCount);
  1018.         }
  1019.         if (columnCount == 0) {
  1020.             if (this.profileSql) {
  1021.                 System.err.println(profileMsgBuf.toString());
  1022.             }
  1023.             return buildResultSetWithUpdates(resultPacket);
  1024.         } else if (columnCount == Buffer.NULL_LENGTH) {
  1025.             String charEncoding = null;
  1026.             if (this.connection.useUnicode()) {
  1027.                 charEncoding = this.connection.getEncoding();
  1028.             }
  1029.             String fileName = null;
  1030.             if (this.platformDbCharsetMatches) {
  1031.                 fileName = ((charEncoding != null)
  1032.                     ? resultPacket.readString(charEncoding)
  1033.                     : resultPacket.readString());
  1034.             } else {
  1035.                 fileName = resultPacket.readString();
  1036.             }
  1037.             return sendFileToServer(fileName);
  1038.         } else {
  1039.             long fetchStartTime = 0;
  1040.             if (this.profileSql) {
  1041.                 fetchStartTime = System.currentTimeMillis();
  1042.             }
  1043.             com.mysql.jdbc.ResultSet results = getResultSet(columnCount,
  1044.                     maxRows, resultSetType, streamResults, catalog);
  1045.             if (this.profileSql) {
  1046.                 long fetchElapsedTime = System.currentTimeMillis()
  1047.                     - fetchStartTime;
  1048.                 profileMsgBuf.append("result set fetch time:t");
  1049.                 profileMsgBuf.append(fetchElapsedTime);
  1050.                 System.err.println(profileMsgBuf.toString());
  1051.             }
  1052.             return results;
  1053.         }
  1054.     }
  1055.     /**
  1056.      * Returns the host this IO is connected to
  1057.      *
  1058.      * @return DOCUMENT ME!
  1059.      */
  1060.     String getHost() {
  1061.         return this.host;
  1062.     }
  1063.     /**
  1064.      * Does the version of the MySQL server we are connected to meet the given
  1065.      * minimums?
  1066.      *
  1067.      * @param major DOCUMENT ME!
  1068.      * @param minor DOCUMENT ME!
  1069.      * @param subminor DOCUMENT ME!
  1070.      *
  1071.      * @return DOCUMENT ME!
  1072.      */
  1073.     boolean versionMeetsMinimum(int major, int minor, int subminor) {
  1074.         if (getServerMajorVersion() >= major) {
  1075.             if (getServerMajorVersion() == major) {
  1076.                 if (getServerMinorVersion() >= minor) {
  1077.                     if (getServerMinorVersion() == minor) {
  1078.                         return (getServerSubMinorVersion() >= subminor);
  1079.                     } else {
  1080.                         // newer than major.minor
  1081.                         return true;
  1082.                     }
  1083.                 } else {
  1084.                     // older than major.minor
  1085.                     return false;
  1086.                 }
  1087.             } else {
  1088.                 // newer than major
  1089.                 return true;
  1090.             }
  1091.         } else {
  1092.             return false;
  1093.         }
  1094.     }
  1095.     private final int readFully(InputStream in, byte[] b, int off, int len)
  1096.         throws IOException {
  1097.         if (len < 0) {
  1098.             throw new IndexOutOfBoundsException();
  1099.         }
  1100.         int n = 0;
  1101.         while (n < len) {
  1102.             int count = in.read(b, off + n, len - n);
  1103.             if (count < 0) {
  1104.                 throw new EOFException();
  1105.             }
  1106.             n += count;
  1107.         }
  1108.         return n;
  1109.     }
  1110.     /**
  1111.      * Read one packet from the MySQL server
  1112.      *
  1113.      * @return DOCUMENT ME!
  1114.      *
  1115.      * @throws SQLException DOCUMENT ME!
  1116.      * @throws java.sql.SQLException DOCUMENT ME!
  1117.      */
  1118.     private final Buffer readPacket() throws SQLException {
  1119.         try {
  1120.             int lengthRead = readFully(mysqlInput, this.packetHeaderBuf, 0, 4);
  1121.             if (lengthRead < 4) {
  1122.                 forceClose();
  1123.                 throw new IOException("Unexpected end of input stream");
  1124.             }
  1125.             int packetLength = ((int) (this.packetHeaderBuf[0] & 0xff))
  1126.                 + (((int) (this.packetHeaderBuf[1] & 0xff)) << 8)
  1127.                 + (((int) (this.packetHeaderBuf[2] & 0xff)) << 16);
  1128.             byte multiPacketSeq = this.packetHeaderBuf[3];
  1129.             // Read data
  1130.             byte[] buffer = new byte[packetLength + 1];
  1131.             readFully(this.mysqlInput, buffer, 0, packetLength);
  1132.             buffer[packetLength] = 0;
  1133.             Buffer packet = new Buffer(buffer);
  1134.             return packet;
  1135.         } catch (IOException ioEx) {
  1136.             StringBuffer message = new StringBuffer(SQLError.get(
  1137.                         SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE));
  1138.             message.append(": ");
  1139.             message.append(ioEx.getClass().getName());
  1140.             message.append(", underlying cause: ");
  1141.             message.append(ioEx.getMessage());
  1142.             if (!this.connection.useParanoidErrorMessages()) {
  1143.                 message.append(Util.stackTraceToString(ioEx));
  1144.             }
  1145.             throw new java.sql.SQLException(message.toString(),
  1146.                 SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE, 0);
  1147.         }
  1148.     }
  1149.     private com.mysql.jdbc.ResultSet buildResultSetWithRows(String catalog,
  1150.         com.mysql.jdbc.Field[] fields, RowData rows, int resultSetConcurrency)
  1151.         throws SQLException {
  1152.         switch (resultSetConcurrency) {
  1153.         case java.sql.ResultSet.CONCUR_READ_ONLY:
  1154.             return new com.mysql.jdbc.ResultSet(catalog, fields, rows,
  1155.                 this.connection);
  1156.         case java.sql.ResultSet.CONCUR_UPDATABLE:
  1157.             return new com.mysql.jdbc.UpdatableResultSet(catalog, fields, rows,
  1158.                 this.connection);
  1159.         default:
  1160.             return new com.mysql.jdbc.ResultSet(catalog, fields, rows,
  1161.                 this.connection);
  1162.         }
  1163.     }
  1164.     private com.mysql.jdbc.ResultSet buildResultSetWithUpdates(
  1165.         Buffer resultPacket) throws SQLException {
  1166.         long updateCount = -1;
  1167.         long updateID = -1;
  1168.         String info = null;
  1169.         try {
  1170.             if (this.useNewUpdateCounts) {
  1171.                 updateCount = resultPacket.newReadLength();
  1172.                 updateID = resultPacket.newReadLength();
  1173.             } else {
  1174.                 updateCount = (long) resultPacket.readLength();
  1175.                 updateID = (long) resultPacket.readLength();
  1176.             }
  1177.             if (this.connection.isReadInfoMsgEnabled()) {
  1178.                 if (this.use41Extensions) {
  1179.                     int serverStatus = resultPacket.readInt();
  1180.                     int warningCount = resultPacket.readInt();
  1181.                     resultPacket.readByte(); // advance pointer
  1182.                 }
  1183.                 info = resultPacket.readString();
  1184.             }
  1185.         } catch (Exception ex) {
  1186.             throw new java.sql.SQLException(SQLError.get(
  1187.                     SQLError.SQL_STATE_GENERAL_ERROR) + ": "
  1188.                 + ex.getClass().getName(), SQLError.SQL_STATE_GENERAL_ERROR, -1);
  1189.         }
  1190.         if (Driver.TRACE) {
  1191.             Debug.msg(this, "Update Count = " + updateCount);
  1192.         }
  1193.         ResultSet updateRs = new ResultSet(updateCount, updateID);
  1194.         if (info != null) {
  1195.             updateRs.setServerInfo(info);
  1196.         }
  1197.         return updateRs;
  1198.     }
  1199.     /**
  1200.      * Don't hold on to overly-large packets
  1201.      */
  1202.     private void reclaimLargeReusablePacket() {
  1203.         if ((this.reusablePacket != null)
  1204.                 && (this.reusablePacket.getBufLength() > 1048576)) {
  1205.             this.reusablePacket = new Buffer(this.connection.getNetBufferLength());
  1206.         }
  1207.     }
  1208.     /**
  1209.      * Re-use a packet to read from the MySQL server
  1210.      *
  1211.      * @param reuse DOCUMENT ME!
  1212.      *
  1213.      * @return DOCUMENT ME!
  1214.      *
  1215.      * @throws SQLException DOCUMENT ME!
  1216.      * @throws SQLException DOCUMENT ME!
  1217.      */
  1218.     private final Buffer reuseAndReadPacket(Buffer reuse)
  1219.         throws SQLException {
  1220.         try {
  1221.             reuse.setWasMultiPacket(false);
  1222.             int lengthRead = readFully(mysqlInput, this.packetHeaderBuf, 0, 4);
  1223.             if (lengthRead < 4) {
  1224.                 forceClose();
  1225.                 throw new IOException("Unexpected end of input stream");
  1226.             }
  1227.             int packetLength = ((int) (this.packetHeaderBuf[0] & 0xff))
  1228.                 + (((int) (this.packetHeaderBuf[1] & 0xff)) << 8)
  1229.                 + (((int) (this.packetHeaderBuf[2] & 0xff)) << 16);
  1230.             byte multiPacketSeq = this.packetHeaderBuf[3];
  1231.             //byte multiPacketSeq = (byte) this.mysqlInput.read();
  1232.             // Set the Buffer to it's original state
  1233.             reuse.setPosition(0);
  1234.             reuse.setSendLength(0);
  1235.             // Do we need to re-alloc the byte buffer?
  1236.             //
  1237.             // Note: We actually check the length of the buffer,
  1238.             // rather than getBufLength(), because getBufLength() is not
  1239.             // necesarily the actual length of the byte array
  1240.             // used as the buffer
  1241.             if (reuse.getByteBuffer().length <= packetLength) {
  1242.                 reuse.setByteBuffer(new byte[packetLength + 1]);
  1243.             }
  1244.             // Set the new length
  1245.             reuse.setBufLength(packetLength);
  1246.             // Read the data from the server
  1247.             readFully(this.mysqlInput, reuse.getByteBuffer(), 0, packetLength);
  1248.             boolean isMultiPacket = false;
  1249.             if (packetLength == maxThreeBytes) {
  1250.                 reuse.setPosition((int) maxThreeBytes);
  1251.                 int packetEndPoint = packetLength;
  1252.                 // it's multi-packet
  1253.                 isMultiPacket = true;
  1254.                 lengthRead = readFully(mysqlInput, this.packetHeaderBuf, 0, 4);
  1255.                 if (lengthRead < 4) {
  1256.                     forceClose();
  1257.                     throw new IOException("Unexpected end of input stream");
  1258.                 }
  1259.                 packetLength = ((int) (this.packetHeaderBuf[0] & 0xff))
  1260.                     + (((int) (this.packetHeaderBuf[1] & 0xff)) << 8)
  1261.                     + (((int) (this.packetHeaderBuf[2] & 0xff)) << 16);
  1262.                 Buffer multiPacket = new Buffer(packetLength);
  1263.                 boolean firstMultiPkt = true;
  1264.                 while (true) {
  1265.                     if (!firstMultiPkt) {
  1266.                         lengthRead = readFully(mysqlInput,
  1267.                                 this.packetHeaderBuf, 0, 4);
  1268.                         if (lengthRead < 4) {
  1269.                             forceClose();
  1270.                             throw new IOException(
  1271.                                 "Unexpected end of input stream");
  1272.                         }
  1273.                         packetLength = ((int) (this.packetHeaderBuf[0] & 0xff))
  1274.                             + (((int) (this.packetHeaderBuf[1] & 0xff)) << 8)
  1275.                             + (((int) (this.packetHeaderBuf[2] & 0xff)) << 16);
  1276.                     } else {
  1277.                         firstMultiPkt = false;
  1278.                     }
  1279.                     if (!this.useNewLargePackets && (packetLength == 1)) {
  1280.                         clearInputStream();
  1281.                         break;
  1282.                     } else if (packetLength < this.maxThreeBytes) {
  1283.                         byte newPacketSeq = this.packetHeaderBuf[3];
  1284.                         if (newPacketSeq != (multiPacketSeq + 1)) {
  1285.                             throw new IOException(
  1286.                                 "Packets received out of order");
  1287.                         }
  1288.                         multiPacketSeq = newPacketSeq;
  1289.                         // Set the Buffer to it's original state
  1290.                         multiPacket.setPosition(0);
  1291.                         multiPacket.setSendLength(0);
  1292.                         // Set the new length
  1293.                         multiPacket.setBufLength(packetLength);
  1294.                         // Read the data from the server
  1295.                         byte[] byteBuf = multiPacket.getByteBuffer();
  1296.                         int lengthToWrite = packetLength;
  1297.                         int bytesRead = readFully(this.mysqlInput, byteBuf, 0,
  1298.                                 packetLength);
  1299.                         if (bytesRead != lengthToWrite) {
  1300.                             throw new SQLException(
  1301.                                 "Short read from server, expected "
  1302.                                 + lengthToWrite + " bytes, received only "
  1303.                                 + bytesRead + ".",
  1304.                                 SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE);
  1305.                         }
  1306.                         reuse.writeBytesNoNull(byteBuf, 0, lengthToWrite);
  1307.                         packetEndPoint += lengthToWrite;
  1308.                         break; // end of multipacket sequence
  1309.                     }
  1310.                     byte newPacketSeq = this.packetHeaderBuf[3];
  1311.                     if (newPacketSeq != (multiPacketSeq + 1)) {
  1312.                         throw new IOException("Packets received out of order");
  1313.                     }
  1314.                     multiPacketSeq = newPacketSeq;
  1315.                     // Set the Buffer to it's original state
  1316.                     multiPacket.setPosition(0);
  1317.                     multiPacket.setSendLength(0);
  1318.                     // Set the new length
  1319.                     multiPacket.setBufLength(packetLength);
  1320.                     // Read the data from the server
  1321.                     byte[] byteBuf = multiPacket.getByteBuffer();
  1322.                     int lengthToWrite = packetLength;
  1323.                     int bytesRead = readFully(this.mysqlInput, byteBuf, 0,
  1324.                             packetLength);
  1325.                     if (bytesRead != lengthToWrite) {
  1326.                         throw new SQLException(
  1327.                             "Short read from server, expected " + lengthToWrite
  1328.                             + " bytes, received only " + bytesRead + ".",
  1329.                             SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE);
  1330.                     }
  1331.                     reuse.writeBytesNoNull(byteBuf, 0, lengthToWrite);
  1332.                     packetEndPoint += lengthToWrite;
  1333.                 }
  1334.                 //reuse.writeByte((byte) 0);
  1335.                 reuse.setPosition(0);
  1336.                 reuse.setWasMultiPacket(true);
  1337.             }
  1338.             if (!isMultiPacket) {
  1339.                 reuse.getByteBuffer()[packetLength] = 0; // Null-termination
  1340.             }
  1341.             return reuse;
  1342.         } catch (IOException ioEx) {
  1343.             StringBuffer message = new StringBuffer(SQLError.get(
  1344.                         SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE));
  1345.             message.append(": ");
  1346.             message.append(ioEx.getClass().getName());
  1347.             message.append(", underlying cause: ");
  1348.             message.append(ioEx.getMessage());
  1349.             if (!this.connection.useParanoidErrorMessages()) {
  1350.                 message.append(Util.stackTraceToString(ioEx));
  1351.             }
  1352.             throw new java.sql.SQLException(message.toString(),
  1353.                 SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE, 0);
  1354.         }
  1355.     }
  1356.     /**
  1357.      * Send a packet to the MySQL server
  1358.      *
  1359.      * @param packet DOCUMENT ME!
  1360.      *
  1361.      * @throws SQLException DOCUMENT ME!
  1362.      */
  1363.     private final void send(Buffer packet) throws SQLException {
  1364.         int l = packet.getPosition();
  1365.         send(packet, l);
  1366.         // 
  1367.         // Don't hold on to large packets
  1368.         //
  1369.         if (packet == this.sharedSendPacket) {
  1370.             reclaimLargeSharedSendPacket();
  1371.         }
  1372.     }
  1373.     private final void send(Buffer packet, int packetLen)
  1374.         throws SQLException {
  1375.         try {
  1376.             if (packetLen > this.maxAllowedPacket) {
  1377.                 throw new PacketTooBigException(packetLen, this.maxAllowedPacket);
  1378.             }
  1379.             if ((serverMajorVersion >= 4) && (packetLen >= maxThreeBytes)) {
  1380.                 sendSplitPackets(packet);
  1381.             } else {
  1382.                 this.packetSequence++;
  1383.                 Buffer packetToSend = packet;
  1384.                 packetToSend.setPosition(0);
  1385.                 if (this.useCompression) {
  1386.                     packetToSend = compressPacket(packet, 0, packetLen,
  1387.                             HEADER_LENGTH);
  1388.                     packetLen = packetToSend.getPosition();
  1389.                 } else {
  1390.                     packetToSend.writeLongInt(packetLen - HEADER_LENGTH);
  1391.                     packetToSend.writeByte(this.packetSequence);
  1392.                 }
  1393.                 this.mysqlOutput.write(packetToSend.getByteBuffer(), 0,
  1394.                     packetLen);
  1395.                 this.mysqlOutput.flush();
  1396.             }
  1397.             // 
  1398.             // Don't hold on to large packets
  1399.             //
  1400.             if (packet == this.sharedSendPacket) {
  1401.                 reclaimLargeSharedSendPacket();
  1402.             }
  1403.         } catch (IOException ioEx) {
  1404.             StringBuffer message = new StringBuffer(SQLError.get(
  1405.                         SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE));
  1406.             message.append(": ");
  1407.             message.append(ioEx.getClass().getName());
  1408.             message.append(", underlying cause: ");
  1409.             message.append(ioEx.getMessage());
  1410.             if (!this.connection.useParanoidErrorMessages()) {
  1411.                 message.append(Util.stackTraceToString(ioEx));
  1412.             }
  1413.             throw new java.sql.SQLException(message.toString(),
  1414.                 SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE, 0);
  1415.         }
  1416.     }
  1417.     /**
  1418.      * Reads and sends a file to the server for LOAD DATA LOCAL INFILE
  1419.      *
  1420.      * @param fileName the file name to send.
  1421.      *
  1422.      * @return DOCUMENT ME!
  1423.      *
  1424.      * @throws SQLException DOCUMENT ME!
  1425.      */
  1426.     private final ResultSet sendFileToServer(String fileName)
  1427.         throws SQLException {
  1428.         Buffer filePacket = (loadFileBufRef == null) ? null
  1429.                                                      : (Buffer) (loadFileBufRef
  1430.             .get());
  1431.         int packetLength = Math.min(this.connection.getMaxAllowedPacket()
  1432.                 - (HEADER_LENGTH * 3),
  1433.                 alignPacketSize(this.connection.getMaxAllowedPacket() - 16, 4096)
  1434.                 - (HEADER_LENGTH * 3));
  1435.         //
  1436.         // This packet may be _way_ too large to actually allocate,
  1437.         // unforunately, LOAD DATA LOCAL INFILE requires this setup...
  1438.         //
  1439.         try {
  1440.             if (filePacket == null) {
  1441.                 filePacket = new Buffer((int) (packetLength + HEADER_LENGTH));
  1442.                 loadFileBufRef = new SoftReference(filePacket);
  1443.             }
  1444.         } catch (OutOfMemoryError oom) {
  1445.             // Attempt to do this, but it might not work...
  1446.             // The server is expecting at least one packet, so we 
  1447.             // send an empty 'EOF' packet...
  1448.             this.reusablePacket.clear();
  1449.             send(this.reusablePacket);
  1450.             throw new SQLException("Unable to allocate packet of size '"
  1451.                 + (packetLength + HEADER_LENGTH)
  1452.                 + "' for LOAD DATA LOCAL INFILE. Either increase heap space available to your JVM, or adjust the MySQL server variable 'max_allowed_packet'",
  1453.                 SQLError.SQL_STATE_MEMORY_ALLOCATION_FAILURE);
  1454.         }
  1455.         filePacket.clear();
  1456.         send(filePacket, 0);
  1457.         byte[] fileBuf = new byte[packetLength];
  1458.         BufferedInputStream fileIn = null;
  1459.         try {
  1460.             fileIn = new BufferedInputStream(new FileInputStream(fileName));
  1461.             int bytesRead = 0;
  1462.             while ((bytesRead = fileIn.read(fileBuf)) != -1) {
  1463.                 filePacket.clear();
  1464.                 filePacket.writeBytesNoNull(fileBuf, 0, bytesRead);
  1465.                 send(filePacket);
  1466.             }
  1467.         } catch (IOException ioEx) {
  1468.             StringBuffer messageBuf = new StringBuffer("Unable to open file ");
  1469.             if (!this.connection.useParanoidErrorMessages()) {
  1470.                 messageBuf.append("'");
  1471.                 if (fileName != null) {
  1472.                     messageBuf.append(fileName);
  1473.                 }
  1474.                 messageBuf.append("'");
  1475.             }
  1476.             messageBuf.append("for 'LOAD DATA LOCAL INFILE' command.");
  1477.             if (!this.connection.useParanoidErrorMessages()) {
  1478.                 messageBuf.append("Due to underlying IOException: ");
  1479.                 messageBuf.append(Util.stackTraceToString(ioEx));
  1480.             }
  1481.             throw new SQLException(messageBuf.toString(),
  1482.                 SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
  1483.         } finally {
  1484.             if (fileIn != null) {
  1485.                 try {
  1486.                     fileIn.close();
  1487.                 } catch (Exception ex) {
  1488.                     throw new SQLException("Unable to close local file during LOAD DATA LOCAL INFILE command",
  1489.                         SQLError.SQL_STATE_GENERAL_ERROR);
  1490.                 }
  1491.                 fileIn = null;
  1492.             } else {
  1493.                 // file open failed, but server needs one packet
  1494.                 filePacket.clear();
  1495.                 send(filePacket);
  1496.             }
  1497.         }
  1498.         // send empty packet to mark EOF
  1499.         filePacket.clear();
  1500.         send(filePacket);
  1501.         Buffer resultPacket = checkErrorPacket();
  1502.         return buildResultSetWithUpdates(resultPacket);
  1503.     }
  1504.     /**
  1505.      * Checks for errors in the reply packet, and if none, returns the reply
  1506.      * packet, ready for reading
  1507.      *
  1508.      * @return DOCUMENT ME!
  1509.      *
  1510.      * @throws SQLException DOCUMENT ME!
  1511.      */
  1512.     private Buffer checkErrorPacket() throws SQLException {
  1513.         return checkErrorPacket(-1);
  1514.     }
  1515.     /**
  1516.      * Checks for errors in the reply packet, and if none, returns the reply
  1517.      * packet, ready for reading
  1518.      *
  1519.      * @param command the command being issued (if used)
  1520.      *
  1521.      * @return DOCUMENT ME!
  1522.      *
  1523.      * @throws SQLException if an error packet was received
  1524.      * @throws java.sql.SQLException DOCUMENT ME!
  1525.      */
  1526.     private Buffer checkErrorPacket(int command) throws SQLException {
  1527.         int statusCode = 0;
  1528.         Buffer resultPacket = null;
  1529.         try {
  1530.             // Check return value, if we get a java.io.EOFException,
  1531.             // the server has gone away. We'll pass it on up the
  1532.             // exception chain and let someone higher up decide
  1533.             // what to do (barf, reconnect, etc).
  1534.             resultPacket = reuseAndReadPacket(this.reusablePacket);
  1535.             statusCode = resultPacket.readByte();
  1536.         } catch (SQLException sqlEx) {
  1537.             // don't wrap SQLExceptions
  1538.             throw sqlEx;
  1539.         } catch (Exception fallThru) {
  1540.             String underlyingMessage = fallThru.getMessage();
  1541.             throw new java.sql.SQLException(SQLError.get(
  1542.                     SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE) + ": "
  1543.                 + fallThru.getClass().getName() + ", "
  1544.                 + ((underlyingMessage != null) ? underlyingMessage
  1545.                                                : "no message given by JVM"),
  1546.                 SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE, 0);
  1547.         }
  1548.         // Error handling
  1549.         if (statusCode == (byte) 0xff) {
  1550.             String serverErrorMessage;
  1551.             int errno = 2000;
  1552.             if (this.protocolVersion > 9) {
  1553.                 errno = resultPacket.readInt();
  1554.                 String xOpen = null;
  1555.                 serverErrorMessage = resultPacket.readString();
  1556.                 if (serverErrorMessage.startsWith("#")) {
  1557.                     // we have an SQLState
  1558.                     if (serverErrorMessage.length() > 6) {
  1559.                         xOpen = serverErrorMessage.substring(1, 6);
  1560.                         serverErrorMessage = serverErrorMessage.substring(6);
  1561.                         if (xOpen.equals("HY000")) {
  1562.                             xOpen = SQLError.mysqlToXOpen(errno);
  1563.                         }
  1564.                     } else {
  1565.                         xOpen = SQLError.mysqlToXOpen(errno);
  1566.                     }
  1567.                 } else {
  1568.                     xOpen = SQLError.mysqlToXOpen(errno);
  1569.                 }
  1570.                 clearInputStream();
  1571.                 StringBuffer errorBuf = new StringBuffer(
  1572.                         " message from server: "");
  1573.                 errorBuf.append(serverErrorMessage);
  1574.                 errorBuf.append(""");
  1575.                 throw new SQLException(SQLError.get(xOpen) + ", "
  1576.                     + errorBuf.toString(), xOpen, errno);
  1577.             } else {
  1578.                 serverErrorMessage = resultPacket.readString();
  1579.                 clearInputStream();
  1580.                 if (serverErrorMessage.indexOf("Unknown column") != -1) {
  1581.                     throw new java.sql.SQLException(SQLError.get(
  1582.                             SQLError.SQL_STATE_COLUMN_NOT_FOUND) + ", "
  1583.                         + serverErrorMessage,
  1584.                         SQLError.SQL_STATE_COLUMN_NOT_FOUND, -1);
  1585.                 } else {
  1586.                     StringBuffer errorBuf = new StringBuffer(
  1587.                             " message from server: "");
  1588.                     errorBuf.append(serverErrorMessage);
  1589.                     errorBuf.append(""");
  1590.                     throw new java.sql.SQLException(SQLError.get(
  1591.                             SQLError.SQL_STATE_GENERAL_ERROR) + ", "
  1592.                         + errorBuf.toString(),
  1593.                         SQLError.SQL_STATE_GENERAL_ERROR, -1);
  1594.                 }
  1595.             }
  1596.         }
  1597.         return resultPacket;
  1598.     }
  1599.     /**
  1600.      * Sends a large packet to the server as a series of smaller packets
  1601.      *
  1602.      * @param packet DOCUMENT ME!
  1603.      *
  1604.      * @throws SQLException DOCUMENT ME!
  1605.      * @throws SQLException DOCUMENT ME!
  1606.      */
  1607.     private final void sendSplitPackets(Buffer packet)
  1608.         throws SQLException {
  1609.         try {
  1610.             //
  1611.             // Big packets are handled by splitting them in packets of MAX_THREE_BYTES
  1612.             // length. The last packet is always a packet that is < MAX_THREE_BYTES.
  1613.             // (The last packet may even have a length of 0)
  1614.             //
  1615.             //
  1616.             // NB: Guarded by execSQL. If the driver changes architecture, this
  1617.             // will need to be synchronized in some other way
  1618.             //
  1619.             Buffer headerPacket = (splitBufRef == null) ? null
  1620.                                                         : (Buffer) (splitBufRef
  1621.                 .get());
  1622.             //
  1623.             // Store this packet in a soft reference...It can be re-used if not GC'd (so clients
  1624.             // that use it frequently won't have to re-alloc the 16M buffer), but we don't
  1625.             // penalize infrequent users of large packets by keeping 16M allocated all of the time
  1626.             //
  1627.             if (headerPacket == null) {
  1628.                 headerPacket = new Buffer((int) (maxThreeBytes + HEADER_LENGTH));
  1629.                 splitBufRef = new SoftReference(headerPacket);
  1630.             }
  1631.             int len = packet.getPosition();
  1632.             int splitSize = (int) maxThreeBytes;
  1633.             int originalPacketPos = HEADER_LENGTH;
  1634.             byte[] origPacketBytes = packet.getByteBuffer();
  1635.             byte[] headerPacketBytes = headerPacket.getByteBuffer();
  1636.             if (Driver.DEBUG) {
  1637.                 System.out.println("nnSending split packets for packet of "
  1638.                     + len + " bytes:n");
  1639.             }
  1640.             while (len >= maxThreeBytes) {
  1641.                 headerPacket.setPosition(0);
  1642.                 headerPacket.writeLongInt(splitSize);
  1643.                 this.packetSequence++;
  1644.                 headerPacket.writeByte(this.packetSequence);
  1645.                 System.arraycopy(origPacketBytes, originalPacketPos,
  1646.                     headerPacketBytes, 4, splitSize);
  1647.                 this.mysqlOutput.write(headerPacketBytes, 0,
  1648.                     splitSize + HEADER_LENGTH);
  1649.                 this.mysqlOutput.flush();
  1650.                 if (Driver.DEBUG) {
  1651.                     System.out.print("  total packet length (header & data) "
  1652.                         + (splitSize + HEADER_LENGTH) + "nheader: ");
  1653.                     headerPacket.dumpHeader();
  1654.                     System.out.println();
  1655.                     System.out.print("last eight bytes: ");
  1656.                     headerPacket.dumpNBytes(((splitSize + HEADER_LENGTH) - 8), 8);
  1657.                     System.out.println();
  1658.                 }
  1659.                 originalPacketPos += splitSize;
  1660.                 len -= splitSize;
  1661.             }
  1662.             //
  1663.             // Write last packet
  1664.             //
  1665.             headerPacket.clear();
  1666.             headerPacket.setPosition(0);
  1667.             headerPacket.writeLongInt(len - HEADER_LENGTH);
  1668.             this.packetSequence++;
  1669.             headerPacket.writeByte(this.packetSequence);
  1670.             if (len != 0) {
  1671.                 System.arraycopy(origPacketBytes, originalPacketPos,
  1672.                     headerPacketBytes, 4, len - HEADER_LENGTH);
  1673.             }
  1674.             this.mysqlOutput.write(headerPacket.getByteBuffer(), 0, len);
  1675.             this.mysqlOutput.flush();
  1676.             if (Driver.DEBUG) {
  1677.                 System.out.print("  total packet length (header & data) " + len
  1678.                     + ",nheader: ");
  1679.                 headerPacket.dumpHeader();
  1680.                 System.out.println();
  1681.                 System.out.print("last packet bytes: ");
  1682.                 headerPacket.dumpNBytes(0, len);
  1683.                 System.out.println();
  1684.             }
  1685.         } catch (IOException ioEx) {
  1686.             StringBuffer message = new StringBuffer(SQLError.get(
  1687.                         SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE));
  1688.             message.append(": ");
  1689.             message.append(ioEx.getClass().getName());
  1690.             message.append(", underlying cause: ");
  1691.             message.append(ioEx.getMessage());
  1692.             if (!this.connection.useParanoidErrorMessages()) {
  1693.                 message.append(Util.stackTraceToString(ioEx));
  1694.             }
  1695.             throw new java.sql.SQLException(message.toString(),
  1696.                 SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE, 0);
  1697.         }
  1698.     }
  1699.     private int alignPacketSize(int a, int l) {
  1700.         return ((((a) + (l)) - 1) & ~((l) - 1));
  1701.     }
  1702.     private void checkForOutstandingStreamingData() throws SQLException {
  1703.         if (this.streamingData != null) {
  1704.             if (!this.connection.getClobberStreamingResults()) {
  1705.                 throw new SQLException("Streaming result set "
  1706.                     + this.streamingData + " is still active."
  1707.                     + " Only one streaming result set may be open and in use per-connection. Ensure that you have called .close() on "
  1708.                     + " any active result sets before attempting more queries.");
  1709.             } else {
  1710.                 // Close the result set
  1711.                 this.streamingData.getOwner().realClose(false);
  1712.                 // clear any pending data....
  1713.                 clearInputStream();
  1714.             }
  1715.         }
  1716.     }
  1717.     private void clearInputStream() throws SQLException {
  1718.         try {
  1719.             int len = this.mysqlInput.available();
  1720.             while (len > 0) {
  1721.                 this.mysqlInput.skip(len);
  1722.                 len = this.mysqlInput.available();
  1723.             }
  1724.         } catch (IOException ioEx) {
  1725.             throw new SQLException("I/O error while clearing input stream of old results",
  1726.                 SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE);
  1727.         }
  1728.     }
  1729.     private Buffer compressPacket(Buffer packet, int offset, int packetLen,
  1730.         int headerLength) throws SQLException {
  1731.         packet.writeLongInt(packetLen - headerLength);
  1732.         packet.writeByte((byte) 0); // wrapped packet has 0 packet seq.
  1733.         int lengthToWrite = 0;
  1734.         int compressedLength = 0;
  1735.         byte[] bytesToCompress = packet.getByteBuffer();
  1736.         byte[] compressedBytes = null;
  1737.         int offsetWrite = 0;
  1738.         if (true /*packetLen < MIN_COMPRESS_LEN*/) {
  1739.             lengthToWrite = packetLen;
  1740.             compressedBytes = packet.getByteBuffer();
  1741.             compressedLength = 0;
  1742.             offsetWrite = offset;
  1743.         } else {
  1744.             compressedBytes = new byte[bytesToCompress.length * 2];
  1745.             this.deflater.reset();
  1746.             this.deflater.setInput(bytesToCompress, offset, packetLen);
  1747.             this.deflater.finish();
  1748.             int compLen = this.deflater.deflate(compressedBytes);
  1749.             if (compLen > packetLen) {
  1750.                 lengthToWrite = packetLen;
  1751.                 compressedBytes = packet.getByteBuffer();
  1752.                 compressedLength = 0;
  1753.                 offsetWrite = offset;
  1754.             } else {
  1755.                 lengthToWrite = compLen;
  1756.                 headerLength += COMP_HEADER_LENGTH;
  1757.                 compressedLength = packetLen;
  1758.             }
  1759.         }
  1760.         Buffer compressedPacket = new Buffer(packetLen + headerLength);
  1761.         compressedPacket.setPosition(0);
  1762.         compressedPacket.writeLongInt(lengthToWrite);
  1763.         compressedPacket.writeByte(this.packetSequence);
  1764.         compressedPacket.writeLongInt(compressedLength);
  1765.         compressedPacket.writeBytesNoNull(compressedBytes, offsetWrite,
  1766.             lengthToWrite);
  1767.         return compressedPacket;
  1768.     }
  1769.     private SocketFactory createSocketFactory() throws SQLException {
  1770.         try {
  1771.             if (socketFactoryClassName == null) {
  1772.                 throw new SQLException("No name specified for socket factory",
  1773.                     SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE);
  1774.             }
  1775.             return (SocketFactory) (Class.forName(socketFactoryClassName)
  1776.                                          .newInstance());
  1777.         } catch (Exception ex) {
  1778.             throw new SQLException("Could not create socket factory '"
  1779.                 + socketFactoryClassName + "' due to underlying exception: "
  1780.                 + ex.toString(),
  1781.                 SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE);
  1782.         }
  1783.     }
  1784.     /**
  1785.      * Ensures that we don't hold on to overly-large send packets
  1786.      */
  1787.     private void reclaimLargeSharedSendPacket() {
  1788.         if ((this.sharedSendPacket != null)
  1789.                 && (this.sharedSendPacket.getBufLength() > 1048576)) {
  1790.             this.sharedSendPacket = new Buffer(this.connection
  1791.                     .getNetBufferLength());
  1792.         }
  1793.     }
  1794.     /**
  1795.      * Secure authentication for 4.1 and newer servers.
  1796.      *
  1797.      * @param packLength
  1798.      * @param serverCapabilities
  1799.      * @param clientParam
  1800.      * @param user
  1801.      * @param password
  1802.      * @param database DOCUMENT ME!
  1803.      *
  1804.      * @throws SQLException
  1805.      */
  1806.     private void secureAuth(int packLength, int serverCapabilities,
  1807.         long clientParam, String user, String password, String database)
  1808.         throws SQLException {
  1809.         // Passwords can be 16 chars long
  1810.         Buffer packet = new Buffer(packLength);
  1811.         if (this.use41Extensions) {
  1812.             if (versionMeetsMinimum(4, 1, 1)) {
  1813.                 packet.writeLong(clientParam);
  1814.                 packet.writeLong(this.maxThreeBytes);
  1815.                 // charset, JDBC will connect as 'latin1',
  1816.                 // and use 'SET NAMES' to change to the desired
  1817.                 // charset after the connection is established.
  1818.                 packet.writeByte((byte) 8);
  1819.                 // Set of bytes reserved for future use.
  1820.                 packet.writeBytesNoNull(new byte[23]);
  1821.             } else {
  1822.                 packet.writeLong(clientParam);
  1823.                 packet.writeLong(this.maxThreeBytes);
  1824.             }
  1825.         } else {
  1826.             packet.writeInt((int) clientParam);
  1827.             packet.writeLongInt(this.maxThreeBytes);
  1828.         }
  1829.         // User/Password data
  1830.         packet.writeString(user);
  1831.         if (password.length() != 0) {
  1832.             /* Prepare false scramble  */
  1833.             packet.writeString(FALSE_SCRAMBLE);
  1834.         } else {
  1835.             /* For empty password*/
  1836.             packet.writeString("");
  1837.         }
  1838.         if (((serverCapabilities & CLIENT_CONNECT_WITH_DB) != 0)
  1839.                 && (database != null) && (database.length() > 0)) {
  1840.             packet.writeString(database);
  1841.         }
  1842.         send(packet);
  1843.         //
  1844.         // Don't continue stages if password is empty
  1845.         //
  1846.         if (password.length() > 0) {
  1847.             Buffer b = readPacket();
  1848.             b.setPosition(0);
  1849.             byte[] replyAsBytes = b.getByteBuffer();
  1850.             if ((replyAsBytes.length == 25) && (replyAsBytes[0] != 0)) {
  1851.                 // Old passwords will have '*' at the first byte of hash */
  1852.                 if (replyAsBytes[0] != '*') {
  1853.                     try {
  1854.                         /* Build full password hash as it is required to decode scramble */
  1855.                         byte[] buff = Security.passwordHashStage1(password);
  1856.                         /* Store copy as we'll need it later */
  1857.                         byte[] passwordHash = new byte[buff.length];
  1858.                         System.arraycopy(buff, 0, passwordHash, 0, buff.length);
  1859.                         /* Finally hash complete password using hash we got from server */
  1860.                         passwordHash = Security.passwordHashStage2(passwordHash,
  1861.                                 replyAsBytes);
  1862.                         byte[] packetDataAfterSalt = new byte[replyAsBytes.length
  1863.                             - 5];
  1864.                         System.arraycopy(replyAsBytes, 4, packetDataAfterSalt,
  1865.                             0, replyAsBytes.length - 5);
  1866.                         byte[] mysqlScrambleBuff = new byte[20];
  1867.                         /* Decypt and store scramble 4 = hash for stage2 */
  1868.                         Security.passwordCrypt(packetDataAfterSalt,
  1869.                             mysqlScrambleBuff, passwordHash, 20);
  1870.                         /* Encode scramble with password. Recycle buffer */
  1871.                         Security.passwordCrypt(mysqlScrambleBuff, buff, buff, 20);
  1872.                         Buffer packet2 = new Buffer(25);
  1873.                         packet2.writeBytesNoNull(buff);
  1874.                         this.packetSequence++;
  1875.                         send(packet2, 24);
  1876.                     } catch (NoSuchAlgorithmException nse) {
  1877.                         throw new SQLException(
  1878.                             "Failed to create message digest 'SHA-1' for authentication. "
  1879.                             + " You must use a JDK that supports JCE to be able to use secure connection authentication",
  1880.                             SQLError.SQL_STATE_GENERAL_ERROR);
  1881.                     }
  1882.                 } else {
  1883.                     try {
  1884.                         /* Create password to decode scramble */
  1885.                         byte[] passwordHash = Security.createKeyFromOldPassword(password);
  1886.                         /* Decypt and store scramble 4 = hash for stage2 */
  1887.                         byte[] netReadPos4 = new byte[replyAsBytes.length - 5];
  1888.                         System.arraycopy(replyAsBytes, 4, netReadPos4, 0,
  1889.                             replyAsBytes.length - 5);
  1890.                         byte[] mysqlScrambleBuff = new byte[20];
  1891.                         /* Decypt and store scramble 4 = hash for stage2 */
  1892.                         Security.passwordCrypt(netReadPos4, mysqlScrambleBuff,
  1893.                             passwordHash, 20);
  1894.                         /* Finally scramble decoded scramble with password */
  1895.                         String scrambledPassword = Util.scramble(new String(
  1896.                                     mysqlScrambleBuff), password);
  1897.                         Buffer packet2 = new Buffer(packLength);
  1898.                         packet2.writeString(scrambledPassword);
  1899.                         this.packetSequence++;
  1900.                         send(packet2, 24);
  1901.                     } catch (NoSuchAlgorithmException nse) {
  1902.                         throw new SQLException(
  1903.                             "Failed to create message digest 'SHA-1' for authentication. "
  1904.                             + " You must use a JDK that supports JCE to be able to use secure connection authentication",
  1905.                             SQLError.SQL_STATE_GENERAL_ERROR);
  1906.                     }
  1907.                 }
  1908.             }
  1909.         }
  1910.     }
  1911.     /**
  1912.      * Secure authentication for 4.1.1 and newer servers.
  1913.      *
  1914.      * @param packLength
  1915.      * @param serverCapabilities DOCUMENT ME!
  1916.      * @param clientParam
  1917.      * @param user
  1918.      * @param password
  1919.      * @param database DOCUMENT ME!
  1920.      *
  1921.      * @throws SQLException
  1922.      */
  1923.     private void secureAuth411(int packLength, int serverCapabilities,
  1924.         long clientParam, String user, String password, String database)
  1925.         throws SQLException {
  1926.         // SERVER:  public_seed=create_random_string()
  1927.         //  send(public_seed)
  1928.         //
  1929.         // CLIENT:  recv(public_seed)
  1930.         //  hash_stage1=sha1("password")
  1931.         //  hash_stage2=sha1(hash_stage1)
  1932.         //  reply=xor(hash_stage1, sha1(public_seed,hash_stage2)
  1933.         //
  1934.         //  // this three steps are done in scramble()
  1935.         //
  1936.         //  send(reply)
  1937.         //
  1938.         //
  1939.         // SERVER:  recv(reply)
  1940.         //  hash_stage1=xor(reply, sha1(public_seed,hash_stage2))
  1941.         //  candidate_hash2=sha1(hash_stage1)
  1942.         //  check(candidate_hash2==hash_stage2)
  1943.         // Passwords can be 16 chars long
  1944.         Buffer packet = new Buffer(packLength);
  1945.         if (this.use41Extensions) {
  1946.             if (versionMeetsMinimum(4, 1, 1)) {
  1947.                 packet.writeLong(this.clientParam);
  1948.                 packet.writeLong(this.maxThreeBytes);
  1949.                 // charset, JDBC will connect as 'latin1',
  1950.                 // and use 'SET NAMES' to change to the desired
  1951.                 // charset after the connection is established.
  1952.                 packet.writeByte((byte) 8);
  1953.                 // Set of bytes reserved for future use.
  1954.                 packet.writeBytesNoNull(new byte[23]);
  1955.             } else {
  1956.                 packet.writeLong(this.clientParam);
  1957.                 packet.writeLong(this.maxThreeBytes);
  1958.             }
  1959.         } else {
  1960.             packet.writeInt((int) this.clientParam);
  1961.             packet.writeLongInt(this.maxThreeBytes);
  1962.         }
  1963.         // User/Password data
  1964.         packet.writeString(user);
  1965.         if (password.length() != 0) {
  1966.             packet.writeByte((byte) 0x14);
  1967.             try {
  1968.                 packet.writeBytesNoNull(Security.scramble411(password, this.seed));
  1969.             } catch (NoSuchAlgorithmException nse) {
  1970.                 throw new SQLException(
  1971.                     "Failed to create message digest 'SHA-1' for authentication. "
  1972.                     + " You must use a JDK that supports JCE to be able to use secure connection authentication",
  1973.                     SQLError.SQL_STATE_GENERAL_ERROR);
  1974.             }
  1975.         } else {
  1976.             /* For empty password*/
  1977.             packet.writeByte((byte) 0);
  1978.         }
  1979.         if (((serverCapabilities & CLIENT_CONNECT_WITH_DB) != 0)
  1980.                 && (database != null) && (database.length() > 0)) {
  1981.             packet.writeString(database);
  1982.         }
  1983.         send(packet);
  1984.         byte savePacketSequence = this.packetSequence++;
  1985.         Buffer reply = checkErrorPacket();
  1986.         if (reply.isLastDataPacket()) {
  1987.             /*
  1988.               By sending this very specific reply server asks us to send scrambled
  1989.               password in old format. The reply contains scramble_323.
  1990.             */
  1991.             this.packetSequence = ++savePacketSequence;
  1992.             packet.clear();
  1993.             String seed323 = this.seed.substring(0, 8);
  1994.             packet.writeString(Util.newCrypt(password, seed323));
  1995.             send(packet);
  1996.             /* Read what server thinks about out new auth message report */
  1997.             checkErrorPacket();
  1998.         }
  1999.     }
  2000. }