mirror of
https://github.com/postgres/postgres.git
synced 2025-07-11 10:01:57 +03:00
Bring in Adrian's JDBC driver as an interface
This commit is contained in:
847
src/interfaces/jdbc/postgresql/Connection.java
Normal file
847
src/interfaces/jdbc/postgresql/Connection.java
Normal file
@ -0,0 +1,847 @@
|
||||
package postgresql;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.*;
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
import java.sql.*;
|
||||
import postgresql.*;
|
||||
|
||||
/**
|
||||
* @version 1.0 15-APR-1997
|
||||
* @author <A HREF="mailto:adrian@hottub.org">Adrian Hall</A>
|
||||
*
|
||||
* A Connection represents a session with a specific database. Within the
|
||||
* context of a Connection, SQL statements are executed and results are
|
||||
* returned.
|
||||
*
|
||||
* A Connection's database is able to provide information describing
|
||||
* its tables, its supported SQL grammar, its stored procedures, the
|
||||
* capabilities of this connection, etc. This information is obtained
|
||||
* with the getMetaData method.
|
||||
*
|
||||
* <B>Note:</B> By default, the Connection automatically commits changes
|
||||
* after executing each statement. If auto-commit has been disabled, an
|
||||
* explicit commit must be done or database changes will not be saved.
|
||||
*
|
||||
* @see java.sql.Connection
|
||||
*/
|
||||
public class Connection implements java.sql.Connection
|
||||
{
|
||||
private PG_Stream pg_stream;
|
||||
|
||||
private String PG_HOST;
|
||||
private int PG_PORT;
|
||||
private String PG_USER;
|
||||
private String PG_PASSWORD;
|
||||
private String PG_DATABASE;
|
||||
private boolean PG_STATUS;
|
||||
|
||||
public boolean CONNECTION_OK = true;
|
||||
public boolean CONNECTION_BAD = false;
|
||||
|
||||
private int STARTUP_CODE = 7;
|
||||
|
||||
private boolean autoCommit = true;
|
||||
private boolean readOnly = false;
|
||||
|
||||
private Driver this_driver;
|
||||
private String this_url;
|
||||
private String cursor = null; // The positioned update cursor name
|
||||
|
||||
/**
|
||||
* Connect to a PostgreSQL database back end.
|
||||
*
|
||||
* @param host the hostname of the database back end
|
||||
* @param port the port number of the postmaster process
|
||||
* @param info a Properties[] thing of the user and password
|
||||
* @param database the database to connect to
|
||||
* @param u the URL of the connection
|
||||
* @param d the Driver instantation of the connection
|
||||
* @return a valid connection profile
|
||||
* @exception SQLException if a database access error occurs
|
||||
*/
|
||||
public Connection(String host, int port, Properties info, String database, String url, Driver d) throws SQLException
|
||||
{
|
||||
int len = 288; // Length of a startup packet
|
||||
|
||||
this_driver = d;
|
||||
this_url = new String(url);
|
||||
PG_DATABASE = new String(database);
|
||||
PG_PASSWORD = new String(info.getProperty("password"));
|
||||
PG_USER = new String(info.getProperty("user"));
|
||||
PG_PORT = port;
|
||||
PG_HOST = new String(host);
|
||||
PG_STATUS = CONNECTION_BAD;
|
||||
|
||||
try
|
||||
{
|
||||
pg_stream = new PG_Stream(host, port);
|
||||
} catch (IOException e) {
|
||||
throw new SQLException ("Connection failed: " + e.toString());
|
||||
}
|
||||
|
||||
// Now we need to construct and send a startup packet
|
||||
try
|
||||
{
|
||||
pg_stream.SendInteger(len, 4); len -= 4;
|
||||
pg_stream.SendInteger(STARTUP_CODE, 4); len -= 4;
|
||||
pg_stream.Send(database.getBytes(), 64); len -= 64;
|
||||
pg_stream.Send(PG_USER.getBytes(), len);
|
||||
} catch (IOException e) {
|
||||
throw new SQLException("Connection failed: " + e.toString());
|
||||
}
|
||||
ExecSQL(" "); // Test connection
|
||||
PG_STATUS = CONNECTION_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL statements without parameters are normally executed using
|
||||
* Statement objects. If the same SQL statement is executed many
|
||||
* times, it is more efficient to use a PreparedStatement
|
||||
*
|
||||
* @return a new Statement object
|
||||
* @exception SQLException passed through from the constructor
|
||||
*/
|
||||
public java.sql.Statement createStatement() throws SQLException
|
||||
{
|
||||
return new Statement(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* A SQL statement with or without IN parameters can be pre-compiled
|
||||
* and stored in a PreparedStatement object. This object can then
|
||||
* be used to efficiently execute this statement multiple times.
|
||||
*
|
||||
* <B>Note:</B> This method is optimized for handling parametric
|
||||
* SQL statements that benefit from precompilation if the drivers
|
||||
* supports precompilation. PostgreSQL does not support precompilation.
|
||||
* In this case, the statement is not sent to the database until the
|
||||
* PreparedStatement is executed. This has no direct effect on users;
|
||||
* however it does affect which method throws certain SQLExceptions
|
||||
*
|
||||
* @param sql a SQL statement that may contain one or more '?' IN
|
||||
* parameter placeholders
|
||||
* @return a new PreparedStatement object containing the pre-compiled
|
||||
* statement.
|
||||
* @exception SQLException if a database access error occurs.
|
||||
*/
|
||||
public java.sql.PreparedStatement prepareStatement(String sql) throws SQLException
|
||||
{
|
||||
return new PreparedStatement(this, sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* A SQL stored procedure call statement is handled by creating a
|
||||
* CallableStatement for it. The CallableStatement provides methods
|
||||
* for setting up its IN and OUT parameters and methods for executing
|
||||
* it.
|
||||
*
|
||||
* <B>Note:</B> This method is optimised for handling stored procedure
|
||||
* call statements. Some drivers may send the call statement to the
|
||||
* database when the prepareCall is done; others may wait until the
|
||||
* CallableStatement is executed. This has no direct effect on users;
|
||||
* however, it does affect which method throws certain SQLExceptions
|
||||
*
|
||||
* @param sql a SQL statement that may contain one or more '?' parameter
|
||||
* placeholders. Typically this statement is a JDBC function call
|
||||
* escape string.
|
||||
* @return a new CallableStatement object containing the pre-compiled
|
||||
* SQL statement
|
||||
* @exception SQLException if a database access error occurs
|
||||
*/
|
||||
public java.sql.CallableStatement prepareCall(String sql) throws SQLException
|
||||
{
|
||||
throw new SQLException("Callable Statements are not supported at this time");
|
||||
// return new CallableStatement(this, sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* A driver may convert the JDBC sql grammar into its system's
|
||||
* native SQL grammar prior to sending it; nativeSQL returns the
|
||||
* native form of the statement that the driver would have sent.
|
||||
*
|
||||
* @param sql a SQL statement that may contain one or more '?'
|
||||
* parameter placeholders
|
||||
* @return the native form of this statement
|
||||
* @exception SQLException if a database access error occurs
|
||||
*/
|
||||
public String nativeSQL(String sql) throws SQLException
|
||||
{
|
||||
return sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* If a connection is in auto-commit mode, than all its SQL
|
||||
* statements will be executed and committed as individual
|
||||
* transactions. Otherwise, its SQL statements are grouped
|
||||
* into transactions that are terminated by either commit()
|
||||
* or rollback(). By default, new connections are in auto-
|
||||
* commit mode. The commit occurs when the statement completes
|
||||
* or the next execute occurs, whichever comes first. In the
|
||||
* case of statements returning a ResultSet, the statement
|
||||
* completes when the last row of the ResultSet has been retrieved
|
||||
* or the ResultSet has been closed. In advanced cases, a single
|
||||
* statement may return multiple results as well as output parameter
|
||||
* values. Here the commit occurs when all results and output param
|
||||
* values have been retrieved.
|
||||
*
|
||||
* @param autoCommit - true enables auto-commit; false disables it
|
||||
* @exception SQLException if a database access error occurs
|
||||
*/
|
||||
public void setAutoCommit(boolean autoCommit) throws SQLException
|
||||
{
|
||||
if (this.autoCommit == autoCommit)
|
||||
return;
|
||||
if (autoCommit)
|
||||
ExecSQL("end");
|
||||
else
|
||||
ExecSQL("begin");
|
||||
this.autoCommit = autoCommit;
|
||||
}
|
||||
|
||||
/**
|
||||
* gets the current auto-commit state
|
||||
*
|
||||
* @return Current state of the auto-commit mode
|
||||
* @exception SQLException (why?)
|
||||
* @see setAutoCommit
|
||||
*/
|
||||
public boolean getAutoCommit() throws SQLException
|
||||
{
|
||||
return this.autoCommit;
|
||||
}
|
||||
|
||||
/**
|
||||
* The method commit() makes all changes made since the previous
|
||||
* commit/rollback permanent and releases any database locks currently
|
||||
* held by the Connection. This method should only be used when
|
||||
* auto-commit has been disabled. (If autoCommit == true, then we
|
||||
* just return anyhow)
|
||||
*
|
||||
* @exception SQLException if a database access error occurs
|
||||
* @see setAutoCommit
|
||||
*/
|
||||
public void commit() throws SQLException
|
||||
{
|
||||
if (autoCommit)
|
||||
return;
|
||||
ExecSQL("commit");
|
||||
autoCommit = true;
|
||||
ExecSQL("begin");
|
||||
autoCommit = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The method rollback() drops all changes made since the previous
|
||||
* commit/rollback and releases any database locks currently held by
|
||||
* the Connection.
|
||||
*
|
||||
* @exception SQLException if a database access error occurs
|
||||
* @see commit
|
||||
*/
|
||||
public void rollback() throws SQLException
|
||||
{
|
||||
if (autoCommit)
|
||||
return;
|
||||
ExecSQL("rollback");
|
||||
autoCommit = true;
|
||||
ExecSQL("begin");
|
||||
autoCommit = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* In some cases, it is desirable to immediately release a Connection's
|
||||
* database and JDBC resources instead of waiting for them to be
|
||||
* automatically released (cant think why off the top of my head)
|
||||
*
|
||||
* <B>Note:</B> A Connection is automatically closed when it is
|
||||
* garbage collected. Certain fatal errors also result in a closed
|
||||
* connection.
|
||||
*
|
||||
* @exception SQLException if a database access error occurs
|
||||
*/
|
||||
public void close() throws SQLException
|
||||
{
|
||||
if (pg_stream != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
pg_stream.close();
|
||||
} catch (IOException e) {}
|
||||
pg_stream = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests to see if a Connection is closed
|
||||
*
|
||||
* @return the status of the connection
|
||||
* @exception SQLException (why?)
|
||||
*/
|
||||
public boolean isClosed() throws SQLException
|
||||
{
|
||||
return (pg_stream == null);
|
||||
}
|
||||
|
||||
/**
|
||||
* A connection's database is able to provide information describing
|
||||
* its tables, its supported SQL grammar, its stored procedures, the
|
||||
* capabilities of this connection, etc. This information is made
|
||||
* available through a DatabaseMetaData object.
|
||||
*
|
||||
* @return a DatabaseMetaData object for this connection
|
||||
* @exception SQLException if a database access error occurs
|
||||
*/
|
||||
public java.sql.DatabaseMetaData getMetaData() throws SQLException
|
||||
{
|
||||
// return new DatabaseMetaData(this);
|
||||
throw new SQLException("DatabaseMetaData not supported");
|
||||
}
|
||||
|
||||
/**
|
||||
* You can put a connection in read-only mode as a hunt to enable
|
||||
* database optimizations
|
||||
*
|
||||
* <B>Note:</B> setReadOnly cannot be called while in the middle
|
||||
* of a transaction
|
||||
*
|
||||
* @param readOnly - true enables read-only mode; false disables it
|
||||
* @exception SQLException if a database access error occurs
|
||||
*/
|
||||
public void setReadOnly (boolean readOnly) throws SQLException
|
||||
{
|
||||
this.readOnly = readOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests to see if the connection is in Read Only Mode. Note that
|
||||
* we cannot really put the database in read only mode, but we pretend
|
||||
* we can by returning the value of the readOnly flag
|
||||
*
|
||||
* @return true if the connection is read only
|
||||
* @exception SQLException if a database access error occurs
|
||||
*/
|
||||
public boolean isReadOnly() throws SQLException
|
||||
{
|
||||
return readOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* A sub-space of this Connection's database may be selected by
|
||||
* setting a catalog name. If the driver does not support catalogs,
|
||||
* it will silently ignore this request
|
||||
*
|
||||
* @exception SQLException if a database access error occurs
|
||||
*/
|
||||
public void setCatalog(String catalog) throws SQLException
|
||||
{
|
||||
// No-op
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the connections current catalog name, or null if no
|
||||
* catalog name is set, or we dont support catalogs.
|
||||
*
|
||||
* @return the current catalog name or null
|
||||
* @exception SQLException if a database access error occurs
|
||||
*/
|
||||
public String getCatalog() throws SQLException
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* You can call this method to try to change the transaction
|
||||
* isolation level using one of the TRANSACTION_* values.
|
||||
*
|
||||
* <B>Note:</B> setTransactionIsolation cannot be called while
|
||||
* in the middle of a transaction
|
||||
*
|
||||
* @param level one of the TRANSACTION_* isolation values with
|
||||
* the exception of TRANSACTION_NONE; some databases may
|
||||
* not support other values
|
||||
* @exception SQLException if a database access error occurs
|
||||
* @see java.sql.DatabaseMetaData#supportsTransactionIsolationLevel
|
||||
*/
|
||||
public void setTransactionIsolation(int level) throws SQLException
|
||||
{
|
||||
throw new SQLException("Transaction Isolation Levels are not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this Connection's current transaction isolation mode.
|
||||
*
|
||||
* @return the current TRANSACTION_* mode value
|
||||
* @exception SQLException if a database access error occurs
|
||||
*/
|
||||
public int getTransactionIsolation() throws SQLException
|
||||
{
|
||||
return java.sql.Connection.TRANSACTION_SERIALIZABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* The first warning reported by calls on this Connection is
|
||||
* returned.
|
||||
*
|
||||
* <B>Note:</B> Sebsequent warnings will be changed to this
|
||||
* SQLWarning
|
||||
*
|
||||
* @return the first SQLWarning or null
|
||||
* @exception SQLException if a database access error occurs
|
||||
*/
|
||||
public SQLWarning getWarnings() throws SQLException
|
||||
{
|
||||
return null; // We handle warnings as errors
|
||||
}
|
||||
|
||||
/**
|
||||
* After this call, getWarnings returns null until a new warning
|
||||
* is reported for this connection.
|
||||
*
|
||||
* @exception SQLException if a database access error occurs
|
||||
*/
|
||||
public void clearWarnings() throws SQLException
|
||||
{
|
||||
// Not handles since we handle wanrings as errors
|
||||
}
|
||||
|
||||
// **********************************************************
|
||||
// END OF PUBLIC INTERFACE
|
||||
// **********************************************************
|
||||
|
||||
/**
|
||||
* Send a query to the backend. Returns one of the ResultSet
|
||||
* objects.
|
||||
*
|
||||
* <B>Note:</B> there does not seem to be any method currently
|
||||
* in existance to return the update count.
|
||||
*
|
||||
* @param sql the SQL statement to be executed
|
||||
* @return a ResultSet holding the results
|
||||
* @exception SQLException if a database error occurs
|
||||
*/
|
||||
public synchronized ResultSet ExecSQL(String sql) throws SQLException
|
||||
{
|
||||
Field[] fields = null;
|
||||
Vector tuples = new Vector();
|
||||
byte[] buf = new byte[sql.length()];
|
||||
int fqp = 0;
|
||||
boolean hfr = false;
|
||||
String recv_status = null, msg;
|
||||
SQLException final_error = null;
|
||||
|
||||
if (sql.length() > 8192)
|
||||
throw new SQLException("SQL Statement too long: " + sql);
|
||||
try
|
||||
{
|
||||
pg_stream.SendChar('Q');
|
||||
buf = sql.getBytes();
|
||||
pg_stream.Send(buf);
|
||||
pg_stream.SendChar(0);
|
||||
} catch (IOException e) {
|
||||
throw new SQLException("I/O Error: " + e.toString());
|
||||
}
|
||||
|
||||
while (!hfr || fqp > 0)
|
||||
{
|
||||
int c = pg_stream.ReceiveChar();
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case 'A': // Asynchronous Notify
|
||||
int pid = pg_stream.ReceiveInteger(4);
|
||||
msg = pg_stream.ReceiveString(8192);
|
||||
break;
|
||||
case 'B': // Binary Data Transfer
|
||||
if (fields == null)
|
||||
throw new SQLException("Tuple received before MetaData");
|
||||
tuples.addElement(pg_stream.ReceiveTuple(fields.length, true));
|
||||
break;
|
||||
case 'C': // Command Status
|
||||
recv_status = pg_stream.ReceiveString(8192);
|
||||
if (fields != null)
|
||||
hfr = true;
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
pg_stream.SendChar('Q');
|
||||
pg_stream.SendChar(' ');
|
||||
pg_stream.SendChar(0);
|
||||
} catch (IOException e) {
|
||||
throw new SQLException("I/O Error: " + e.toString());
|
||||
}
|
||||
fqp++;
|
||||
}
|
||||
break;
|
||||
case 'D': // Text Data Transfer
|
||||
if (fields == null)
|
||||
throw new SQLException("Tuple received before MetaData");
|
||||
tuples.addElement(pg_stream.ReceiveTuple(fields.length, false));
|
||||
break;
|
||||
case 'E': // Error Message
|
||||
msg = pg_stream.ReceiveString(4096);
|
||||
final_error = new SQLException(msg);
|
||||
hfr = true;
|
||||
break;
|
||||
case 'I': // Empty Query
|
||||
int t = pg_stream.ReceiveChar();
|
||||
|
||||
if (t != 0)
|
||||
throw new SQLException("Garbled Data");
|
||||
if (fqp > 0)
|
||||
fqp--;
|
||||
if (fqp == 0)
|
||||
hfr = true;
|
||||
break;
|
||||
case 'N': // Error Notification
|
||||
msg = pg_stream.ReceiveString(4096);
|
||||
PrintStream log = DriverManager.getLogStream();
|
||||
log.println(msg);
|
||||
break;
|
||||
case 'P': // Portal Name
|
||||
String pname = pg_stream.ReceiveString(8192);
|
||||
break;
|
||||
case 'T': // MetaData Field Description
|
||||
if (fields != null)
|
||||
throw new SQLException("Cannot handle multiple result groups");
|
||||
fields = ReceiveFields();
|
||||
break;
|
||||
default:
|
||||
throw new SQLException("Unknown Response Type: " + (char)c);
|
||||
}
|
||||
}
|
||||
if (final_error != null)
|
||||
throw final_error;
|
||||
return new ResultSet(this, fields, tuples, recv_status, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive the field descriptions from the back end
|
||||
*
|
||||
* @return an array of the Field object describing the fields
|
||||
* @exception SQLException if a database error occurs
|
||||
*/
|
||||
private Field[] ReceiveFields() throws SQLException
|
||||
{
|
||||
int nf = pg_stream.ReceiveInteger(2), i;
|
||||
Field[] fields = new Field[nf];
|
||||
|
||||
for (i = 0 ; i < nf ; ++i)
|
||||
{
|
||||
String typname = pg_stream.ReceiveString(8192);
|
||||
int typid = pg_stream.ReceiveInteger(4);
|
||||
int typlen = pg_stream.ReceiveInteger(2);
|
||||
fields[i] = new Field(this, typname, typid, typlen);
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* In SQL, a result table can be retrieved through a cursor that
|
||||
* is named. The current row of a result can be updated or deleted
|
||||
* using a positioned update/delete statement that references the
|
||||
* cursor name.
|
||||
*
|
||||
* We support one cursor per connection.
|
||||
*
|
||||
* setCursorName sets the cursor name.
|
||||
*
|
||||
* @param cursor the cursor name
|
||||
* @exception SQLException if a database access error occurs
|
||||
*/
|
||||
public void setCursorName(String cursor) throws SQLException
|
||||
{
|
||||
this.cursor = cursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* getCursorName gets the cursor name.
|
||||
*
|
||||
* @return the current cursor name
|
||||
* @exception SQLException if a database access error occurs
|
||||
*/
|
||||
public String getCursorName() throws SQLException
|
||||
{
|
||||
return cursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* We are required to bring back certain information by
|
||||
* the DatabaseMetaData class. These functions do that.
|
||||
*
|
||||
* Method getURL() brings back the URL (good job we saved it)
|
||||
*
|
||||
* @return the url
|
||||
* @exception SQLException just in case...
|
||||
*/
|
||||
public String getURL() throws SQLException
|
||||
{
|
||||
return this_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method getUserName() brings back the User Name (again, we
|
||||
* saved it)
|
||||
*
|
||||
* @return the user name
|
||||
* @exception SQLException just in case...
|
||||
*/
|
||||
public String getUserName() throws SQLException
|
||||
{
|
||||
return PG_USER;
|
||||
}
|
||||
}
|
||||
|
||||
// ***********************************************************************
|
||||
|
||||
// This class handles all the Streamed I/O for a postgresql connection
|
||||
class PG_Stream
|
||||
{
|
||||
private Socket connection;
|
||||
private InputStream pg_input;
|
||||
private OutputStream pg_output;
|
||||
|
||||
/**
|
||||
* Constructor: Connect to the PostgreSQL back end and return
|
||||
* a stream connection.
|
||||
*
|
||||
* @param host the hostname to connect to
|
||||
* @param port the port number that the postmaster is sitting on
|
||||
* @exception IOException if an IOException occurs below it.
|
||||
*/
|
||||
public PG_Stream(String host, int port) throws IOException
|
||||
{
|
||||
connection = new Socket(host, port);
|
||||
pg_input = connection.getInputStream();
|
||||
pg_output = connection.getOutputStream();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a single character to the back end
|
||||
*
|
||||
* @param val the character to be sent
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public void SendChar(int val) throws IOException
|
||||
{
|
||||
pg_output.write(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an integer to the back end
|
||||
*
|
||||
* @param val the integer to be sent
|
||||
* @param siz the length of the integer in bytes (size of structure)
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public void SendInteger(int val, int siz) throws IOException
|
||||
{
|
||||
byte[] buf = new byte[siz];
|
||||
|
||||
while (siz-- > 0)
|
||||
{
|
||||
buf[siz] = (byte)(val & 0xff);
|
||||
val >>= 8;
|
||||
}
|
||||
Send(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an array of bytes to the backend
|
||||
*
|
||||
* @param buf The array of bytes to be sent
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public void Send(byte buf[]) throws IOException
|
||||
{
|
||||
pg_output.write(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an exact array of bytes to the backend - if the length
|
||||
* has not been reached, send nulls until it has.
|
||||
*
|
||||
* @param buf the array of bytes to be sent
|
||||
* @param siz the number of bytes to be sent
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public void Send(byte buf[], int siz) throws IOException
|
||||
{
|
||||
int i;
|
||||
|
||||
pg_output.write(buf, 0, (buf.length < siz ? buf.length : siz));
|
||||
if (buf.length < siz)
|
||||
{
|
||||
for (i = buf.length ; i < siz ; ++i)
|
||||
{
|
||||
pg_output.write(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives a single character from the backend
|
||||
*
|
||||
* @return the character received
|
||||
* @exception SQLException if an I/O Error returns
|
||||
*/
|
||||
public int ReceiveChar() throws SQLException
|
||||
{
|
||||
int c = 0;
|
||||
|
||||
try
|
||||
{
|
||||
c = pg_input.read();
|
||||
if (c < 0) throw new IOException("EOF");
|
||||
} catch (IOException e) {
|
||||
throw new SQLException("Error reading from backend: " + e.toString());
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives an integer from the backend
|
||||
*
|
||||
* @param siz length of the integer in bytes
|
||||
* @return the integer received from the backend
|
||||
* @exception SQLException if an I/O error occurs
|
||||
*/
|
||||
public int ReceiveInteger(int siz) throws SQLException
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
try
|
||||
{
|
||||
for (int i = 0 ; i < siz ; i++)
|
||||
{
|
||||
int b = pg_input.read();
|
||||
|
||||
if (b < 0)
|
||||
throw new IOException("EOF");
|
||||
n = n | (b >> (8 * i)) ;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new SQLException("Error reading from backend: " + e.toString());
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives a null-terminated string from the backend. Maximum of
|
||||
* maxsiz bytes - if we don't see a null, then we assume something
|
||||
* has gone wrong.
|
||||
*
|
||||
* @param maxsiz maximum length of string
|
||||
* @return string from back end
|
||||
* @exception SQLException if an I/O error occurs
|
||||
*/
|
||||
public String ReceiveString(int maxsiz) throws SQLException
|
||||
{
|
||||
byte[] rst = new byte[maxsiz];
|
||||
int s = 0;
|
||||
|
||||
try
|
||||
{
|
||||
while (s < maxsiz)
|
||||
{
|
||||
int c = pg_input.read();
|
||||
if (c < 0)
|
||||
throw new IOException("EOF");
|
||||
else if (c == 0)
|
||||
break;
|
||||
else
|
||||
rst[s++] = (byte)c;
|
||||
}
|
||||
if (s >= maxsiz)
|
||||
throw new IOException("Too Much Data");
|
||||
} catch (IOException e) {
|
||||
throw new SQLException("Error reading from backend: " + e.toString());
|
||||
}
|
||||
String v = new String(rst, 0, s);
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a tuple from the back end. A tuple is a two dimensional
|
||||
* array of bytes
|
||||
*
|
||||
* @param nf the number of fields expected
|
||||
* @param bin true if the tuple is a binary tuple
|
||||
* @return null if the current response has no more tuples, otherwise
|
||||
* an array of strings
|
||||
* @exception SQLException if a data I/O error occurs
|
||||
*/
|
||||
public byte[][] ReceiveTuple(int nf, boolean bin) throws SQLException
|
||||
{
|
||||
int i, bim = (nf + 7)/8;
|
||||
byte[] bitmask = Receive(bim);
|
||||
byte[][] answer = new byte[nf][0];
|
||||
|
||||
int whichbit = 0x80;
|
||||
int whichbyte = 0;
|
||||
|
||||
for (i = 0 ; i < nf ; ++i)
|
||||
{
|
||||
boolean isNull = ((bitmask[whichbyte] & whichbit) == 0);
|
||||
whichbit >>= 1;
|
||||
if (whichbit == 0)
|
||||
{
|
||||
++whichbyte;
|
||||
whichbit = 0x80;
|
||||
}
|
||||
if (isNull)
|
||||
answer[i] = null;
|
||||
else
|
||||
{
|
||||
int len = ReceiveInteger(4);
|
||||
if (!bin)
|
||||
len -= 4;
|
||||
if (len < 0)
|
||||
len = 0;
|
||||
answer[i] = Receive(len);
|
||||
}
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads in a given number of bytes from the backend
|
||||
*
|
||||
* @param siz number of bytes to read
|
||||
* @return array of bytes received
|
||||
* @exception SQLException if a data I/O error occurs
|
||||
*/
|
||||
private byte[] Receive(int siz) throws SQLException
|
||||
{
|
||||
byte[] answer = new byte[siz];
|
||||
int s = 0;
|
||||
|
||||
try
|
||||
{
|
||||
while (s < siz)
|
||||
{
|
||||
int w = pg_input.read(answer, s, siz - s);
|
||||
if (w < 0)
|
||||
throw new IOException("EOF");
|
||||
s += w;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new SQLException("Error reading from backend: " + e.toString());
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the connection
|
||||
*
|
||||
* @exception IOException if a IO Error occurs
|
||||
*/
|
||||
public void close() throws IOException
|
||||
{
|
||||
pg_output.close();
|
||||
pg_input.close();
|
||||
connection.close();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user