|
What this is
Other links
The source code/* * MM JDBC Drivers for MySQL * * $Id: MysqlIO.java,v 1.3 1998/08/25 00:53:47 mmatthew Exp $ * * Copyright (C) 1998 Mark Matthews <mmatthew@worldserver.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * See the COPYING file located in the top-level-directory of * the archive of this library for complete text of license. */ /** * This class is used by Connection for communicating with the * MySQL server. * * @see java.sql.Connection * @author Mark Matthews <mmatthew@worldserver.com> * @version $Id$ */ package org.gjt.mm.mysql; import java.io.*; import java.net.*; import java.util.*; import java.sql.*; import java.sql.SQLException; import java.util.zip.Deflater; import java.util.zip.Inflater; public class MysqlIO { private Socket _Mysql_Conn = null; private BufferedInputStream _Mysql_Buf_Input = null; private BufferedOutputStream _Mysql_Buf_Output = null; private DataInputStream _Mysql_Input = null; private DataOutputStream _Mysql_Output = null; static int MAXBUF = 65535; static final int HEADER_LENGTH = 4; static final int COMP_HEADER_LENGTH = 3; private byte _packetSequence = 0; private byte _protocol_V = 0; private String _Server_V = null; private int _server_major_version = 0; private int _server_minor_version = 0; private int _server_sub_minor_version = 0; private int _port = 3306; private String _Host = null; private Deflater _Deflater = null; private Inflater _Inflater = null; // // Use this when reading in rows to avoid thousands of new() // calls, because the byte arrays just get copied out of the // packet anyway // private Buffer _ReusablePacket = new Buffer(MAXBUF); // // For SQL Warnings // java.sql.SQLWarning _Warning = null; private static int CLIENT_LONG_PASSWORD = 1; /* new more secure passwords */ private static int CLIENT_FOUND_ROWS = 2; /* Found instead of affected rows */ private static int CLIENT_LONG_FLAG = 4; /* Get all column flags */ private static int CLIENT_CONNECT_WITH_DB = 8; /* One can specify db on connect */ private static int CLIENT_NO_SCHEMA = 16; /* Don't allow database.table.column */ private static int CLIENT_COMPRESS = 32; /* Can use compression protcol */ private static int CLIENT_ODBC = 64; /* Odbc client */ private static int CLIENT_LOCAL_FILES = 128; /* Can use LOAD DATA LOCAL */ private static int CLIENT_IGNORE_SPACE = 256; /* Ignore spaces before '(' */ private boolean use_compression = false; /** * Constructor: Connect to the MySQL server and setup * a stream connection. * * @param host the hostname to connect to * @param port the port number that the server is listening on * @exception IOException if an IOException occurs during connect. */ MysqlIO(String Host, int port) throws IOException, java.sql.SQLException { _port = port; _Host = Host; _Mysql_Conn = new Socket(_Host, _port); _Mysql_Buf_Input = new BufferedInputStream(_Mysql_Conn.getInputStream()); _Mysql_Buf_Output = new BufferedOutputStream(_Mysql_Conn.getOutputStream()); _Mysql_Input = new DataInputStream(_Mysql_Buf_Input); _Mysql_Output = new DataOutputStream(_Mysql_Buf_Output); } /** * Initialize communications with the MySQL server. * * Handles logging on, and handling initial connection errors. */ void init(String User, String Password) throws java.sql.SQLException { String Seed; try { // Read the first packet Buffer Buf = readPacket(); // Get the protocol version _protocol_V = Buf.readByte(); if (_protocol_V == -1) { try { _Mysql_Conn.close(); } catch (Exception E) {} throw new SQLException("Server configuration denies access to data source", "08001", 0); } _Server_V = Buf.readString(); // Parse the server version into major/minor/subminor int point = _Server_V.indexOf("."); if (point != -1) { try { int n = Integer.parseInt(_Server_V.substring(0, point)); _server_major_version = n; } catch (NumberFormatException NFE1) { } String Remaining = _Server_V.substring(point + 1, _Server_V.length()); point = Remaining.indexOf("."); if (point != -1) { try { int n = Integer.parseInt(Remaining.substring(0, point)); _server_minor_version = n; } catch (NumberFormatException NFE2) { } Remaining = Remaining.substring(point + 1, Remaining.length()); int pos = 0; while (pos < Remaining.length()) { if (Remaining.charAt(pos) < '0' || Remaining.charAt(pos) > '9') { break; } pos++; } try { int n = Integer.parseInt(Remaining.substring(0, pos)); _server_sub_minor_version = n; } catch (NumberFormatException NFE3) { } } } long threadId = Buf.readLong(); Seed = Buf.readString(); if (Driver.trace) { Debug.msg(this, "Protocol Version: " + (int)_protocol_V); Debug.msg(this, "Server Version: " + _Server_V); Debug.msg(this, "Thread ID: " + threadId); Debug.msg(this, "Crypt Seed: " + Seed); } // Client capabilities int clientParam = 0; if (Buf.pos < Buf.buf_length) { int serverCapabilities = Buf.readInt(); // Should be settable by user if ((serverCapabilities & CLIENT_COMPRESS) != 0) { // The following match with ZLIB's // decompress() and compress() //_Deflater = new Deflater(); //_Inflater = new Inflater(); //clientParam |= CLIENT_COMPRESS; } } // Authenticate if (_protocol_V > 9) { //clientParam |= CLIENT_LONG_PASSWORD; // for long passwords clientParam = 1; } else { clientParam &= ~CLIENT_LONG_PASSWORD; } int password_length = 16; int user_length = 0; if (User != null) { user_length = User.length(); } int pack_length = (user_length + password_length) + 6 + HEADER_LENGTH; // Passwords can be 16 chars long Buffer Packet = new Buffer(pack_length); Packet.writeInt(clientParam); Packet.writeLongInt(pack_length); // User/Password data Packet.writeString(User); if (_protocol_V > 9) { Packet.writeString(Util.newCrypt(Password, Seed)); } else { Packet.writeString(Util.oldCrypt(Password, Seed)); } send(Packet); // Check for errors Buffer B = readPacket(); byte status = B.readByte(); if (status == (byte) 0xff) { String Message = ""; int errno = 2000; if (_protocol_V > 9) { errno = B.readInt(); Message = B.readString(); clearReceive(); String XOpen = SQLError.mysqlToXOpen(errno); if (XOpen.equals("S1000")) { throw new java.sql.SQLException("Communication failure during handshake. Is there a server running on " + _Host + ":" + _port + "?"); } else { throw new java.sql.SQLException(SQLError.get(XOpen) + ": " + Message, XOpen, errno); } } else { Message = B.readString(); clearReceive(); if (Message.indexOf("Access denied") != -1) { throw new java.sql.SQLException(SQLError.get("28000") + ": " + Message, "28000", errno); } else { throw new java.sql.SQLException(SQLError.get("08001") + ": " + Message, "08001", errno); } } } else if (status == 0x00) { if (_server_major_version >= 3 && _server_minor_version >= 22 && _server_sub_minor_version >= 5) { Packet.newReadLength(); Packet.newReadLength(); } else { Packet.readLength(); Packet.readLength(); } } else { throw new java.sql.SQLException("Unknown Status code from server", "08007", status); } //if ((clientParam & CLIENT_COMPRESS) != 0) { //use_compression = true; //} } catch (IOException E) { E.printStackTrace(); throw new java.sql.SQLException(SQLError.get("08S01") + ": " + E.getClass().getName(), "08S01", 0); } } /** * Get the version string of the server we are talking to */ String getServerVersion() { return _Server_V; } /** * Send a command to the MySQL server * * If data is to be sent with command, it should be put in ExtraData * * Raw packets can be sent by setting QueryPacket to something other * than null. */ final Buffer sendCommand(int command, String ExtraData, Buffer QueryPacket) throws Exception { Buffer Ret = null; byte statusCode; try { Buffer Packet = null; if (QueryPacket == null) { int pack_length = HEADER_LENGTH + COMP_HEADER_LENGTH + 1 + ExtraData.length() + 2; Packet = new Buffer(pack_length); _packetSequence = -1; Packet.clear(); // Offset different for compression if (use_compression) { Packet.pos += COMP_HEADER_LENGTH; } Packet.writeByte((byte)command); if (command == MysqlDefs.INIT_DB || command == MysqlDefs.CREATE_DB || command == MysqlDefs.DROP_DB || command == MysqlDefs.QUERY) { Packet.writeStringNoNull(ExtraData); } else if (command == MysqlDefs.PROCESS_KILL) { long id = new Long(ExtraData).longValue(); Packet.writeLong(id); } else if (command == MysqlDefs.RELOAD && _protocol_V > 9) { Debug.msg(this, "Reload"); //Packet.writeByte(reloadParam); } } else { _packetSequence = -1; Packet = QueryPacket; } send(Packet); } catch (Exception Ex) { Ex.printStackTrace(); throw new java.sql.SQLException(SQLError.get("08S01") + ": " + Ex.getClass().getName(), "08S01", 0); } try { // Check return value, if we get a java.io.EOFException, // the server has gone away. We'll pass it on up the // exception chain and let someone higher up decide // what to do (barf, reconnect, etc). Ret = readPacket(); statusCode = Ret.readByte(); } catch (java.io.EOFException EOFE) { throw EOFE; } catch (Exception FallThru) { throw new java.sql.SQLException(SQLError.get("08S01") + ": " + FallThru.getClass().getName(), "08S01", 0); } try { // Error handling if (statusCode == (byte)0xff) { String ErrorMessage; int errno = 2000; if (_protocol_V > 9) { errno = Ret.readInt(); ErrorMessage = Ret.readString(); clearReceive(); String XOpen = SQLError.mysqlToXOpen(errno); throw new java.sql.SQLException(SQLError.get(XOpen) + ": " + ErrorMessage, XOpen, errno); } else { ErrorMessage = Ret.readString(); clearReceive(); if (ErrorMessage.indexOf("Unknown column") != -1) { throw new java.sql.SQLException(SQLError.get("S0022") + ": " + ErrorMessage, "S0022", -1); } else { throw new java.sql.SQLException(SQLError.get("S1000") + ": " + ErrorMessage, "S1000", -1); } } } else if (statusCode == 0x00) { if (command == MysqlDefs.CREATE_DB || command == MysqlDefs.DROP_DB) { java.sql.SQLWarning NW = new java.sql.SQLWarning("Command=" + command + ": "); if (_Warning != null) NW.setNextException(_Warning); _Warning = NW; } } else if (Ret.isLastDataPacket()) { java.sql.SQLWarning NW = new java.sql.SQLWarning("Command=" + command + ": "); if (_Warning != null) NW.setNextException(_Warning); _Warning = NW; } return Ret; } catch (IOException E) { throw new java.sql.SQLException(SQLError.get("08S01") + ": " + E.getClass().getName(), "08S01", 0); } } /** * Send a query stored in a packet directly to the server. */ final ResultSet sqlQueryDirect(Buffer QueryPacket, int max_rows) throws Exception { long updateCount = -1; long updateID = -1; // Send query command and sql query string clearAllReceive(); Buffer Packet = sendCommand(MysqlDefs.QUERY, null, QueryPacket); Packet.pos--; long columnCount = Packet.readLength(); if (Driver.trace) { Debug.msg(this, "Column count: " + columnCount); } if (columnCount == 0) { try { if (_server_major_version >= 3 && _server_minor_version >= 22 && _server_sub_minor_version >= 5) { updateCount = Packet.newReadLength(); updateID = Packet.newReadLength(); } else { updateCount = (long)Packet.readLength(); updateID = (long)Packet.readLength(); } } catch (Exception E) { throw new java.sql.SQLException(SQLError.get("S1000") + ": " + E.getClass().getName(), "S1000", -1); } if (Driver.trace) { Debug.msg(this, "Update Count = " + updateCount); } return new ResultSet(updateCount, updateID); } else { Field[] Fields = new Field[(int)columnCount]; // Read in the column information for (int i = 0; i < columnCount; i++) { Packet = readPacket(); String TableName = Packet.readLenString(); String ColName = Packet.readLenString(); int colLength = Packet.readnBytes(); int colType = Packet.readnBytes(); Packet.readByte(); // We know it's currently 2 short colFlag = (short)Buffer.ub(Packet.readByte()); int colDecimals = Buffer.ub(Packet.readByte()); Fields[i] = new Field(TableName, ColName, colLength, colType, colFlag, colDecimals); } Packet = readPacket(); Vector Rows = new Vector(); // Now read the data byte[][] Row = nextRow((int)columnCount); Rows.addElement(Row); int row_count = 1; while (Row != null && row_count < max_rows) { Row = nextRow((int)columnCount); if (Row != null) { Rows.addElement(Row); row_count++; } else { if (Driver.trace) { Debug.msg(this, "* NULL Row *"); } } } if (Driver.trace) { Debug.msg(this, "* Fetched " + Rows.size() + " rows from server *"); } return new ResultSet(Fields, Rows); } } /** * Send a query specified in the String "Query" to the MySQL server. * * This method uses the specified character encoding to get the * bytes from the query string. */ final ResultSet sqlQuery(String Query, int max_rows, String Encoding) throws Exception { // We don't know exactly how many bytes we're going to get // from the query. Since we're dealing with Unicode, the // max is 2, so pad it (2 * query) + space for headers int pack_length = HEADER_LENGTH + 1 + (Query.length() * 2) + 2; Buffer Packet = new Buffer(pack_length); Packet.writeByte((byte)MysqlDefs.QUERY); if (Encoding != null) { Packet.writeStringNoNull(Query, Encoding); } else { Packet.writeStringNoNull(Query); } return sqlQueryDirect(Packet, max_rows); } final ResultSet sqlQuery(String Query, int max_rows) throws Exception { long updateCount = -1; long updateID = -1; // Send query command and sql query string clearAllReceive(); Buffer Packet = sendCommand(MysqlDefs.QUERY, Query, null); //, (byte)0); Packet.pos--; long columnCount = Packet.readLength(); if (Driver.trace) { Debug.msg(this, "Column count: " + columnCount); } if (columnCount == 0) { try { if (_server_major_version >= 3 && _server_minor_version >= 22 && _server_sub_minor_version >= 5) { updateCount = Packet.newReadLength(); updateID = Packet.newReadLength(); } else { updateCount = (long)Packet.readLength(); updateID = (long)Packet.readLength(); } } catch (Exception E) { throw new java.sql.SQLException(SQLError.get("S1000") + ": " + E.getClass().getName(), "S1000", -1); } if (Driver.trace) { Debug.msg(this, "Update Count = " + updateCount); } return new ResultSet(updateCount, updateID); } else { Field[] Fields = new Field[(int)columnCount]; // Read in the column information for (int i = 0; i < columnCount; i++) { Packet = readPacket(); String TableName = Packet.readLenString(); String ColName = Packet.readLenString(); int colLength = Packet.readnBytes(); int colType = Packet.readnBytes(); Packet.readByte(); // We know it's currently 2 short colFlag = (short)Buffer.ub(Packet.readByte()); int colDecimals = Buffer.ub(Packet.readByte()); Fields[i] = new Field(TableName, ColName, colLength, colType, colFlag, colDecimals); } Packet = readPacket(); Vector Rows = new Vector(); // Now read the data byte[][] Row = nextRow((int)columnCount); Rows.addElement(Row); int row_count = 1; while (Row != null && row_count < max_rows) { Row = nextRow((int)columnCount); if (Row != null) { Rows.addElement(Row); row_count++; } else { if (Driver.trace) { Debug.msg(this, "* NULL Row *"); } } } if (Driver.trace) { Debug.msg(this, "* Fetched " + Rows.size() + " rows from server *"); } return new ResultSet(Fields, Rows); } } /** * Retrieve one row from the MySQL server. * * Note: this method is not thread-safe, but it is only called * from methods that are guarded by synchronizing on this object. */ private final byte[][] nextRow(int columnCount) throws Exception { // Get the next incoming packet, re-using the packet because // all the data we need gets copied out of it. Buffer Packet = reuseAndReadPacket(_ReusablePacket); // check for errors. if (Packet.readByte() == (byte)0xff) { String ErrorMessage; int errno = 2000; if (_protocol_V > 9) { errno = Packet.readInt(); ErrorMessage = Packet.readString(); String XOpen = SQLError.mysqlToXOpen(errno); clearReceive(); throw new java.sql.SQLException(SQLError.get(SQLError.get(XOpen)) + ": " + ErrorMessage, XOpen, errno); } else { ErrorMessage = Packet.readString(); clearReceive(); throw new java.sql.SQLException(ErrorMessage, SQLError.mysqlToXOpen(errno), errno); } } // Away we go.... Packet.pos--; int[] dataStart = new int[columnCount]; byte[][] Row = new byte[columnCount][]; if (!Packet.isLastDataPacket()) { for (int i = 0; i < columnCount; i++) { int p = Packet.pos; dataStart[i] = p; Packet.pos = (int)Packet.readLength() + Packet.pos; } for (int i = 0; i < columnCount; i++) { Packet.pos = dataStart[i]; Row[i] = Packet.readLenByteArray(); if (Driver.trace) { if (Row[i] == null) { Debug.msg(this, "Field value: NULL"); } else { Debug.msg(this, "Field value: " + Row[i].toString()); } } } return Row; } return null; } /** * Log-off of the MySQL server and close the socket. */ final void quit() throws IOException { Buffer Packet = new Buffer(6); _packetSequence = -1; Packet.writeByte((byte)MysqlDefs.QUIT); send(Packet); _Mysql_Conn.close(); } /** * Get the major version of the MySQL server we are * talking to. */ final int getServerMajorVersion() { return _server_major_version; } /** * Get the minor version of the MySQL server we are * talking to. */ final int getServerMinorVersion() { return _server_minor_version; } /** * Get the sub-minor version of the MySQL server we are * talking to. */ final int getServerSubMinorVersion() { return _server_sub_minor_version; } /** * Read one packet from the MySQL server */ private final Buffer readPacket() throws IOException { byte b0, b1, b2; b0 = _Mysql_Input.readByte(); b1 = _Mysql_Input.readByte(); b2 = _Mysql_Input.readByte(); int packetLength = (int)(Buffer.ub(b0) + (256*Buffer.ub(b1)) + (256*256*Buffer.ub(b2))); byte packetSeq = _Mysql_Input.readByte(); // Read data byte[] buffer = new byte[packetLength + 1]; _Mysql_Input.readFully(buffer, 0, packetLength); buffer[packetLength] = 0; return new Buffer(buffer); } /** * Re-use a packet to read from the MySQL server */ private final Buffer reuseAndReadPacket(Buffer Reuse) throws IOException { byte b0, b1, b2; b0 = _Mysql_Input.readByte(); b1 = _Mysql_Input.readByte(); b2 = _Mysql_Input.readByte(); int packetLength = (int)(Buffer.ub(b0) + (256*Buffer.ub(b1)) + (256*256*Buffer.ub(b2))); byte packetSeq = _Mysql_Input.readByte(); // Set the Buffer to it's original state Reuse.pos = 0; Reuse.send_length = 0; // Do we need to re-alloc the byte buffer? // // Note: We actually check the length of the buffer, // rather than buf_length, because buf_length is not // necesarily the actual length of the byte array // used as the buffer if (Reuse.buf.length < packetLength) { Reuse.buf = new byte[packetLength]; } // Set the new length Reuse.buf_length = packetLength; // Read the data from the server _Mysql_Input.readFully(Reuse.buf, 0, packetLength); Reuse.buf[packetLength] = 0; // Null-termination return Reuse; } /** * Send a packet to the MySQL server */ private final void send(Buffer Packet) throws IOException { int l = Packet.pos; _packetSequence++; Packet.pos = 0; Packet.writeLongInt(l - HEADER_LENGTH); Packet.writeByte(_packetSequence); _Mysql_Output.write(Packet.buf, 0, l); _Mysql_Output.flush(); _Mysql_Buf_Output.flush(); int total_header_length = HEADER_LENGTH; } /** * Clear waiting data in the InputStream */ private final void clearReceive() throws IOException { int len = _Mysql_Buf_Input.available(); if (len > 0) { _Mysql_Input.skipBytes(len); } } /** * Clear all data in the InputStream that is being * sent by the MySQL server. */ private final void clearAllReceive() throws java.sql.SQLException { try { int len = _Mysql_Buf_Input.available(); if (len > 0) { Buffer Packet = readPacket(); if (Packet.buf[0] == (byte)0xff){ clearReceive(); return; } while (!Packet.isLastDataPacket()) { // larger than the socket buffer. Packet = readPacket(); if (Packet.buf[0] == (byte)0xff) break; } } clearReceive(); } catch (IOException E) { throw new SQLException("Communication link failure: " + E.getClass().getName(), "08S01"); } } static int getMaxBuf() { return MAXBUF; } }; |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.