diff options
author | Peter Mount <peter@retep.org.uk> | 2000-04-17 20:07:56 +0000 |
---|---|---|
committer | Peter Mount <peter@retep.org.uk> | 2000-04-17 20:07:56 +0000 |
commit | 25dadc85140b1f688a4cf0c2de8b961a07b10f11 (patch) | |
tree | 14c91b7e37ca13fba1a4880687cf02bd2dad2de2 /src/interfaces/jdbc/org/postgresql/fastpath | |
parent | aafff4af162dcf2be91fcd67db6881d6cf166565 (diff) |
Another attempt at 7.0
Diffstat (limited to 'src/interfaces/jdbc/org/postgresql/fastpath')
-rw-r--r-- | src/interfaces/jdbc/org/postgresql/fastpath/Fastpath.java | 303 | ||||
-rw-r--r-- | src/interfaces/jdbc/org/postgresql/fastpath/FastpathArg.java | 106 |
2 files changed, 409 insertions, 0 deletions
diff --git a/src/interfaces/jdbc/org/postgresql/fastpath/Fastpath.java b/src/interfaces/jdbc/org/postgresql/fastpath/Fastpath.java new file mode 100644 index 00000000000..9e9b07f89be --- /dev/null +++ b/src/interfaces/jdbc/org/postgresql/fastpath/Fastpath.java @@ -0,0 +1,303 @@ +package org.postgresql.fastpath; + +import java.io.*; +import java.lang.*; +import java.net.*; +import java.util.*; +import java.sql.*; +import org.postgresql.util.*; + +// Important: There are a lot of debug code commented out. Please do not +// delete these. + +/** + * This class implements the Fastpath api. + * + * <p>This is a means of executing functions imbeded in the org.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 org.postgresql.FastpathFastpathArg + * @see org.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 org.postgresql.Connection conn; // our connection + protected org.postgresql.PG_Stream stream; // the network stream + + /** + * Initialises the fastpath system + * + * <p><b>Important Notice</b> + * <br>This is called from org.postgresql.Connection, and should not be called + * from client code. + * + * @param conn org.postgresql.Connection to attach to + * @param stream The network stream to the backend + */ + public Fastpath(org.postgresql.Connection conn,org.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 + { + // added Oct 7 1998 to give us thread safety + synchronized(stream) { + + // 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); + stream.SendInteger(fnid,4); + stream.SendInteger(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 PSQLException("postgresql.fp.send",new Integer(fnid),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.ReceiveIntegerR(4); + //DriverManager.println("G: size="+sz); //debug + + // Return an Integer if + if(resulttype) + result = new Integer(stream.ReceiveIntegerR(sz)); + else { + byte buf[] = new byte[sz]; + stream.Receive(buf,0,sz); + result = buf; + } + break; + + //------------------------------ + // Error message returned + case 'E': + throw new PSQLException("postgresql.fp.error",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; + + case 'Z': + break; + + default: + throw new PSQLException("postgresql.fp.protocol",new Character((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 org.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 org.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 PSQLException("postgresql.fp.expint",name); + 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 org.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 org.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 PSQLException("postgresql.fp.unknown",name); + + return id.intValue(); + } +} + diff --git a/src/interfaces/jdbc/org/postgresql/fastpath/FastpathArg.java b/src/interfaces/jdbc/org/postgresql/fastpath/FastpathArg.java new file mode 100644 index 00000000000..87b8475f645 --- /dev/null +++ b/src/interfaces/jdbc/org/postgresql/fastpath/FastpathArg.java @@ -0,0 +1,106 @@ +package org.postgresql.fastpath; + +import java.io.*; +import java.lang.*; +import java.net.*; +import java.util.*; +import java.sql.*; +import org.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 org.postgresql.largeobject + * package + * + * @see org.postgresql.fastpath.Fastpath + * @see org.postgresql.largeobject.LargeObjectManager + * @see org.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(org.postgresql.PG_Stream s) throws IOException + { + if(type) { + // argument is an integer + s.SendInteger(4,4); // size of an integer + s.SendInteger(value,4); // integer value of argument + } else { + // argument is a byte array + s.SendInteger(bytes.length,4); // size of array + s.Send(bytes); + } + } +} + |