From ff246d7b64e41278cf1ac68a9245529a29dc98eb Mon Sep 17 00:00:00 2001
From: Marc G. Fournier
Date: Sat, 16 Aug 1997 20:51:53 +0000
Subject: Bring in Adrian's JDBC driver as an interface
---
src/interfaces/jdbc/postgresql/Connection.java | 847 +++++++++++++++++++++++++
1 file changed, 847 insertions(+)
create mode 100644 src/interfaces/jdbc/postgresql/Connection.java
(limited to 'src/interfaces/jdbc/postgresql/Connection.java')
diff --git a/src/interfaces/jdbc/postgresql/Connection.java b/src/interfaces/jdbc/postgresql/Connection.java
new file mode 100644
index 00000000000..aa354b61fe1
--- /dev/null
+++ b/src/interfaces/jdbc/postgresql/Connection.java
@@ -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 Adrian Hall
+ *
+ * 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.
+ *
+ * Note: 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.
+ *
+ * Note: 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.
+ *
+ * Note: 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)
+ *
+ * Note: 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
+ *
+ * Note: 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.
+ *
+ * Note: 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.
+ *
+ * Note: 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.
+ *
+ * Note: 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();
+ }
+}
--
cgit v1.2.3