diff options
| author | Marc G. Fournier <scrappy@hub.org> | 1998-01-13 02:19:56 +0000 |
|---|---|---|
| committer | Marc G. Fournier <scrappy@hub.org> | 1998-01-13 02:19:56 +0000 |
| commit | 06bad7807584351c2ab1cd544d677fa0ea087544 (patch) | |
| tree | c17f9da61c34ca63d8f4e4ba56cf1980d6a62c13 /src/interfaces/jdbc/postgresql | |
| parent | c77c608aa2aba1d8b8e70f6d35371d09b48ff584 (diff) | |
| download | postgresql-06bad7807584351c2ab1cd544d677fa0ea087544.tar.gz | |
Oops...missed over half the patch :(
Diffstat (limited to 'src/interfaces/jdbc/postgresql')
| -rw-r--r-- | src/interfaces/jdbc/postgresql/fastpath/Fastpath.java | 291 | ||||
| -rw-r--r-- | src/interfaces/jdbc/postgresql/fastpath/FastpathArg.java | 106 | ||||
| -rw-r--r-- | src/interfaces/jdbc/postgresql/geometric/PGbox.java | 105 | ||||
| -rw-r--r-- | src/interfaces/jdbc/postgresql/geometric/PGcircle.java | 108 | ||||
| -rw-r--r-- | src/interfaces/jdbc/postgresql/geometric/PGlseg.java | 100 | ||||
| -rw-r--r-- | src/interfaces/jdbc/postgresql/geometric/PGpath.java | 145 | ||||
| -rw-r--r-- | src/interfaces/jdbc/postgresql/geometric/PGpoint.java | 167 | ||||
| -rw-r--r-- | src/interfaces/jdbc/postgresql/geometric/PGpolygon.java | 105 | ||||
| -rw-r--r-- | src/interfaces/jdbc/postgresql/largeobject/LargeObject.java | 253 | ||||
| -rw-r--r-- | src/interfaces/jdbc/postgresql/largeobject/LargeObjectManager.java | 205 | ||||
| -rw-r--r-- | src/interfaces/jdbc/postgresql/util/PGobject.java | 102 | ||||
| -rw-r--r-- | src/interfaces/jdbc/postgresql/util/PGtokenizer.java | 197 |
12 files changed, 1884 insertions, 0 deletions
diff --git a/src/interfaces/jdbc/postgresql/fastpath/Fastpath.java b/src/interfaces/jdbc/postgresql/fastpath/Fastpath.java new file mode 100644 index 0000000000..ce278acef7 --- /dev/null +++ b/src/interfaces/jdbc/postgresql/fastpath/Fastpath.java @@ -0,0 +1,291 @@ +package postgresql.fastpath; + +import java.io.*; +import java.lang.*; +import java.net.*; +import java.util.*; +import java.sql.*; +import postgresql.util.*; + +/** + * This class implements the Fastpath api. + * + * <p>This is a means of executing functions imbeded in the postgresql backend + * from within a java application. + * + * <p>It is based around the file src/interfaces/libpq/fe-exec.c + * + * + * <p><b>Implementation notes:</b> + * + * <p><b><em>Network protocol:</em></b> + * + * <p>The code within the backend reads integers in reverse. + * + * <p>There is work in progress to convert all of the protocol to + * network order but it may not be there for v6.3 + * + * <p>When fastpath switches, simply replace SendIntegerReverse() with + * SendInteger() + * + * @see postgresql.FastpathFastpathArg + * @see postgresql.LargeObject + */ +public class Fastpath +{ + // This maps the functions names to their id's (possible unique just + // to a connection). + protected Hashtable func = new Hashtable(); + + protected postgresql.Connection conn; // our connection + protected postgresql.PG_Stream stream; // the network stream + + /** + * Initialises the fastpath system + * + * <p><b>Important Notice</b> + * <br>This is called from postgresql.Connection, and should not be called + * from client code. + * + * @param conn postgresql.Connection to attach to + * @param stream The network stream to the backend + */ + public Fastpath(postgresql.Connection conn,postgresql.PG_Stream stream) + { + this.conn=conn; + this.stream=stream; + DriverManager.println("Fastpath initialised"); + } + + /** + * Send a function call to the PostgreSQL backend + * + * @param fnid Function id + * @param resulttype True if the result is an integer, false for other results + * @param args FastpathArguments to pass to fastpath + * @return null if no data, Integer if an integer result, or byte[] otherwise + * @exception SQLException if a database-access error occurs. + */ + public Object fastpath(int fnid,boolean resulttype,FastpathArg[] args) throws SQLException + { + // send the function call + try { + // 70 is 'F' in ASCII. Note: don't use SendChar() here as it adds padding + // that confuses the backend. The 0 terminates the command line. + stream.SendInteger(70,1); + stream.SendInteger(0,1); + + stream.SendIntegerReverse(fnid,4); + stream.SendIntegerReverse(args.length,4); + + for(int i=0;i<args.length;i++) + args[i].send(stream); + + // This is needed, otherwise data can be lost + stream.flush(); + + } catch(IOException ioe) { + throw new SQLException("Failed to send fastpath call "+fnid+"\n"+ioe); + } + + // Now handle the result + + // We should get 'V' on sucess or 'E' on error. Anything else is treated + // as an error. + //int in = stream.ReceiveChar(); + //DriverManager.println("ReceiveChar() = "+in+" '"+((char)in)+"'"); + //if(in!='V') { + //if(in=='E') + //throw new SQLException(stream.ReceiveString(4096)); + //throw new SQLException("Fastpath: expected 'V' from backend, got "+((char)in)); + //} + + // Now loop, reading the results + Object result = null; // our result + while(true) { + int in = stream.ReceiveChar(); + DriverManager.println("ReceiveChar() = "+in+" '"+((char)in)+"'"); + switch(in) + { + case 'V': + break; + + //------------------------------ + // Function returned properly + // + case 'G': + int sz = stream.ReceiveInteger(4); + DriverManager.println("G: size="+sz); //debug + + // Return an Integer if + if(resulttype) + result = new Integer(stream.ReceiveInteger(sz)); + else { + byte buf[] = new byte[sz]; + stream.Receive(buf,0,sz); + result = buf; + } + break; + + //------------------------------ + // Error message returned + case 'E': + throw new SQLException("Fastpath: "+stream.ReceiveString(4096)); + + //------------------------------ + // Notice from backend + case 'N': + conn.addWarning(stream.ReceiveString(4096)); + break; + + //------------------------------ + // End of results + // + // Here we simply return res, which would contain the result + // processed earlier. If no result, this already contains null + case '0': + DriverManager.println("returning "+result); + return result; + + default: + throw new SQLException("Fastpath: protocol error. Got '"+((char)in)+"'"); + } + } + } + + /** + * Send a function call to the PostgreSQL backend by name. + * + * Note: the mapping for the procedure name to function id needs to exist, + * usually to an earlier call to addfunction(). + * + * This is the prefered method to call, as function id's can/may change + * between versions of the backend. + * + * For an example of how this works, refer to postgresql.LargeObject + * + * @param name Function name + * @param resulttype True if the result is an integer, false for other + * results + * @param args FastpathArguments to pass to fastpath + * @return null if no data, Integer if an integer result, or byte[] otherwise + * @exception SQLException if name is unknown or if a database-access error + * occurs. + * @see postgresql.LargeObject + */ + public Object fastpath(String name,boolean resulttype,FastpathArg[] args) throws SQLException + { + DriverManager.println("Fastpath: calling "+name); + return fastpath(getID(name),resulttype,args); + } + + /** + * This convenience method assumes that the return value is an Integer + * @param name Function name + * @param args Function arguments + * @return integer result + * @exception SQLException if a database-access error occurs or no result + */ + public int getInteger(String name,FastpathArg[] args) throws SQLException + { + Integer i = (Integer)fastpath(name,true,args); + if(i==null) + throw new SQLException("Fastpath:"+name+": no result returned, expected integer"); + return i.intValue(); + } + + /** + * This convenience method assumes that the return value is an Integer + * @param name Function name + * @param args Function arguments + * @return byte[] array containing result + * @exception SQLException if a database-access error occurs or no result + */ + public byte[] getData(String name,FastpathArg[] args) throws SQLException + { + return (byte[])fastpath(name,false,args); + } + + /** + * This adds a function to our lookup table. + * + * <p>User code should use the addFunctions method, which is based upon a + * query, rather than hard coding the oid. The oid for a function is not + * guaranteed to remain static, even on different servers of the same + * version. + * + * @param name Function name + * @param fnid Function id + */ + public void addFunction(String name,int fnid) + { + func.put(name,new Integer(fnid)); + } + + /** + * This takes a ResultSet containing two columns. Column 1 contains the + * function name, Column 2 the oid. + * + * <p>It reads the entire ResultSet, loading the values into the function + * table. + * + * <p><b>REMEMBER</b> to close() the resultset after calling this!! + * + * <p><b><em>Implementation note about function name lookups:</em></b> + * + * <p>PostgreSQL stores the function id's and their corresponding names in + * the pg_proc table. To speed things up locally, instead of querying each + * function from that table when required, a Hashtable is used. Also, only + * the function's required are entered into this table, keeping connection + * times as fast as possible. + * + * <p>The postgresql.LargeObject class performs a query upon it's startup, + * and passes the returned ResultSet to the addFunctions() method here. + * + * <p>Once this has been done, the LargeObject api refers to the functions by + * name. + * + * <p>Dont think that manually converting them to the oid's will work. Ok, + * they will for now, but they can change during development (there was some + * discussion about this for V7.0), so this is implemented to prevent any + * unwarranted headaches in the future. + * + * @param rs ResultSet + * @exception SQLException if a database-access error occurs. + * @see postgresql.LargeObjectManager + */ + public void addFunctions(ResultSet rs) throws SQLException + { + while(rs.next()) { + func.put(rs.getString(1),new Integer(rs.getInt(2))); + } + } + + /** + * This returns the function id associated by its name + * + * <p>If addFunction() or addFunctions() have not been called for this name, + * then an SQLException is thrown. + * + * @param name Function name to lookup + * @return Function ID for fastpath call + * @exception SQLException is function is unknown. + */ + public int getID(String name) throws SQLException + { + Integer id = (Integer)func.get(name); + + // may be we could add a lookup to the database here, and store the result + // in our lookup table, throwing the exception if that fails. + // We must, however, ensure that if we do, any existing ResultSet is + // unaffected, otherwise we could break user code. + // + // so, until we know we can do this (needs testing, on the TODO list) + // for now, we throw the exception and do no lookups. + if(id==null) + throw new SQLException("Fastpath: function "+name+" is unknown"); + + return id.intValue(); + } +} + diff --git a/src/interfaces/jdbc/postgresql/fastpath/FastpathArg.java b/src/interfaces/jdbc/postgresql/fastpath/FastpathArg.java new file mode 100644 index 0000000000..9b800073c2 --- /dev/null +++ b/src/interfaces/jdbc/postgresql/fastpath/FastpathArg.java @@ -0,0 +1,106 @@ +package postgresql.fastpath; + +import java.io.*; +import java.lang.*; +import java.net.*; +import java.util.*; +import java.sql.*; +import postgresql.util.*; + +/** + * Each fastpath call requires an array of arguments, the number and type + * dependent on the function being called. + * + * <p>This class implements methods needed to provide this capability. + * + * <p>For an example on how to use this, refer to the postgresql.largeobject + * package + * + * @see postgresql.fastpath.Fastpath + * @see postgresql.largeobject.LargeObjectManager + * @see postgresql.largeobject.LargeObject + */ +public class FastpathArg +{ + /** + * Type of argument, true=integer, false=byte[] + */ + public boolean type; + + /** + * Integer value if type=true + */ + public int value; + + /** + * Byte value if type=false; + */ + public byte[] bytes; + + /** + * Constructs an argument that consists of an integer value + * @param value int value to set + */ + public FastpathArg(int value) + { + type=true; + this.value=value; + } + + /** + * Constructs an argument that consists of an array of bytes + * @param bytes array to store + */ + public FastpathArg(byte bytes[]) + { + type=false; + this.bytes=bytes; + } + + /** + * Constructs an argument that consists of part of a byte array + * @param buf source array + * @param off offset within array + * @param len length of data to include + */ + public FastpathArg(byte buf[],int off,int len) + { + type=false; + bytes = new byte[len]; + System.arraycopy(buf,off,bytes,0,len); + } + + /** + * Constructs an argument that consists of a String. + * @param s String to store + */ + public FastpathArg(String s) + { + this(s.getBytes()); + } + + /** + * This sends this argument down the network stream. + * + * <p>The stream sent consists of the length.int4 then the contents. + * + * <p><b>Note:</b> This is called from Fastpath, and cannot be called from + * client code. + * + * @param s output stream + * @exception IOException if something failed on the network stream + */ + protected void send(postgresql.PG_Stream s) throws IOException + { + if(type) { + // argument is an integer + s.SendIntegerReverse(4,4); // size of an integer + s.SendIntegerReverse(value,4); // integer value of argument + } else { + // argument is a byte array + s.SendIntegerReverse(bytes.length,4); // size of array + s.Send(bytes); + } + } +} + diff --git a/src/interfaces/jdbc/postgresql/geometric/PGbox.java b/src/interfaces/jdbc/postgresql/geometric/PGbox.java new file mode 100644 index 0000000000..1814832690 --- /dev/null +++ b/src/interfaces/jdbc/postgresql/geometric/PGbox.java @@ -0,0 +1,105 @@ +package postgresql.geometric; + +import java.io.*; +import java.sql.*; +import postgresql.util.*; + +/** + * This represents the box datatype within postgresql. + */ +public class PGbox extends PGobject implements Serializable,Cloneable +{ + /** + * These are the two points. + */ + public PGpoint point[] = new PGpoint[2]; + + /** + * @param x1 first x coordinate + * @param y1 first y coordinate + * @param x2 second x coordinate + * @param y2 second y coordinate + */ + public PGbox(double x1,double y1,double x2,double y2) + { + this(); + this.point[0] = new PGpoint(x1,y1); + this.point[1] = new PGpoint(x2,y2); + } + + /** + * @param p1 first point + * @param p2 second point + */ + public PGbox(PGpoint p1,PGpoint p2) + { + this(); + this.point[0] = p1; + this.point[1] = p2; + } + + /** + * @param s Box definition in PostgreSQL syntax + * @exception SQLException if definition is invalid + */ + public PGbox(String s) throws SQLException + { + this(); + setValue(s); + } + + /** + * Required constructor + */ + public PGbox() + { + setType("box"); + } + + /** + * This method sets the value of this object. It should be overidden, + * but still called by subclasses. + * + * @param value a string representation of the value of the object + * @exception SQLException thrown if value is invalid for this type + */ + public void setValue(String value) throws SQLException + { + PGtokenizer t = new PGtokenizer(value,','); + if(t.getSize() != 2) + throw new SQLException("conversion of box failed - "+value); + + point[0] = new PGpoint(t.getToken(0)); + point[1] = new PGpoint(t.getToken(1)); + } + + /** + * @param obj Object to compare with + * @return true if the two boxes are identical + */ + public boolean equals(Object obj) + { + if(obj instanceof PGbox) { + 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])); + } + return false; + } + + /** + * This must be overidden to allow the object to be cloned + */ + public Object clone() + { + return new PGbox((PGpoint)point[0].clone(),(PGpoint)point[1].clone()); + } + + /** + * @return the PGbox in the syntax expected by postgresql + */ + public String getValue() + { + return point[0].toString()+","+point[1].toString(); + } +} diff --git a/src/interfaces/jdbc/postgresql/geometric/PGcircle.java b/src/interfaces/jdbc/postgresql/geometric/PGcircle.java new file mode 100644 index 0000000000..105ed91a2f --- /dev/null +++ b/src/interfaces/jdbc/postgresql/geometric/PGcircle.java @@ -0,0 +1,108 @@ +package postgresql.geometric; + +import java.io.*; +import java.sql.*; +import postgresql.util.*; + +/** + * This represents postgresql's circle datatype, consisting of a point and + * a radius + */ +public class PGcircle extends PGobject implements Serializable,Cloneable +{ + /** + * This is the centre point + */ + public PGpoint center; + + /** + * This is the radius + */ + double radius; + + /** + * @param x coordinate of centre + * @param y coordinate of centre + * @param r radius of circle + */ + public PGcircle(double x,double y,double r) + { + this(new PGpoint(x,y),r); + } + + /** + * @param c PGpoint describing the circle's centre + * @param r radius of circle + */ + public PGcircle(PGpoint c,double r) + { + this(); + this.center = c; + this.radius = r; + } + + /** + * @param s definition of the circle in PostgreSQL's syntax. + * @exception SQLException on conversion failure + */ + public PGcircle(String s) throws SQLException + { + this(); + setValue(s); + } + + /** + * This constructor is used by the driver. + */ + public PGcircle() + { + setType("circle"); + } + + /** + * @param s definition of the circle in PostgreSQL's syntax. + * @exception SQLException on conversion failure + */ + public void setValue(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()); + } + } + + /** + * @param obj Object to compare with + * @return true if the two boxes are identical + */ + public boolean equals(Object obj) + { + if(obj instanceof PGcircle) { + PGcircle p = (PGcircle)obj; + return p.center.equals(center) && p.radius==radius; + } + return false; + } + + /** + * This must be overidden to allow the object to be cloned + */ + public Object clone() + { + return new PGcircle((PGpoint)center.clone(),radius); + } + + /** + * @return the PGcircle in the syntax expected by postgresql + */ + public String getValue() + { + return "<"+center+","+radius+">"; + } +} diff --git a/src/interfaces/jdbc/postgresql/geometric/PGlseg.java b/src/interfaces/jdbc/postgresql/geometric/PGlseg.java new file mode 100644 index 0000000000..9daddfc91a --- /dev/null +++ b/src/interfaces/jdbc/postgresql/geometric/PGlseg.java @@ -0,0 +1,100 @@ +package postgresql.geometric; + +import java.io.*; +import java.sql.*; +import postgresql.util.*; + +/** + * This implements a lseg (line segment) consisting of two points + */ +public class PGlseg extends PGobject implements Serializable,Cloneable +{ + /** + * These are the two points. + */ + public PGpoint point[] = new PGpoint[2]; + + /** + * @param x1 coordinate for first point + * @param y1 coordinate for first point + * @param x2 coordinate for second point + * @param y2 coordinate for second point + */ + public PGlseg(double x1,double y1,double x2,double y2) + { + this(new PGpoint(x1,y1),new PGpoint(x2,y2)); + } + + /** + * @param p1 first point + * @param p2 second point + */ + public PGlseg(PGpoint p1,PGpoint p2) + { + this(); + this.point[0] = p1; + this.point[1] = p2; + } + + /** + * @param s definition of the circle in PostgreSQL's syntax. + * @exception SQLException on conversion failure + */ + public PGlseg(String s) throws SQLException + { + this(); + setValue(s); + } + + /** + * reuired by the driver + */ + public PGlseg() + { + setType("lseg"); + } + + /** + * @param s Definition of the line segment in PostgreSQL's syntax + * @exception SQLException on conversion failure + */ + public void setValue(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)); + } + + /** + * @param obj Object to compare with + * @return true if the two boxes are identical + */ + public boolean equals(Object obj) + { + if(obj instanceof PGlseg) { + 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])); + } + return false; + } + + /** + * This must be overidden to allow the object to be cloned + */ + public Object clone() + { + return new PGlseg((PGpoint)point[0].clone(),(PGpoint)point[1].clone()); + } + + /** + * @return the PGlseg in the syntax expected by postgresql + */ + public String getValue() + { + return "["+point[0]+","+point[1]+"]"; + } +} diff --git a/src/interfaces/jdbc/postgresql/geometric/PGpath.java b/src/interfaces/jdbc/postgresql/geometric/PGpath.java new file mode 100644 index 0000000000..45c162ac8a --- /dev/null +++ b/src/interfaces/jdbc/postgresql/geometric/PGpath.java @@ -0,0 +1,145 @@ +package postgresql.geometric; + +import java.io.*; +import java.sql.*; +import postgresql.util.*; + +/** + * This implements a path (a multiple segmented line, which may be closed) + */ +public class PGpath extends PGobject implements Serializable,Cloneable +{ + /** + * True if the path is open, false if closed + */ + public boolean open; + + /** + * The points defining this path + */ + public PGpoint points[]; + + /** + * @param points the PGpoints that define the path + * @param open True if the path is open, false if closed + */ + public PGpath(PGpoint[] points,boolean open) + { + this(); + this.points = points; + this.open = open; + } + + /** + * Required by the driver + */ + public PGpath() + { + setType("path"); + } + + /** + * @param s definition of the circle in PostgreSQL's syntax. + * @exception SQLException on conversion failure + */ + public PGpath(String s) throws SQLException + { + this(); + setValue(s); + } + + /** + * @param s Definition of the path in PostgreSQL's syntax + * @exception SQLException on conversion failure + */ + public void setValue(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,','); + int npoints = t.getSize(); + points = new PGpoint[npoints]; + for(int p=0;p<npoints;p++) + points[p] = new PGpoint(t.getToken(p)); + } + + /** + * @param obj Object to compare with + * @return true if the two boxes are identical + */ + public boolean equals(Object obj) + { + if(obj instanceof PGpath) { + PGpath p = (PGpath)obj; + + if(p.points.length != points.length) + return false; + + if(p.open != open) + return false; + + for(int i=0;i<points.length;i++) + if(!points[i].equals(p.points[i])) + return false; + + return true; + } + return false; + } + + /** + * This must be overidden to allow the object to be cloned + */ + public Object clone() + { + PGpoint ary[] = new PGpoint[points.length]; + for(int i=0;i<points.length;i++) + ary[i]=(PGpoint)points[i].clone(); + return new PGpath(ary,open); + } + + /** + * This returns the polygon in the syntax expected by postgresql + */ + public String getValue() + { + StringBuffer b = new StringBuffer(open?"[":"("); + + for(int p=0;p<points.length;p++) { + if(p>0) b.append(","); + b.append(points[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/geometric/PGpoint.java b/src/interfaces/jdbc/postgresql/geometric/PGpoint.java new file mode 100644 index 0000000000..29f2c75346 --- /dev/null +++ b/src/interfaces/jdbc/postgresql/geometric/PGpoint.java @@ -0,0 +1,167 @@ +package postgresql.geometric; + +import java.awt.Point; +import java.io.*; +import java.sql.*; + +import postgresql.util.*; + +/** + * This implements a version of java.awt.Point, except it uses double + * to represent the coordinates. + * + * <p>It maps to the point datatype in postgresql. + */ +public class PGpoint extends PGobject implements Serializable,Cloneable +{ + /** + * The X coordinate of the point + */ + public double x; + + /** + * The Y coordinate of the point + */ + public double y; + + /** + * @param x coordinate + * @param y coordinate + */ + public PGpoint(double x,double y) + { + this(); + this.x = x; + this.y = y; + } + + /** + * This is called mainly from the other geometric types, when a + * point is imbeded within their definition. + * + * @param value Definition of this point in PostgreSQL's syntax + */ + public PGpoint(String value) throws SQLException + { + this(); + setValue(value); + } + + /** + * Required by the driver + */ + public PGpoint() + { + setType("point"); + } + + /** + * @param s Definition of this point in PostgreSQL's syntax + * @exception SQLException on conversion failure + */ + public void setValue(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()); + } + } + + /** + * @param obj Object to compare with + * @return true if the two boxes are identical + */ + public boolean equals(Object obj) + { + if(obj instanceof PGpoint) { + PGpoint p = (PGpoint)obj; + return x == p.x && y == p.y; + } + return false; + } + + /** + * This must be overidden to allow the object to be cloned + */ + public Object clone() + { + return new PGpoint(x,y); + } + + /** + * @return the PGpoint in the syntax expected by postgresql + */ + public String getValue() + { + return "("+x+","+y+")"; + } + + /** + * Translate the point with the supplied amount. + * @param x integer amount to add on the x axis + * @param y integer amount to add on the y axis + */ + public void translate(int x,int y) + { + translate((double)x,(double)y); + } + + /** + * Translate the point with the supplied amount. + * @param x double amount to add on the x axis + * @param y double amount to add on the y axis + */ + public void translate(double x,double y) + { + this.x += x; + this.y += y; + } + + /** + * Moves the point to the supplied coordinates. + * @param x integer coordinate + * @param y integer coordinate + */ + public void move(int x,int y) + { + setLocation(x,y); + } + + /** + * Moves the point to the supplied coordinates. + * @param x double coordinate + * @param y double coordinate + */ + public void move(double x,double y) + { + this.x = x; + this.y = y; + } + + /** + * Moves the point to the supplied coordinates. + * refer to java.awt.Point for description of this + * @param x integer coordinate + * @param y integer coordinate + * @see java.awt.Point + */ + public void setLocation(int x,int y) + { + move((double)x,(double)y); + } + + /** + * Moves the point to the supplied java.awt.Point + * refer to java.awt.Point for description of this + * @param p Point to move to + * @see java.awt.Point + */ + public void setLocation(Point p) + { + setLocation(p.x,p.y); + } + +} diff --git a/src/interfaces/jdbc/postgresql/geometric/PGpolygon.java b/src/interfaces/jdbc/postgresql/geometric/PGpolygon.java new file mode 100644 index 0000000000..b60c16e8ce --- /dev/null +++ b/src/interfaces/jdbc/postgresql/geometric/PGpolygon.java @@ -0,0 +1,105 @@ +package postgresql.geometric; + +import java.io.*; +import java.sql.*; +import postgresql.util.*; + +/** + * This implements the polygon datatype within PostgreSQL. + */ +public class PGpolygon extends PGobject implements Serializable,Cloneable +{ + /** + * The points defining the polygon + */ + public PGpoint points[]; + + /** + * Creates a polygon using an array of PGpoints + * + * @param points the points defining the polygon + */ + public PGpolygon(PGpoint[] points) + { + this(); + this.points = points; + } + + /** + * @param s definition of the circle in PostgreSQL's syntax. + * @exception SQLException on conversion failure + */ + public PGpolygon(String s) throws SQLException + { + this(); + setValue(s); + } + + /** + * Required by the driver + */ + public PGpolygon() + { + setType("polygon"); + } + + /** + * @param s Definition of the polygon in PostgreSQL's syntax + * @exception SQLException on conversion failure + */ + public void setValue(String s) throws SQLException + { + PGtokenizer t = new PGtokenizer(PGtokenizer.removePara(s),','); + int npoints = t.getSize(); + points = new PGpoint[npoints]; + for(int p=0;p<npoints;p++) + points[p] = new PGpoint(t.getToken(p)); + } + + /** + * @param obj Object to compare with + * @return true if the two boxes are identical + */ + public boolean equals(Object obj) + { + if(obj instanceof PGpolygon) { + PGpolygon p = (PGpolygon)obj; + + if(p.points.length != points.length) + return false; + + for(int i=0;i<points.length;i++) + if(!points[i].equals(p.points[i])) + return false; + + return true; + } + return false; + } + + /** + * This must be overidden to allow the object to be cloned + */ + public Object clone() + { + PGpoint ary[] = new PGpoint[points.length]; + for(int i=0;i<points.length;i++) + ary[i] = (PGpoint)points[i].clone(); + return new PGpolygon(ary); + } + + /** + * @return the PGpolygon in the syntax expected by postgresql + */ + public String getValue() + { + StringBuffer b = new StringBuffer(); + b.append("("); + for(int p=0;p<points.length;p++) { + if(p>0) b.append(","); + b.append(points[p].toString()); + } + b.append(")"); + return b.toString(); + } +} diff --git a/src/interfaces/jdbc/postgresql/largeobject/LargeObject.java b/src/interfaces/jdbc/postgresql/largeobject/LargeObject.java new file mode 100644 index 0000000000..0cab20f8e3 --- /dev/null +++ b/src/interfaces/jdbc/postgresql/largeobject/LargeObject.java @@ -0,0 +1,253 @@ +package postgresql.largeobject; + +import java.io.*; +import java.lang.*; +import java.net.*; +import java.util.*; +import java.sql.*; + +import postgresql.fastpath.*; + +/** + * This class implements the large object interface to postgresql. + * + * <p>It provides the basic methods required to run the interface, plus + * a pair of methods that provide InputStream and OutputStream classes + * for this object. + * + * <p>Normally, client code would use the getAsciiStream, getBinaryStream, + * or getUnicodeStream methods in ResultSet, or setAsciiStream, + * setBinaryStream, or setUnicodeStream methods in PreparedStatement to + * access Large Objects. + * + * <p>However, sometimes lower level access to Large Objects are required, + * that are not supported by the JDBC specification. + * + * <p>Refer to postgresql.largeobject.LargeObjectManager on how to gain access + * to a Large Object, or how to create one. + * + * @see postgresql.largeobject.LargeObjectManager + * @see postgresql.ResultSet#getAsciiStream + * @see postgresql.ResultSet#getBinaryStream + * @see postgresql.ResultSet#getUnicodeStream + * @see postgresql.PreparedStatement#setAsciiStream + * @see postgresql.PreparedStatement#setBinaryStream + * @see postgresql.PreparedStatement#setUnicodeStream + * @see java.sql.ResultSet#getAsciiStream + * @see java.sql.ResultSet#getBinaryStream + * @see java.sql.ResultSet#getUnicodeStream + * @see java.sql.PreparedStatement#setAsciiStream + * @see java.sql.PreparedStatement#setBinaryStream + * @see java.sql.PreparedStatement#setUnicodeStream + * + */ +public class LargeObject +{ + /** + * Indicates a seek from the begining of a file + */ + public static final int SEEK_SET = 0; + + /** + * Indicates a seek from the current position + */ + public static final int SEEK_CUR = 1; + + /** + * Indicates a seek from the end of a file + */ + public static final int SEEK_END = 2; + + private Fastpath fp; // Fastpath API to use + private int oid; // OID of this object + private int fd; // the descriptor of the open large object + + /** + * This opens a large object. + * + * <p>If the object does not exist, then an SQLException is thrown. + * + * @param fp FastPath API for the connection to use + * @param oid of the Large Object to open + * @param mode Mode of opening the large object + * (defined in LargeObjectManager) + * @exception SQLException if a database-access error occurs. + * @see postgresql.largeobject.LargeObjectManager + */ + protected LargeObject(Fastpath fp,int oid,int mode) throws SQLException + { + this.fp = fp; + this.oid = oid; + + FastpathArg args[] = new FastpathArg[2]; + args[0] = new FastpathArg(oid); + args[1] = new FastpathArg(mode); + this.fd = fp.getInteger("lo_open",args); + } + + /** + * @return the OID of this LargeObject + */ + public int getOID() + { + return oid; + } + + /** + * This method closes the object. You must not call methods in this + * object after this is called. + * @exception SQLException if a database-access error occurs. + */ + public void close() throws SQLException + { + FastpathArg args[] = new FastpathArg[1]; + args[0] = new FastpathArg(fd); + fp.fastpath("lo_close",false,args); // true here as we dont care!! + } + + /** + * Reads some data from the object, and return as a byte[] array + * + * @param len number of bytes to read + * @return byte[] array containing data read + * @exception SQLException if a database-access error occurs. + */ + public byte[] read(int len) throws SQLException + { + FastpathArg args[] = new FastpathArg[2]; + args[0] = new FastpathArg(fd); + args[1] = new FastpathArg(len); + return fp.getData("loread",args); + } + + /** + * Reads some data from the object into an existing array + * + * @param buf destination array + * @param off offset within array + * @param len number of bytes to read + * @exception SQLException if a database-access error occurs. + */ + public void read(byte buf[],int off,int len) throws SQLException + { + System.arraycopy(read(len),0,buf,off,len); + } + + /** + * Writes an array to the object + * + * @param buf array to write + * @exception SQLException if a database-access error occurs. + */ + public void write(byte buf[]) throws SQLException + { + FastpathArg args[] = new FastpathArg[2]; + args[0] = new FastpathArg(fd); + args[1] = new FastpathArg(buf); + fp.fastpath("lowrite",false,args); + } + + /** + * Writes some data from an array to the object + * + * @param buf destination array + * @param off offset within array + * @param len number of bytes to write + * @exception SQLException if a database-access error occurs. + */ + public void write(byte buf[],int off,int len) throws SQLException + { + byte data[] = new byte[len]; + System.arraycopy(buf,off,data,0,len); + write(data); + } + + /** + * Sets the current position within the object. + * + * <p>This is similar to the fseek() call in the standard C library. It + * allows you to have random access to the large object. + * + * @param pos position within object + * @param ref Either SEEK_SET, SEEK_CUR or SEEK_END + * @exception SQLException if a database-access error occurs. + */ + public void seek(int pos,int ref) throws SQLException + { + FastpathArg args[] = new FastpathArg[3]; + args[0] = new FastpathArg(fd); + args[1] = new FastpathArg(pos); + args[2] = new FastpathArg(ref); + fp.fastpath("lo_lseek",false,args); + } + + /** + * Sets the current position within the object. + * + * <p>This is similar to the fseek() call in the standard C library. It + * allows you to have random access to the large object. + * + * @param pos position within object from begining + * @exception SQLException if a database-access error occurs. + */ + public void seek(int pos) throws SQLException + { + seek(pos,SEEK_SET); + } + + /** + * @return the current position within the object + * @exception SQLException if a database-access error occurs. + */ + public int tell() throws SQLException + { + FastpathArg args[] = new FastpathArg[1]; + args[0] = new FastpathArg(fd); + return fp.getInteger("lo_tell",args); + } + + /** + * This method is inefficient, as the only way to find out the size of + * the object is to seek to the end, record the current position, then + * return to the original position. + * + * <p>A better method will be found in the future. + * + * @return the size of the large object + * @exception SQLException if a database-access error occurs. + */ + public int size() throws SQLException + { + int cp = tell(); + seek(0,SEEK_END); + int sz = tell(); + seek(cp,SEEK_SET); + return sz; + } + + /** + * Returns an InputStream from this object. + * + * <p>This InputStream can then be used in any method that requires an + * InputStream. + * + * @exception SQLException if a database-access error occurs. + */ + public InputStream getInputStream() throws SQLException + { + throw new SQLException("LargeObject:getInputStream not implemented"); + } + + /** + * Returns an OutputStream to this object + * + * <p>This OutputStream can then be used in any method that requires an + * OutputStream. + * + * @exception SQLException if a database-access error occurs. + */ + public OutputStream getOutputStream() throws SQLException + { + throw new SQLException("LargeObject:getOutputStream not implemented"); + } +} diff --git a/src/interfaces/jdbc/postgresql/largeobject/LargeObjectManager.java b/src/interfaces/jdbc/postgresql/largeobject/LargeObjectManager.java new file mode 100644 index 0000000000..c7798d15a1 --- /dev/null +++ b/src/interfaces/jdbc/postgresql/largeobject/LargeObjectManager.java @@ -0,0 +1,205 @@ +package postgresql.largeobject; + +import java.io.*; +import java.lang.*; +import java.net.*; +import java.util.*; +import java.sql.*; + +import postgresql.fastpath.*; + +/** + * This class implements the large object interface to postgresql. + * + * <p>It provides methods that allow client code to create, open and delete + * large objects from the database. When opening an object, an instance of + * postgresql.largeobject.LargeObject is returned, and its methods then allow + * access to the object. + * + * <p>This class can only be created by postgresql.Connection + * + * <p>To get access to this class, use the following segment of code: + * <br><pre> + * import postgresql.largeobject.*; + * + * Connection conn; + * LargeObjectManager lobj; + * + * ... code that opens a connection ... + * + * lobj = ((postgresql.Connection)myconn).getLargeObjectAPI(); + * </pre> + * + * <p>Normally, client code would use the getAsciiStream, getBinaryStream, + * or getUnicodeStream methods in ResultSet, or setAsciiStream, + * setBinaryStream, or setUnicodeStream methods in PreparedStatement to + * access Large Objects. + * + * <p>However, sometimes lower level access to Large Objects are required, + * that are not supported by the JDBC specification. + * + * <p>Refer to postgresql.largeobject.LargeObject on how to manipulate the + * contents of a Large Object. + * + * @see postgresql.largeobject.LargeObject + * @see postgresql.ResultSet#getAsciiStream + * @see postgresql.ResultSet#getBinaryStream + * @see postgresql.ResultSet#getUnicodeStream + * @see postgresql.PreparedStatement#setAsciiStream + * @see postgresql.PreparedStatement#setBinaryStream + * @see postgresql.PreparedStatement#setUnicodeStream + * @see java.sql.ResultSet#getAsciiStream + * @see java.sql.ResultSet#getBinaryStream + * @see java.sql.ResultSet#getUnicodeStream + * @see java.sql.PreparedStatement#setAsciiStream + * @see java.sql.PreparedStatement#setBinaryStream + * @see java.sql.PreparedStatement#setUnicodeStream + */ +public class LargeObjectManager +{ + // the fastpath api for this connection + private Fastpath fp; + + /** + * This mode indicates we want to write to an object + */ + public static final int WRITE = 0x00020000; + + /** + * This mode indicates we want to read an object + */ + public static final int READ = 0x00040000; + + /** + * This mode is the default. It indicates we want read and write access to + * a large object + */ + public static final int READWRITE = READ | WRITE; + + /** + * This prevents us being created by mere mortals + */ + private LargeObjectManager() + { + } + + /** + * Constructs the LargeObject API. + * + * <p><b>Important Notice</b> + * <br>This method should only be called by postgresql.Connection + * + * <p>There should only be one LargeObjectManager per Connection. The + * postgresql.Connection class keeps track of the various extension API's + * and it's advised you use those to gain access, and not going direct. + */ + public LargeObjectManager(postgresql.Connection conn) throws SQLException + { + // We need Fastpath to do anything + this.fp = conn.getFastpathAPI(); + + // Now get the function oid's for the api + // + // This is an example of Fastpath.addFunctions(); + // + 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 LargeObject API"); + + fp.addFunctions(res); + res.close(); + DriverManager.println("Large Object initialised"); + } + + /** + * This opens an existing large object, based on its OID. This method + * assumes that READ and WRITE access is required (the default). + * + * @param oid of large object + * @return LargeObject instance providing access to the object + * @exception SQLException on error + */ + public LargeObject open(int oid) throws SQLException + { + return new LargeObject(fp,oid,READWRITE); + } + + /** + * This opens an existing large object, based on its OID + * + * @param oid of large object + * @param mode mode of open + * @return LargeObject instance providing access to the object + * @exception SQLException on error + */ + public LargeObject open(int oid,int mode) throws SQLException + { + return new LargeObject(fp,oid,mode); + } + + /** + * This creates a large object, returning its OID. + * + * <p>It defaults to READWRITE for the new object's attributes. + * + * @return oid of new object + * @exception SQLException on error + */ + public int create() throws SQLException + { + FastpathArg args[] = new FastpathArg[1]; + args[0] = new FastpathArg(READWRITE); + return fp.getInteger("lo_creat",args); + } + + /** + * This creates a large object, returning its OID + * + * @param mode a bitmask describing different attributes of the new object + * @return oid of new object + * @exception SQLException on error + */ + public int create(int mode) throws SQLException + { + FastpathArg args[] = new FastpathArg[1]; + args[0] = new FastpathArg(mode); + return fp.getInteger("lo_creat",args); + } + + /** + * This deletes a large object. + * + * @param oid describing object to delete + * @exception SQLException on error + */ + public void delete(int oid) throws SQLException + { + FastpathArg args[] = new FastpathArg[1]; + args[0] = new FastpathArg(oid); + fp.fastpath("lo_unlink",false,args); + } + + /** + * This deletes a large object. + * + * <p>It is identical to the delete method, and is supplied as the C API uses + * unlink. + * + * @param oid describing object to delete + * @exception SQLException on error + */ + public void unlink(int oid) throws SQLException + { + delete(oid); + } + +} diff --git a/src/interfaces/jdbc/postgresql/util/PGobject.java b/src/interfaces/jdbc/postgresql/util/PGobject.java new file mode 100644 index 0000000000..62b3d55f5e --- /dev/null +++ b/src/interfaces/jdbc/postgresql/util/PGobject.java @@ -0,0 +1,102 @@ +package postgresql.util; + +import java.io.*; +import java.lang.*; +import java.sql.*; +import java.util.*; + +/** + * postgresql.PG_Object is a class used to describe unknown types + * An unknown type is any type that is unknown by JDBC Standards + * + * <p>As of PostgreSQL 6.3, this allows user code to add their own + * handlers via a call to postgresql.Connection. These handlers + * must extend this class. + */ +public class PGobject implements Serializable,Cloneable +{ + protected String type; + protected String value; + + /** + * This is called by postgresql.Connection.getObject() to create the + * object. + */ + public PGobject() + { + } + + /** + * This method sets the type of this object. + * + * <p>It should not be extended by subclasses, hence its final + * + * @param type a string describing the type of the object + */ + public final void setType(String type) + { + this.type = type; + } + + /** + * This method sets the value of this object. It must be overidden. + * + * @param value a string representation of the value of the object + * @exception SQLException thrown if value is invalid for this type + */ + public void setValue(String value) throws SQLException + { + this.value = value; + } + + /** + * As this cannot change during the life of the object, it's final. + * @return the type name of this object + */ + public final String getType() + { + return type; + } + + /** + * This must be overidden, to return the value of the object, in the + * form required by postgresql. + * @return the value of this object + */ + public String getValue() + { + return value; + } + + /** + * This must be overidden to allow comparisons of objects + * @param obj Object to compare with + * @return true if the two boxes are identical + */ + public boolean equals(Object obj) + { + if(obj instanceof PGobject) + return ((PGobject)obj).getValue().equals(getValue()); + return false; + } + + /** + * This must be overidden to allow the object to be cloned + */ + public Object clone() + { + PGobject obj = new PGobject(); + obj.type=type; + obj.value=value; + return obj; + } + + /** + * This is defined here, so user code need not overide it. + * @return the value of this object, in the syntax expected by postgresql + */ + public String toString() + { + return value; + } +} diff --git a/src/interfaces/jdbc/postgresql/util/PGtokenizer.java b/src/interfaces/jdbc/postgresql/util/PGtokenizer.java new file mode 100644 index 0000000000..0d07596891 --- /dev/null +++ b/src/interfaces/jdbc/postgresql/util/PGtokenizer.java @@ -0,0 +1,197 @@ +package postgresql.util; + +import java.sql.*; +import java.util.*; + +/** + * This class is used to tokenize the text output of postgres. + * + * <p>It's mainly used by the geometric classes, but is useful in parsing any + * output from custom data types output from postgresql. + * + * @see postgresql.geometric.PGbox + * @see postgresql.geometric.PGcircle + * @see postgresql.geometric.PGlseg + * @see postgresql.geometric.PGpath + * @see postgresql.geometric.PGpoint + * @see postgresql.geometric.PGpolygon + */ +public class PGtokenizer +{ + // Our tokens + protected Vector tokens; + + /** + * Create a tokeniser. + * + * <p>We could have used StringTokenizer to do this, however, we needed to + * handle nesting of '(' ')' '[' ']' '<' and '>' as these are used + * by the geometric data types. + * + * @param string containing tokens + * @param delim single character to split the tokens + */ + public PGtokenizer(String string,char delim) + { + tokenize(string,delim); + } + + /** + * This resets this tokenizer with a new string and/or delimiter. + * + * @param string containing tokens + * @param delim single character to split the tokens + */ + public int tokenize(String string,char delim) + { + tokens = new Vector(); + + // nest holds how many levels we are in the current token. + // if this is > 0 then we don't split a token when delim is matched. + // + // The Geometric datatypes use this, because often a type may have others + // (usualls PGpoint) imbedded within a token. + // + // Peter 1998 Jan 6 - Added < and > to the nesting rules + 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 == '[' || c == '<') + nest++; + + // decrease nesting if a close character is found + if(c == ')' || 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(); + } + + /** + * @return the number of tokens available + */ + public int getSize() + { + return tokens.size(); + } + + /** + * @param n Token number ( 0 ... getSize()-1 ) + * @return The token value + */ + public String getToken(int n) + { + return (String)tokens.elementAt(n); + } + + /** + * This returns a new tokenizer based on one of our tokens. + * + * The geometric datatypes use this to process nested tokens (usually + * PGpoint). + * + * @param n Token number ( 0 ... getSize()-1 ) + * @param delim The delimiter to use + * @return A new instance of PGtokenizer based on the token + */ + public PGtokenizer tokenizeToken(int n,char delim) + { + return new PGtokenizer(getToken(n),delim); + } + + /** + * This removes the lead/trailing strings from a string + * @param s Source string + * @param l Leading string to remove + * @param t Trailing string to remove + * @return String without the lead/trailing strings + */ + 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 + * @param l Leading string to remove + * @param t Trailing string to remove + */ + 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); + } + } + + /** + * Removes ( and ) from the beginning and end of a string + * @param s String to remove from + * @return String without the ( or ) + */ + public static String removePara(String s) + { + return remove(s,"(",")"); + } + + /** + * Removes ( and ) from the beginning and end of all tokens + * @return String without the ( or ) + */ + public void removePara() + { + remove("(",")"); + } + + /** + * Removes [ and ] from the beginning and end of a string + * @param s String to remove from + * @return String without the [ or ] + */ + public static String removeBox(String s) + { + return remove(s,"[","]"); + } + + /** + * Removes [ and ] from the beginning and end of all tokens + * @return String without the [ or ] + */ + public void removeBox() + { + remove("[","]"); + } + + /** + * Removes < and > from the beginning and end of a string + * @param s String to remove from + * @return String without the < or > + */ + public static String removeAngle(String s) + { + return remove(s,"<",">"); + } + + /** + * Removes < and > from the beginning and end of all tokens + * @return String without the < or > + */ + public void removeAngle() + { + remove("<",">"); + } +} |
