Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/postgresql/fastpath/Fastpath.java


1   package org.postgresql.fastpath;
2   
3   import java.io.*;
4   import java.lang.*;
5   import java.net.*;
6   import java.util.*;
7   import java.sql.*;
8   import org.postgresql.util.*;
9   
10  // Important: There are a lot of debug code commented out. Please do not
11  // delete these.
12  
13  /**
14   * This class implements the Fastpath api.
15   *
16   * <p>This is a means of executing functions imbeded in the org.postgresql backend
17   * from within a java application.
18   *
19   * <p>It is based around the file src/interfaces/libpq/fe-exec.c
20   *
21   *
22   * <p><b>Implementation notes:</b>
23   *
24   * <p><b><em>Network protocol:</em></b>
25   *
26   * <p>The code within the backend reads integers in reverse.
27   *
28   * <p>There is work in progress to convert all of the protocol to
29   * network order but it may not be there for v6.3
30   *
31   * <p>When fastpath switches, simply replace SendIntegerReverse() with
32   * SendInteger()
33   *
34   * @see org.postgresql.FastpathFastpathArg
35   * @see org.postgresql.LargeObject
36   */
37  public class Fastpath
38  {
39    // This maps the functions names to their id's (possible unique just
40    // to a connection).
41    protected Hashtable func = new Hashtable();
42    
43    protected org.postgresql.Connection conn;    // our connection
44    protected org.postgresql.PG_Stream stream;  // the network stream
45    
46    /**
47     * Initialises the fastpath system
48     *
49     * <p><b>Important Notice</b>
50     * <br>This is called from org.postgresql.Connection, and should not be called
51     * from client code.
52     *
53     * @param conn org.postgresql.Connection to attach to
54     * @param stream The network stream to the backend
55     */
56    public Fastpath(org.postgresql.Connection conn,org.postgresql.PG_Stream stream)
57    {
58      this.conn=conn;
59      this.stream=stream;
60      //DriverManager.println("Fastpath initialised");
61    }
62    
63    /**
64     * Send a function call to the PostgreSQL backend
65     *
66     * @param fnid Function id
67     * @param resulttype True if the result is an integer, false for other results
68     * @param args FastpathArguments to pass to fastpath
69     * @return null if no data, Integer if an integer result, or byte[] otherwise
70     * @exception SQLException if a database-access error occurs.
71     */
72    public Object fastpath(int fnid,boolean resulttype,FastpathArg[] args) throws SQLException
73    {
74      // added Oct 7 1998 to give us thread safety
75      synchronized(stream) {
76        
77      // send the function call
78      try {
79        // 70 is 'F' in ASCII. Note: don't use SendChar() here as it adds padding
80        // that confuses the backend. The 0 terminates the command line.
81        stream.SendInteger(70,1);
82        stream.SendInteger(0,1);
83        
84        //stream.SendIntegerReverse(fnid,4);
85        //stream.SendIntegerReverse(args.length,4);
86        stream.SendInteger(fnid,4);
87        stream.SendInteger(args.length,4);
88        
89        for(int i=0;i<args.length;i++)
90    args[i].send(stream);
91        
92        // This is needed, otherwise data can be lost
93        stream.flush();
94        
95      } catch(IOException ioe) {
96        throw new PSQLException("postgresql.fp.send",new Integer(fnid),ioe);
97      }
98      
99      // Now handle the result
100     
101     // We should get 'V' on sucess or 'E' on error. Anything else is treated
102     // as an error.
103     //int in = stream.ReceiveChar();
104     //DriverManager.println("ReceiveChar() = "+in+" '"+((char)in)+"'");
105     //if(in!='V') {
106     //if(in=='E')
107     //throw new SQLException(stream.ReceiveString(4096));
108     //throw new SQLException("Fastpath: expected 'V' from backend, got "+((char)in));
109     //}
110     
111     // Now loop, reading the results
112     Object result = null; // our result
113     while(true) {
114       int in = stream.ReceiveChar();
115       //DriverManager.println("ReceiveChar() = "+in+" '"+((char)in)+"'");
116       switch(in)
117   {
118   case 'V':
119     break;
120     
121     //------------------------------
122     // Function returned properly
123     //
124   case 'G':
125     int sz = stream.ReceiveIntegerR(4);
126     //DriverManager.println("G: size="+sz);  //debug
127     
128     // Return an Integer if
129     if(resulttype)
130       result = new Integer(stream.ReceiveIntegerR(sz));
131     else {
132       byte buf[] = new byte[sz];
133       stream.Receive(buf,0,sz);
134       result = buf;
135     }
136     break;
137     
138     //------------------------------
139     // Error message returned
140   case 'E':
141     throw new PSQLException("postgresql.fp.error",stream.ReceiveString(4096));
142     
143     //------------------------------
144     // Notice from backend
145   case 'N':
146     conn.addWarning(stream.ReceiveString(4096));
147     break;
148     
149     //------------------------------
150     // End of results
151     //
152     // Here we simply return res, which would contain the result
153     // processed earlier. If no result, this already contains null
154   case '0':
155     //DriverManager.println("returning "+result);
156     return result;
157     
158   case 'Z':
159       break;
160       
161   default:
162     throw new PSQLException("postgresql.fp.protocol",new Character((char)in));
163   }
164     }
165     }
166   }
167   
168   /**
169    * Send a function call to the PostgreSQL backend by name.
170    *
171    * Note: the mapping for the procedure name to function id needs to exist,
172    * usually to an earlier call to addfunction().
173    *
174    * This is the prefered method to call, as function id's can/may change
175    * between versions of the backend.
176    *
177    * For an example of how this works, refer to org.postgresql.LargeObject
178    *
179    * @param name Function name
180    * @param resulttype True if the result is an integer, false for other
181    * results
182    * @param args FastpathArguments to pass to fastpath
183    * @return null if no data, Integer if an integer result, or byte[] otherwise
184    * @exception SQLException if name is unknown or if a database-access error
185    * occurs.
186    * @see org.postgresql.LargeObject
187    */
188   public Object fastpath(String name,boolean resulttype,FastpathArg[] args) throws SQLException
189   {
190     //DriverManager.println("Fastpath: calling "+name);
191     return fastpath(getID(name),resulttype,args);
192   }
193   
194   /**
195    * This convenience method assumes that the return value is an Integer
196    * @param name Function name
197    * @param args Function arguments
198    * @return integer result
199    * @exception SQLException if a database-access error occurs or no result
200    */
201   public int getInteger(String name,FastpathArg[] args) throws SQLException
202   {
203     Integer i = (Integer)fastpath(name,true,args);
204     if(i==null)
205       throw new PSQLException("postgresql.fp.expint",name);
206     return i.intValue();
207   }
208   
209   /**
210    * This convenience method assumes that the return value is an Integer
211    * @param name Function name
212    * @param args Function arguments
213    * @return byte[] array containing result
214    * @exception SQLException if a database-access error occurs or no result
215    */
216   public byte[] getData(String name,FastpathArg[] args) throws SQLException
217   {
218     return (byte[])fastpath(name,false,args);
219   }
220   
221   /**
222    * This adds a function to our lookup table.
223    *
224    * <p>User code should use the addFunctions method, which is based upon a
225    * query, rather than hard coding the oid. The oid for a function is not
226    * guaranteed to remain static, even on different servers of the same
227    * version.
228    *
229    * @param name Function name
230    * @param fnid Function id
231    */
232   public void addFunction(String name,int fnid)
233   {
234     func.put(name,new Integer(fnid));
235   }
236   
237   /**
238    * This takes a ResultSet containing two columns. Column 1 contains the
239    * function name, Column 2 the oid.
240    *
241    * <p>It reads the entire ResultSet, loading the values into the function
242    * table.
243    *
244    * <p><b>REMEMBER</b> to close() the resultset after calling this!!
245    *
246    * <p><b><em>Implementation note about function name lookups:</em></b>
247    *
248    * <p>PostgreSQL stores the function id's and their corresponding names in
249    * the pg_proc table. To speed things up locally, instead of querying each
250    * function from that table when required, a Hashtable is used. Also, only
251    * the function's required are entered into this table, keeping connection
252    * times as fast as possible.
253    *
254    * <p>The org.postgresql.LargeObject class performs a query upon it's startup,
255    * and passes the returned ResultSet to the addFunctions() method here.
256    *
257    * <p>Once this has been done, the LargeObject api refers to the functions by
258    * name.
259    *
260    * <p>Dont think that manually converting them to the oid's will work. Ok,
261    * they will for now, but they can change during development (there was some
262    * discussion about this for V7.0), so this is implemented to prevent any
263    * unwarranted headaches in the future.
264    *
265    * @param rs ResultSet
266    * @exception SQLException if a database-access error occurs.
267    * @see org.postgresql.LargeObjectManager
268    */
269   public void addFunctions(ResultSet rs) throws SQLException
270   {
271     while(rs.next()) {
272       func.put(rs.getString(1),new Integer(rs.getInt(2)));
273     }
274   }
275   
276   /**
277    * This returns the function id associated by its name
278    *
279    * <p>If addFunction() or addFunctions() have not been called for this name,
280    * then an SQLException is thrown.
281    *
282    * @param name Function name to lookup
283    * @return Function ID for fastpath call
284    * @exception SQLException is function is unknown.
285    */
286   public int getID(String name) throws SQLException
287   {
288     Integer id = (Integer)func.get(name);
289     
290     // may be we could add a lookup to the database here, and store the result
291     // in our lookup table, throwing the exception if that fails.
292     // We must, however, ensure that if we do, any existing ResultSet is
293     // unaffected, otherwise we could break user code.
294     //
295     // so, until we know we can do this (needs testing, on the TODO list)
296     // for now, we throw the exception and do no lookups.
297     if(id==null)
298       throw new PSQLException("postgresql.fp.unknown",name);
299     
300     return id.intValue();
301   }
302 }
303