*/
public class Connection implements java.sql.Connection
{
- private PG_Stream pg_stream;
+ protected PG_Stream pg_stream;
private String PG_HOST;
private int PG_PORT;
{
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
+ * This method is not part of the Connection interface. Its is an extension
+ * that allows access to the PostgreSQL Large Object API
*
- * @exception IOException if a IO Error occurs
+ * @return PGlobj class that implements the API
*/
- public void close() throws IOException
+ public PGlobj getLargeObjectAPI() throws SQLException
{
- pg_output.close();
- pg_input.close();
- connection.close();
+ return new PGlobj(this);
}
}
+
+// ***********************************************************************
+
--- /dev/null
+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
+ *
+ * This class is used by Connection & PGlobj for communicating with the
+ * backend.
+ *
+ * @see java.sql.Connection
+ */
+// This class handles all the Streamed I/O for a postgresql connection
+public 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);
+ byte b[] = new byte[1];
+ b[0] = (byte)val;
+ pg_output.write(b);
+ }
+
+ /**
+ * 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
+ {
+ Send(buf,0,siz);
+ }
+
+ /**
+ * 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 off offset in the array to start sending from
+ * @param siz the number of bytes to be sent
+ * @exception IOException if an I/O error occurs
+ */
+ public void Send(byte buf[], int off, int siz) throws IOException
+ {
+ int i;
+
+ pg_output.write(buf, off, ((buf.length-off) < siz ? (buf.length-off) : siz));
+ if((buf.length-off) < siz)
+ {
+ for (i = buf.length-off ; 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;
+ }
+
+ /**
+ * Reads in a given number of bytes from the backend
+ *
+ * @param buf buffer to store result
+ * @param off offset in buffer
+ * @param siz number of bytes to read
+ * @exception SQLException if a data I/O error occurs
+ */
+ public void Receive(byte[] b,int off,int siz) throws SQLException
+ {
+ int s = 0;
+
+ try
+ {
+ while (s < siz)
+ {
+ int w = pg_input.read(b, off+s, siz - s);
+ if (w < 0)
+ throw new IOException("EOF");
+ s += w;
+ }
+ } catch (IOException e) {
+ throw new SQLException("Error reading from backend: " + e.toString());
+ }
+ }
+
+ /**
+ * Closes the connection
+ *
+ * @exception IOException if a IO Error occurs
+ */
+ public void close() throws IOException
+ {
+ pg_output.close();
+ pg_input.close();
+ connection.close();
+ }
+}
--- /dev/null
+/**
+ * @version 6.2
+ *
+ * This implements a box consisting of two points
+ *
+ */
+
+package postgresql;
+
+import java.io.*;
+import java.sql.*;
+
+public class PGbox implements Serializable
+{
+ /**
+ * These are the two points.
+ */
+ public PGpoint point[] = new PGpoint[2];
+
+ public PGbox(double x1,double y1,double x2,double y2)
+ {
+ this.point[0] = new PGpoint(x1,y1);
+ this.point[1] = new PGpoint(x2,y2);
+ }
+
+ public PGbox(PGpoint p1,PGpoint p2)
+ {
+ this.point[0] = p1;
+ this.point[1] = p2;
+ }
+
+ /**
+ * This constructor is used by the driver.
+ */
+ public PGbox(String s) throws SQLException
+ {
+ PGtokenizer t = new PGtokenizer(s,',');
+ if(t.getSize() != 2)
+ throw new SQLException("conversion of box failed - "+s);
+
+ point[0] = new PGpoint(t.getToken(0));
+ point[1] = new PGpoint(t.getToken(1));
+ }
+
+ public boolean equals(Object obj)
+ {
+ PGbox p = (PGbox)obj;
+ return (p.point[0].equals(point[0]) && p.point[1].equals(point[1])) ||
+ (p.point[0].equals(point[1]) && p.point[1].equals(point[0]));
+ }
+
+ /**
+ * This returns the lseg in the syntax expected by postgresql
+ */
+ public String toString()
+ {
+ return point[0].toString()+","+point[1].toString();
+ }
+}
--- /dev/null
+/**
+ *
+ * This implements a circle consisting of a point and a radius
+ *
+ */
+
+package postgresql;
+
+import java.io.*;
+import java.sql.*;
+
+public class PGcircle implements Serializable
+{
+ /**
+ * This is the centre point
+ */
+ public PGpoint center;
+
+ /**
+ * This is the radius
+ */
+ double radius;
+
+ public PGcircle(double x,double y,double r)
+ {
+ this.center = new PGpoint(x,y);
+ this.radius = r;
+ }
+
+ public PGcircle(PGpoint c,double r)
+ {
+ this.center = c;
+ this.radius = r;
+ }
+
+ public PGcircle(PGcircle c)
+ {
+ this.center = new PGpoint(c.center);
+ this.radius = c.radius;
+ }
+
+ /**
+ * This constructor is used by the driver.
+ */
+ public PGcircle(String s) throws SQLException
+ {
+ PGtokenizer t = new PGtokenizer(PGtokenizer.removeAngle(s),',');
+ if(t.getSize() != 2)
+ throw new SQLException("conversion of circle failed - "+s);
+
+ try {
+ center = new PGpoint(t.getToken(0));
+ radius = Double.valueOf(t.getToken(1)).doubleValue();
+ } catch(NumberFormatException e) {
+ throw new SQLException("conversion of circle failed - "+s+" - +"+e.toString());
+ }
+ }
+
+ public boolean equals(Object obj)
+ {
+ PGcircle p = (PGcircle)obj;
+ return p.center.equals(center) && p.radius==radius;
+ }
+
+ /**
+ * This returns the circle in the syntax expected by postgresql
+ */
+ public String toString()
+ {
+ return "<"+center+","+radius+">";
+ }
+}
--- /dev/null
+// Java Interface to Postgres
+// $Id: PGlobj.java,v 1.1 1997/09/20 02:21:22 scrappy Exp $
+
+// Copyright (c) 1997 Peter T Mount
+
+package postgresql;
+
+import java.sql.*;
+import java.math.*;
+import java.net.*;
+import java.io.*;
+import java.util.*;
+
+/**
+ * This class implements the large object interface to postgresql.
+ *
+ * It provides the basic methods required to run the interface, plus
+ * a pair of methods that provide InputStream and OutputStream classes
+ * for this object.
+ */
+public class PGlobj
+{
+ // This table contains the function oid's used by the backend
+ private Hashtable func = new Hashtable();
+
+ protected postgresql.Connection conn;
+
+ /**
+ * These are the values for mode, taken from libpq-fs.h
+ */
+ public static final int INV_ARCHIVE = 0x00010000;
+ public static final int INV_WRITE = 0x00020000;
+ public static final int INV_READ = 0x00040000;
+
+ /**
+ * These are the functions that implement the interface
+ */
+ private static final String OPEN = "lo_open";
+ private static final String CLOSE = "lo_close";
+ private static final String CREATE = "lo_creat";
+ private static final String UNLINK = "lo_unlink";
+ private static final String SEEK = "lo_lseek";
+ private static final String TELL = "lo_tell";
+ private static final String READ = "loread";
+ private static final String WRITE = "lowrite";
+
+ /**
+ * This creates the interface
+ */
+ public PGlobj(Connection conn) throws SQLException
+ {
+ if(!(conn instanceof postgresql.Connection))
+ throw new SQLException("PGlobj: Wrong connection class");
+
+ this.conn = (postgresql.Connection)conn;
+ ResultSet res = (postgresql.ResultSet)conn.createStatement().executeQuery("select proname, oid from pg_proc" +
+ " where proname = 'lo_open'" +
+ " or proname = 'lo_close'" +
+ " or proname = 'lo_creat'" +
+ " or proname = 'lo_unlink'" +
+ " or proname = 'lo_lseek'" +
+ " or proname = 'lo_tell'" +
+ " or proname = 'loread'" +
+ " or proname = 'lowrite'");
+
+ if(res==null)
+ throw new SQLException("failed to initialise large object interface");
+
+ while(res.next()) {
+ func.put(res.getString(1),new Integer(res.getInt(2)));
+ DriverManager.println("PGlobj:func "+res.getString(1)+" oid="+res.getInt(2));
+ }
+ res.close();
+ }
+
+ // this returns the oid of the function
+ private int getFunc(String name) throws SQLException
+ {
+ Integer i = (Integer)func.get(name);
+ if(i==null)
+ throw new SQLException("unknown function: "+name);
+ return i.intValue();
+ }
+
+ /**
+ * This calls a function on the backend
+ * @param fnid oid of the function to run
+ * @param args array containing args, 3 ints per arg
+ */
+ public int PQfn(int fnid,int args[]) throws SQLException
+ {
+ return PQfn(fnid,args,null,0,0);
+ }
+
+ // fix bug in 6.1.1
+ public void writeInt(DataOutputStream data,int i) throws IOException
+ {
+ data.writeByte((i>>24)&0xff);
+ data.writeByte( i &0xff);
+ data.writeByte((i>>8) &0xff);
+ data.writeByte((i>>16)&0xff);
+ }
+
+ /**
+ * This calls a function on the backend
+ * @param fnid oid of the function to run
+ * @param args array containing args, 3 ints per arg
+ * @param buf byte array to write into, null returns result as an integer
+ * @param off offset in array
+ * @param len number of bytes to read
+ */
+ public int PQfn(int fnid,int args[],byte buf[],int off,int len) throws SQLException
+ {
+ //ByteArrayOutputStream b = new ByteArrayOutputStream();
+ //DataOutputStream data = new DataOutputStream(b);
+ int in = -1;
+
+ try {
+ int al=args.length/3;
+
+ // For some reason, the backend takes these in the reverse order
+ byte b[] = new byte[2+4+4];
+ int bp=0;
+ b[bp++]='F';
+ b[bp++]=0;
+ b[bp++]=(byte)((fnid)&0xff);
+ b[bp++]=(byte)((fnid>>24)&0xff);
+ b[bp++]=(byte)((fnid>>16)&0xff);
+ b[bp++]=(byte)((fnid>>8)&0xff);
+ b[bp++]=(byte)((al)&0xff);
+ b[bp++]=(byte)((al>>24)&0xff);
+ b[bp++]=(byte)((al>>16)&0xff);
+ b[bp++]=(byte)((al>>8)&0xff);
+ conn.pg_stream.Send(b);
+
+ //conn.pg_stream.SendChar('F');
+ //conn.pg_stream.SendInteger(fnid,4);
+ //conn.pg_stream.SendInteger(args.length / 3,4);
+
+ int l = args.length-1;
+ if(args[l]==0) l--;
+
+ for(int i=0;i<l;i++)
+ conn.pg_stream.SendInteger(args[i],4);
+
+ if(args[args.length-1]==0)
+ conn.pg_stream.Send(buf,off,len);
+
+ } catch(Exception e) {
+ throw new SQLException("lo_open failed");
+ }
+ //try {
+ if((in = conn.pg_stream.ReceiveChar())!='V') {
+ if(in=='E')
+ throw new SQLException(conn.pg_stream.ReceiveString(4096));
+ throw new SQLException("lobj: expected 'V' from backend, got "+((char)in));
+ }
+
+ while(true) {
+ in = conn.pg_stream.ReceiveChar();
+ switch(in)
+ {
+ case 'G':
+ if(buf==null)
+ in = conn.pg_stream.ReceiveInteger(4);
+ else
+ conn.pg_stream.Receive(buf,off,len);
+ conn.pg_stream.ReceiveChar();
+ return in;
+
+ case 'E':
+ throw new SQLException("lobj: error - "+conn.pg_stream.ReceiveString(4096));
+
+ case 'N':
+ conn.pg_stream.ReceiveString(4096);
+ break;
+
+ case '0':
+ return -1;
+
+ default:
+ throw new SQLException("lobj: protocol error");
+ }
+ }
+ // } catch(IOException ioe) {
+ // throw new SQLException("lobj: Network error - "+ioe);
+ //}
+ }
+
+ /**
+ * This opens a large object. It returns a handle that is used to
+ * access the object.
+ */
+ public int open(int lobjId,int mode) throws SQLException
+ {
+ int args[] = new int[2*3];
+ args[0] = args[3] = 4;
+ args[1] = args[4] = 1;
+ args[2] = lobjId;
+ args[5] = mode;
+
+ int fd = PQfn(getFunc(OPEN),args);
+ if(fd<0)
+ throw new SQLException("lo_open: no object");
+ seek(fd,0);
+ return fd;
+ }
+
+ /**
+ * This closes a large object.
+ */
+ public void close(int fd) throws SQLException
+ {
+ int args[] = new int[1*3];
+ args[0] = 4;
+ args[1] = 1;
+ args[2] = fd;
+
+ // flush/close streams here?
+ PQfn(getFunc(CLOSE),args);
+ }
+
+ /**
+ * This reads a block of bytes from the large object
+ * @param fd descriptor for an open large object
+ * @param buf byte array to write into
+ * @param off offset in array
+ * @param len number of bytes to read
+ */
+ public void read(int fd,byte buf[],int off,int len) throws SQLException
+ {
+ int args[] = new int[2*3];
+ args[0] = args[3] = 4;
+ args[1] = args[4] = 1;
+ args[2] = fd;
+ args[5] = len;
+
+ PQfn(getFunc(READ),args,buf,off,len);
+ }
+
+ /**
+ * This writes a block of bytes to an open large object
+ * @param fd descriptor for an open large object
+ * @param buf byte array to write into
+ * @param off offset in array
+ * @param len number of bytes to read
+ */
+ public void write(int fd,byte buf[],int off,int len) throws SQLException
+ {
+ int args[] = new int[2*3];
+ args[0] = args[3] = 4;
+ args[1] = args[4] = 1;
+ args[2] = fd;
+ args[5] = 0;
+
+ PQfn(getFunc(WRITE),args,buf,off,len);
+ }
+
+ /**
+ * This sets the current read or write location on a large object.
+ * @param fd descriptor of an open large object
+ * @param off offset in object
+ */
+ public void seek(int fd,int off) throws SQLException
+ {
+ int args[] = new int[3*3];
+ args[0] = args[3] = args[6] = 4;
+ args[1] = args[4] = args[7] = 1;
+ args[2] = fd;
+ args[5] = off;
+ args[8] = 0; // SEEK
+
+ PQfn(getFunc(SEEK),args);
+ }
+
+ /**
+ * This creates a new large object.
+ *
+ * the mode is a bitmask describing different attributes of the new object
+ *
+ * returns the oid of the large object created.
+ */
+ public int create(int mode) throws SQLException
+ {
+ int args[] = new int[1*3];
+ args[0] = 4;
+ args[1] = 1;
+ args[2] = mode;
+
+ return PQfn(getFunc(CREATE),args);
+ }
+
+ /**
+ * This returns the current location within the large object
+ */
+ public int tell(int fd) throws SQLException
+ {
+ int args[] = new int[1*3];
+ args[0] = 4;
+ args[1] = 1;
+ args[2] = fd;
+
+ return PQfn(getFunc(TELL),args);
+ }
+
+ /**
+ * This removes a large object from the database
+ */
+ public void unlink(int fd) throws SQLException
+ {
+ int args[] = new int[1*3];
+ args[0] = 4;
+ args[1] = 1;
+ args[2] = fd;
+
+ PQfn(getFunc(UNLINK),args);
+ }
+
+ /**
+ * This returns an InputStream based on an object
+ */
+ public InputStream getInputStream(int fd) throws SQLException
+ {
+ return (InputStream) new PGlobjInput(this,fd);
+ }
+
+ /**
+ * This returns an OutputStream based on an object
+ */
+ public OutputStream getOutputStream(int fd) throws SQLException
+ {
+ return (OutputStream) new PGlobjOutput(this,fd);
+ }
+
+ /**
+ * As yet, the lo_import and lo_export functions are not implemented.
+ */
+}
+
+// This class implements an InputStream based on a large object
+//
+// Note: Unlike most InputStreams, this one supports mark()/reset()
+//
+class PGlobjInput extends InputStream
+{
+ private PGlobj obj;
+ private int fd;
+
+ private int mp; // mark position
+ private int rl; // read limit
+
+ // This creates an Input stream based for a large object
+ public PGlobjInput(PGlobj obj,int fd)
+ {
+ this.obj = obj;
+ this.fd = fd;
+ }
+
+ public int read() throws IOException
+ {
+ byte b[] = new byte[1];
+ read(b,0,1);
+ return (int)b[0];
+ }
+
+ public int read(byte b[],int off,int len) throws IOException
+ {
+ try {
+ obj.read(fd,b,off,len);
+ } catch(SQLException e) {
+ throw new IOException(e.toString());
+ }
+ return len;
+ }
+
+ public long skip(long n) throws IOException
+ {
+ try {
+ int cp = obj.tell(fd);
+ obj.seek(fd,cp+(int)n);
+ return obj.tell(fd) - cp;
+ } catch(SQLException e) {
+ throw new IOException(e.toString());
+ }
+ }
+
+ public synchronized void mark(int readLimit)
+ {
+ try {
+ mp = obj.tell(fd);
+ rl = readLimit;
+ } catch(SQLException e) {
+ // We should throw an exception here, but mark() doesn't ;-(
+ }
+ }
+
+ public void reset() throws IOException
+ {
+ try {
+ int cp = obj.tell(fd);
+ if((cp-mp)>rl)
+ throw new IOException("mark invalidated");
+ obj.seek(fd,mp);
+ } catch(SQLException e) {
+ throw new IOException(e.toString());
+ }
+ }
+
+ public boolean markSupported()
+ {
+ return true;
+ }
+
+
+ public void close() throws IOException
+ {
+ try {
+ obj.close(fd);
+ } catch(SQLException e) {
+ throw new IOException(e.toString());
+ }
+ }
+}
+
+// This class implements an OutputStream to a large object
+class PGlobjOutput extends OutputStream
+{
+ private PGlobj obj;
+ private int fd;
+
+ // This creates an Input stream based for a large object
+ public PGlobjOutput(PGlobj obj,int fd)
+ {
+ this.obj = obj;
+ this.fd = fd;
+ }
+
+ public void write(int i) throws IOException
+ {
+ byte b[] = new byte[1];
+ b[0] = (byte)i;
+ write(b,0,1);
+ }
+
+ public void write(byte b[],int off,int len) throws IOException
+ {
+ try {
+ obj.write(fd,b,off,len);
+ } catch(SQLException e) {
+ throw new IOException(e.toString());
+ }
+ }
+
+ public void close() throws IOException
+ {
+ try {
+ obj.close(fd);
+ } catch(SQLException e) {
+ throw new IOException(e.toString());
+ }
+ }
+}
--- /dev/null
+/**
+ *
+ * This implements a lseg (line segment) consisting of two points
+ *
+ */
+
+package postgresql;
+
+import java.io.*;
+import java.sql.*;
+
+public class PGlseg implements Serializable
+{
+ /**
+ * These are the two points.
+ */
+ public PGpoint point[] = new PGpoint[2];
+
+ public PGlseg(double x1,double y1,double x2,double y2)
+ {
+ this.point[0] = new PGpoint(x1,y1);
+ this.point[1] = new PGpoint(x2,y2);
+ }
+
+ public PGlseg(PGpoint p1,PGpoint p2)
+ {
+ this.point[0] = p1;
+ this.point[1] = p2;
+ }
+
+ /**
+ * This constructor is used by the driver.
+ */
+ public PGlseg(String s) throws SQLException
+ {
+ PGtokenizer t = new PGtokenizer(PGtokenizer.removeBox(s),',');
+ if(t.getSize() != 2)
+ throw new SQLException("conversion of lseg failed - "+s);
+
+ point[0] = new PGpoint(t.getToken(0));
+ point[1] = new PGpoint(t.getToken(1));
+ }
+
+ public boolean equals(Object obj)
+ {
+ PGlseg p = (PGlseg)obj;
+ return (p.point[0].equals(point[0]) && p.point[1].equals(point[1])) ||
+ (p.point[0].equals(point[1]) && p.point[1].equals(point[0]));
+ }
+
+ /**
+ * This returns the lseg in the syntax expected by postgresql
+ */
+ public String toString()
+ {
+ return "["+point[0]+","+point[1]+"]";
+ }
+}
--- /dev/null
+/**
+ *
+ * This implements a path (a multiple segmented line, which may be closed)
+ *
+ */
+
+package postgresql;
+
+import java.io.*;
+import java.sql.*;
+
+public class PGpath implements Serializable
+{
+ public int npoints;
+ public boolean open;
+ public PGpoint point[];
+
+ public PGpath(int num,PGpoint[] points,boolean open)
+ {
+ npoints = num;
+ this.point = points;
+ this.open = open;
+ }
+
+ /**
+ * This constructor is used by the driver.
+ */
+ public PGpath(String s) throws SQLException
+ {
+ // First test to see if were open
+ if(s.startsWith("[") && s.endsWith("]")) {
+ open = true;
+ s = PGtokenizer.removeBox(s);
+ } else if(s.startsWith("(") && s.endsWith(")")) {
+ open = false;
+ s = PGtokenizer.removePara(s);
+ } else
+ throw new SQLException("cannot tell if path is open or closed");
+
+ PGtokenizer t = new PGtokenizer(s,',');
+ npoints = t.getSize();
+ point = new PGpoint[npoints];
+ for(int p=0;p<npoints;p++)
+ point[p] = new PGpoint(t.getToken(p));
+ }
+
+ public boolean equals(Object obj)
+ {
+ PGpath p = (PGpath)obj;
+
+ if(p.npoints != npoints)
+ return false;
+
+ if(p.open != open)
+ return false;
+
+ for(int i=0;i<npoints;i++)
+ if(!point[i].equals(p.point[i]))
+ return false;
+
+ return true;
+ }
+
+ /**
+ * This returns the polygon in the syntax expected by postgresql
+ */
+ public String toString()
+ {
+ StringBuffer b = new StringBuffer(open?"[":"(");
+
+ for(int p=0;p<npoints;p++)
+ b.append(point[p].toString());
+
+ b.append(open?"]":")");
+
+ return b.toString();
+ }
+
+ public boolean isOpen()
+ {
+ return open;
+ }
+
+ public boolean isClosed()
+ {
+ return !open;
+ }
+
+ public void closePath()
+ {
+ open = false;
+ }
+
+ public void openPath()
+ {
+ open = true;
+ }
+
+}
--- /dev/null
+/**
+ *
+ * This implements a version of java.awt.Point, except it uses double
+ * to represent the coordinates.
+ *
+ * It maps to the point datatype in postgresql.
+ */
+
+package postgresql;
+
+import java.awt.Point;
+import java.io.*;
+import java.sql.*;
+
+public class PGpoint implements Serializable
+{
+ /**
+ * These are the coordinates.
+ * These are public, because their equivalents in java.awt.Point are
+ */
+ public double x,y;
+
+ public PGpoint(double x,double y)
+ {
+ this.x = x;
+ this.y = y;
+ }
+
+ public PGpoint(PGpoint p)
+ {
+ this(p.x,p.y);
+ }
+
+ /**
+ * This constructor is used by the driver.
+ */
+ public PGpoint(String s) throws SQLException
+ {
+ PGtokenizer t = new PGtokenizer(PGtokenizer.removePara(s),',');
+ try {
+ x = Double.valueOf(t.getToken(0)).doubleValue();
+ y = Double.valueOf(t.getToken(1)).doubleValue();
+ } catch(NumberFormatException e) {
+ throw new SQLException("conversion of point failed - "+e.toString());
+ }
+ }
+
+ public boolean equals(Object obj)
+ {
+ PGpoint p = (PGpoint)obj;
+ return x == p.x && y == p.y;
+ }
+
+ /**
+ * This returns the point in the syntax expected by postgresql
+ */
+ public String toString()
+ {
+ return "("+x+","+y+")";
+ }
+
+ public void translate(int x,int y)
+ {
+ translate((double)x,(double)y);
+ }
+
+ public void translate(double x,double y)
+ {
+ this.x += x;
+ this.y += y;
+ }
+
+ public void move(int x,int y)
+ {
+ setLocation(x,y);
+ }
+
+ public void move(double x,double y)
+ {
+ this.x = x;
+ this.y = y;
+ }
+
+ // refer to java.awt.Point for description of this
+ public void setLocation(int x,int y)
+ {
+ move((double)x,(double)y);
+ }
+
+ // refer to java.awt.Point for description of this
+ public void setLocation(Point p)
+ {
+ setLocation(p.x,p.y);
+ }
+
+}
--- /dev/null
+/**
+ *
+ * This implements a polygon (based on java.awt.Polygon)
+ *
+ */
+
+package postgresql;
+
+import java.io.*;
+import java.sql.*;
+
+public class PGpolygon implements Serializable
+{
+ public int npoints;
+
+ public PGpoint point[];
+
+ public PGpolygon(int num,PGpoint[] points)
+ {
+ npoints = num;
+ this.point = points;
+ }
+
+ /**
+ * This constructor is used by the driver.
+ */
+ public PGpolygon(String s) throws SQLException
+ {
+ PGtokenizer t = new PGtokenizer(PGtokenizer.removePara(s),',');
+ npoints = t.getSize();
+ point = new PGpoint[npoints];
+ for(int p=0;p<npoints;p++)
+ point[p] = new PGpoint(t.getToken(p));
+ }
+
+ public boolean equals(Object obj)
+ {
+ PGpolygon p = (PGpolygon)obj;
+
+ if(p.npoints != npoints)
+ return false;
+
+ for(int i=0;i<npoints;i++)
+ if(!point[i].equals(p.point[i]))
+ return false;
+
+ return true;
+ }
+
+ /**
+ * This returns the polygon in the syntax expected by postgresql
+ */
+ public String toString()
+ {
+ StringBuffer b = new StringBuffer();
+ for(int p=0;p<npoints;p++)
+ b.append(point[p].toString());
+ return b.toString();
+ }
+}
--- /dev/null
+/**
+ *
+ * This class is used to tokenize the text output of postgres.
+ *
+ */
+
+package postgresql;
+
+import java.sql.*;
+import java.util.*;
+
+public class PGtokenizer
+{
+ protected Vector tokens;
+
+ public PGtokenizer(String string,char delim)
+ {
+ tokenize(string,delim);
+ }
+
+ /**
+ * Tokenizes a new string
+ */
+ public int tokenize(String string,char delim)
+ {
+ tokens = new Vector();
+
+ int nest=0,p,s;
+ for(p=0,s=0;p<string.length();p++) {
+ char c = string.charAt(p);
+
+ // increase nesting if an open character is found
+ if(c == '(' || c == '[')
+ nest++;
+
+ // decrease nesting if a close character is found
+ if(c == ')' || c == ']')
+ nest--;
+
+ if(nest==0 && c==delim) {
+ tokens.addElement(string.substring(s,p));
+ s=p+1; // +1 to skip the delimiter
+ }
+
+ }
+
+ // Don't forget the last token ;-)
+ if(s<string.length())
+ tokens.addElement(string.substring(s));
+
+ return tokens.size();
+ }
+
+ public int getSize()
+ {
+ return tokens.size();
+ }
+
+ public String getToken(int n)
+ {
+ return (String)tokens.elementAt(n);
+ }
+
+ /**
+ * This returns a new tokenizer based on one of our tokens
+ */
+ public PGtokenizer tokenizeToken(int n,char delim)
+ {
+ return new PGtokenizer(getToken(n),delim);
+ }
+
+ /**
+ * This removes the lead/trailing strings from a string
+ */
+ public static String remove(String s,String l,String t)
+ {
+ if(s.startsWith(l)) s = s.substring(l.length());
+ if(s.endsWith(t)) s = s.substring(0,s.length()-t.length());
+ return s;
+ }
+
+ /**
+ * This removes the lead/trailing strings from all tokens
+ */
+ public void remove(String l,String t)
+ {
+ for(int i=0;i<tokens.size();i++) {
+ tokens.setElementAt(remove((String)tokens.elementAt(i),l,t),i);
+ }
+ }
+
+ public static String removePara(String s) {return remove(s,"(",")");}
+ public void removePara() {remove("(",")");}
+
+ public static String removeBox(String s) {return remove(s,"[","]");}
+ public void removeBox() {remove("[","]");}
+
+ public static String removeAngle(String s) {return remove(s,"<",">");}
+ public void removeAngle() {remove("<",">");}
+}