OSDN Git Service

Bring in Peter's changes...finally :(
authorMarc G. Fournier <scrappy@hub.org>
Sat, 20 Sep 1997 02:21:25 +0000 (02:21 +0000)
committerMarc G. Fournier <scrappy@hub.org>
Sat, 20 Sep 1997 02:21:25 +0000 (02:21 +0000)
src/interfaces/jdbc/postgresql/Connection.java
src/interfaces/jdbc/postgresql/PG_Stream.java [new file with mode: 0644]
src/interfaces/jdbc/postgresql/PGbox.java [new file with mode: 0644]
src/interfaces/jdbc/postgresql/PGcircle.java [new file with mode: 0644]
src/interfaces/jdbc/postgresql/PGlobj.java [new file with mode: 0644]
src/interfaces/jdbc/postgresql/PGlseg.java [new file with mode: 0644]
src/interfaces/jdbc/postgresql/PGpath.java [new file with mode: 0644]
src/interfaces/jdbc/postgresql/PGpoint.java [new file with mode: 0644]
src/interfaces/jdbc/postgresql/PGpolygon.java [new file with mode: 0644]
src/interfaces/jdbc/postgresql/PGtokenizer.java [new file with mode: 0644]

index a208970..bb41dff 100644 (file)
@@ -28,7 +28,7 @@ import postgresql.*;
  */
 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;
@@ -591,256 +591,18 @@ public class Connection implements java.sql.Connection
   {
     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);
   }
 }
+
+// ***********************************************************************
+
diff --git a/src/interfaces/jdbc/postgresql/PG_Stream.java b/src/interfaces/jdbc/postgresql/PG_Stream.java
new file mode 100644 (file)
index 0000000..3e3350d
--- /dev/null
@@ -0,0 +1,309 @@
+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();
+  }
+}
diff --git a/src/interfaces/jdbc/postgresql/PGbox.java b/src/interfaces/jdbc/postgresql/PGbox.java
new file mode 100644 (file)
index 0000000..b8edc00
--- /dev/null
@@ -0,0 +1,59 @@
+/**
+ * @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();
+  }
+}
diff --git a/src/interfaces/jdbc/postgresql/PGcircle.java b/src/interfaces/jdbc/postgresql/PGcircle.java
new file mode 100644 (file)
index 0000000..543a0a7
--- /dev/null
@@ -0,0 +1,72 @@
+/**
+ *
+ * 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+">";
+  }
+}
diff --git a/src/interfaces/jdbc/postgresql/PGlobj.java b/src/interfaces/jdbc/postgresql/PGlobj.java
new file mode 100644 (file)
index 0000000..939ad9a
--- /dev/null
@@ -0,0 +1,462 @@
+// 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());
+    }
+  }
+}
diff --git a/src/interfaces/jdbc/postgresql/PGlseg.java b/src/interfaces/jdbc/postgresql/PGlseg.java
new file mode 100644 (file)
index 0000000..d073b46
--- /dev/null
@@ -0,0 +1,58 @@
+/**
+ *
+ * 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]+"]";
+  }
+}
diff --git a/src/interfaces/jdbc/postgresql/PGpath.java b/src/interfaces/jdbc/postgresql/PGpath.java
new file mode 100644 (file)
index 0000000..d87bbed
--- /dev/null
@@ -0,0 +1,99 @@
+/**
+ *
+ * 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;
+  }
+  
+}
diff --git a/src/interfaces/jdbc/postgresql/PGpoint.java b/src/interfaces/jdbc/postgresql/PGpoint.java
new file mode 100644 (file)
index 0000000..77a6173
--- /dev/null
@@ -0,0 +1,96 @@
+/**
+ *
+ * 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);
+  }
+  
+}
diff --git a/src/interfaces/jdbc/postgresql/PGpolygon.java b/src/interfaces/jdbc/postgresql/PGpolygon.java
new file mode 100644 (file)
index 0000000..a17ceed
--- /dev/null
@@ -0,0 +1,60 @@
+/**
+ *
+ * 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();
+  }
+}
diff --git a/src/interfaces/jdbc/postgresql/PGtokenizer.java b/src/interfaces/jdbc/postgresql/PGtokenizer.java
new file mode 100644 (file)
index 0000000..d41bfef
--- /dev/null
@@ -0,0 +1,100 @@
+/**
+ *
+ * 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("<",">");}
+}