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

Quick Search    Search Deep

Source code: postgresql/Connection.java


1   package postgresql;
2   
3   import java.io.*;
4   import java.net.*;
5   import java.sql.*;
6   import java.util.*;
7   import postgresql.Field;
8   import postgresql.fastpath.*;
9   import postgresql.largeobject.*;
10  import postgresql.util.*;
11  
12  /**
13   * $Id: Connection.java,v 1.1.1.1 2002/01/22 08:52:45 synmscott Exp $
14   *
15   * This abstract class is used by postgresql.Driver to open either the JDBC1 or
16   * JDBC2 versions of the Connection class.
17   *
18   */
19  public abstract class Connection
20  {
21    // This is the network stream associated with this connection
22    public PG_Stream pg_stream;
23    
24    // This is set by postgresql.Statement.setMaxRows()
25    public int maxrows = 0;    // maximum no. of rows; 0 = unlimited
26    
27    private String PG_HOST;
28    private int PG_PORT;
29    private String PG_USER;
30    private String PG_PASSWORD;
31    private String PG_DATABASE;
32    private boolean PG_STATUS;
33    
34    public boolean CONNECTION_OK = true;
35    public boolean CONNECTION_BAD = false;
36    
37    public boolean autoCommit = true;
38    public boolean readOnly = false;
39    
40    public Driver this_driver;
41    private String this_url;
42    private String cursor = null;  // The positioned update cursor name
43    
44    // These are new for v6.3, they determine the current protocol versions
45    // supported by this version of the driver. They are defined in
46    // src/include/libpq/pqcomm.h
47    protected static final int PG_PROTOCOL_LATEST_MAJOR = 2;
48    protected static final int PG_PROTOCOL_LATEST_MINOR = 0;
49    private static final int SM_DATABASE  = 64;
50    private static final int SM_USER  = 32;
51    private static final int SM_OPTIONS  = 64;
52    private static final int SM_UNUSED  = 64;
53    private static final int SM_TTY  = 64;
54    
55    private static final int AUTH_REQ_OK       = 0;
56    private static final int AUTH_REQ_KRB4     = 1;
57    private static final int AUTH_REQ_KRB5     = 2;
58    private static final int AUTH_REQ_PASSWORD = 3;
59    private static final int AUTH_REQ_CRYPT    = 4;
60    
61    // New for 6.3, salt value for crypt authorisation
62    private String salt;
63    
64    // This is used by Field to cache oid -> names.
65    // It's here, because it's shared across this connection only.
66    // Hence it cannot be static within the Field class, because it would then
67    // be across all connections, which could be to different backends.
68    public Hashtable fieldCache = new Hashtable();
69    
70    // Now handle notices as warnings, so things like "show" now work
71    public SQLWarning firstWarning = null;
72      
73      // The PID an cancellation key we get from the backend process
74      public int pid;
75      public int ckey;
76      
77      /**
78       * This is called by Class.forName() from within postgresql.Driver
79       */
80      public Connection()
81      {
82      }
83      
84      /**
85       * This method actually opens the connection. It is called by Driver.
86       *
87       * @param host the hostname of the database back end
88       * @param port the port number of the postmaster process
89       * @param info a Properties[] thing of the user and password
90       * @param database the database to connect to
91       * @param u the URL of the connection
92       * @param d the Driver instantation of the connection
93       * @return a valid connection profile
94       * @exception SQLException if a database access error occurs
95       */
96      protected void openConnection(String host, int port, Properties info, String database, String url, Driver d) throws SQLException
97      {
98      // Throw an exception if the user or password properties are missing
99      // This occasionally occurs when the client uses the properties version
100     // of getConnection(), and is a common question on the email lists
101     if(info.getProperty("user")==null)
102       throw new PSQLException("postgresql.con.user");
103     if(info.getProperty("password")==null)
104       throw new PSQLException("postgresql.con.pass");
105     
106     this_driver = d;
107     this_url = new String(url);
108     PG_DATABASE = new String(database);
109     PG_PASSWORD = new String(info.getProperty("password"));
110     PG_USER = new String(info.getProperty("user"));
111     PG_PORT = port;
112     PG_HOST = new String(host);
113     PG_STATUS = CONNECTION_BAD;
114     
115     // Now make the initial connection
116     try
117       {
118   pg_stream = new PG_Stream(host, port);
119       } catch (ConnectException cex) {
120   // Added by Peter Mount <peter@retep.org.uk>
121   // ConnectException is thrown when the connection cannot be made.
122   // we trap this an return a more meaningful message for the end user
123   throw new PSQLException ("postgresql.con.refused");
124       } catch (IOException e) {
125   throw new PSQLException ("postgresql.con.failed",e);
126       }
127       
128       // Now we need to construct and send a startup packet
129       try
130   {
131     // Ver 6.3 code
132     pg_stream.SendInteger(4+4+SM_DATABASE+SM_USER+SM_OPTIONS+SM_UNUSED+SM_TTY,4);
133     pg_stream.SendInteger(PG_PROTOCOL_LATEST_MAJOR,2);
134     pg_stream.SendInteger(PG_PROTOCOL_LATEST_MINOR,2);
135     pg_stream.Send(database.getBytes(),SM_DATABASE);
136     
137     // This last send includes the unused fields
138     pg_stream.Send(PG_USER.getBytes(),SM_USER+SM_OPTIONS+SM_UNUSED+SM_TTY);
139     
140     // now flush the startup packets to the backend
141     pg_stream.flush();
142     
143     // Now get the response from the backend, either an error message
144     // or an authentication request
145     int areq = -1; // must have a value here
146     do {
147       int beresp = pg_stream.ReceiveChar();
148       switch(beresp)
149         {
150         case 'E':
151     // An error occured, so pass the error message to the
152     // user.
153     //
154     // The most common one to be thrown here is:
155     // "User authentication failed"
156     //
157     throw new SQLException(pg_stream.ReceiveString(4096));
158     
159         case 'R':
160     // Get the type of request
161     areq = pg_stream.ReceiveIntegerR(4);
162     
163     // Get the password salt if there is one
164     if(areq == AUTH_REQ_CRYPT) {
165       byte[] rst = new byte[2];
166       rst[0] = (byte)pg_stream.ReceiveChar();
167       rst[1] = (byte)pg_stream.ReceiveChar();
168       salt = new String(rst,0,2);
169       DriverManager.println("Salt="+salt);
170     }
171     
172     // now send the auth packet
173     switch(areq)
174       {
175       case AUTH_REQ_OK:
176         break;
177         
178       case AUTH_REQ_KRB4:
179         DriverManager.println("postgresql: KRB4");
180         throw new PSQLException("postgresql.con.kerb4");
181         
182       case AUTH_REQ_KRB5:
183         DriverManager.println("postgresql: KRB5");
184         throw new PSQLException("postgresql.con.kerb5");
185         
186       case AUTH_REQ_PASSWORD:
187         DriverManager.println("postgresql: PASSWORD");
188         pg_stream.SendInteger(5+PG_PASSWORD.length(),4);
189         pg_stream.Send(PG_PASSWORD.getBytes());
190         pg_stream.SendInteger(0,1);
191         pg_stream.flush();
192         break;
193         
194       case AUTH_REQ_CRYPT:
195         DriverManager.println("postgresql: CRYPT");
196         String crypted = UnixCrypt.crypt(salt,PG_PASSWORD);
197         pg_stream.SendInteger(5+crypted.length(),4);
198         pg_stream.Send(crypted.getBytes());
199         pg_stream.SendInteger(0,1);
200         pg_stream.flush();
201         break;
202         
203       default:
204         throw new PSQLException("postgresql.con.auth",new Integer(areq));
205       }
206     break;
207     
208         default:
209     throw new PSQLException("postgresql.con.authfail");
210         }
211       } while(areq != AUTH_REQ_OK);
212     
213   } catch (IOException e) {
214     throw new PSQLException("postgresql.con.failed",e);
215   }
216   
217 
218       // As of protocol version 2.0, we should now receive the cancellation key and the pid
219       int beresp = pg_stream.ReceiveChar();
220       switch(beresp) {
221         case 'K':
222           pid = pg_stream.ReceiveInteger(4);
223           ckey = pg_stream.ReceiveInteger(4);
224           break;
225   case 'E':
226   case 'N':
227            throw new SQLException(pg_stream.ReceiveString(4096));
228         default:
229           throw new PSQLException("postgresql.con.setup");
230       }
231 
232       // Expect ReadyForQuery packet
233       beresp = pg_stream.ReceiveChar();
234       switch(beresp) {
235         case 'Z':
236      break;
237   case 'E':
238   case 'N':
239            throw new SQLException(pg_stream.ReceiveString(4096));
240         default:
241           throw new PSQLException("postgresql.con.setup");
242       }
243 
244       // Originally we issued a SHOW DATESTYLE statement to find the databases default
245       // datestyle. However, this caused some problems with timestamps, so in 6.5, we
246       // went the way of ODBC, and set the connection to ISO.
247       //
248       // This may cause some clients to break when they assume anything other than ISO,
249       // but then - they should be using the proper methods ;-)
250       //
251       //
252       firstWarning = null;
253       
254       ExecSQL("set datestyle to 'ISO'");
255       
256       // Initialise object handling
257       initObjectTypes();
258       
259       // Mark the connection as ok, and cleanup
260       firstWarning = null;
261       PG_STATUS = CONNECTION_OK;
262     }
263     
264     // These methods used to be in the main Connection implementation. As they
265     // are common to all implementations (JDBC1 or 2), they are placed here.
266     // This should make it easy to maintain the two specifications.
267     
268     /**
269      * This adds a warning to the warning chain.
270      * @param msg message to add
271      */
272     public void addWarning(String msg)
273     {
274   DriverManager.println(msg);
275   
276   // Add the warning to the chain
277   if(firstWarning!=null)
278       firstWarning.setNextWarning(new SQLWarning(msg));
279   else
280       firstWarning = new SQLWarning(msg);
281   
282   // Now check for some specific messages
283   
284   // This is obsolete in 6.5, but I've left it in here so if we need to use this
285   // technique again, we'll know where to place it.
286   //
287   // This is generated by the SQL "show datestyle"
288   //if(msg.startsWith("NOTICE:") && msg.indexOf("DateStyle")>0) {
289   //// 13 is the length off "DateStyle is "
290   //msg = msg.substring(msg.indexOf("DateStyle is ")+13);
291   //  
292   //for(int i=0;i<dateStyles.length;i+=2)
293   //if(msg.startsWith(dateStyles[i]))
294   //currentDateStyle=i+1; // this is the index of the format
295   //}
296     }
297     
298     /**
299      * Send a query to the backend.  Returns one of the ResultSet
300      * objects.
301      *
302      * <B>Note:</B> there does not seem to be any method currently
303      * in existance to return the update count.
304      *
305      * @param sql the SQL statement to be executed
306      * @return a ResultSet holding the results
307      * @exception SQLException if a database error occurs
308      */
309     public java.sql.ResultSet ExecSQL(String sql) throws SQLException
310     {
311   // added Oct 7 1998 to give us thread safety.
312   synchronized(pg_stream) {
313       
314       Field[] fields = null;
315       Vector tuples = new Vector();
316       byte[] buf = new byte[sql.length()];
317       int fqp = 0;
318       boolean hfr = false;
319       String recv_status = null, msg;
320     int update_count = 1;
321       SQLException final_error = null;
322       
323       if (sql.length() > 8192)
324     throw new PSQLException("postgresql.con.toolong",sql);
325       try
326     {
327         pg_stream.SendChar('Q');
328         buf = sql.getBytes();
329         pg_stream.Send(buf);
330         pg_stream.SendChar(0);
331         pg_stream.flush();
332     } catch (IOException e) {
333         throw new PSQLException("postgresql.con.ioerror",e);
334     }
335       
336       while (!hfr || fqp > 0)
337     {
338         Object tup=null;  // holds rows as they are recieved
339         
340         int c = pg_stream.ReceiveChar();
341         
342         switch (c)
343       {
344       case 'A':  // Asynchronous Notify
345           pid = pg_stream.ReceiveInteger(4);
346           msg = pg_stream.ReceiveString(8192);
347           break;
348       case 'B':  // Binary Data Transfer
349           if (fields == null)
350         throw new PSQLException("postgresql.con.tuple");
351           tup = pg_stream.ReceiveTuple(fields.length, true);
352           // This implements Statement.setMaxRows()
353           if(maxrows==0 || tuples.size()<maxrows)
354         tuples.addElement(tup);
355           break;
356       case 'C':  // Command Status
357           recv_status = pg_stream.ReceiveString(8192);
358         
359         // Now handle the update count correctly.
360         if(recv_status.startsWith("INSERT") || recv_status.startsWith("UPDATE")) {
361           try {
362             update_count = Integer.parseInt(recv_status.substring(1+recv_status.lastIndexOf(' ')));
363           } catch(NumberFormatException nfe) {
364             throw new PSQLException("postgresql.con.fathom",recv_status);
365           }
366         }
367           if (fields != null)
368         hfr = true;
369           else
370         {
371             try
372           {
373               pg_stream.SendChar('Q');
374               pg_stream.SendChar(' ');
375               pg_stream.SendChar(0);
376               pg_stream.flush();
377           } catch (IOException e) {
378               throw new PSQLException("postgresql.con.ioerror",e);
379           }
380             fqp++;
381         }
382           break;
383       case 'D':  // Text Data Transfer
384           if (fields == null)
385         throw new PSQLException("postgresql.con.tuple");
386           tup = pg_stream.ReceiveTuple(fields.length, false);
387           // This implements Statement.setMaxRows()
388           if(maxrows==0 || tuples.size()<maxrows)
389         tuples.addElement(tup);
390           break;
391       case 'E':  // Error Message
392           msg = pg_stream.ReceiveString(4096);
393           final_error = new SQLException(msg);
394           hfr = true;
395           break;
396       case 'I':  // Empty Query
397           int t = pg_stream.ReceiveChar();
398           
399           if (t != 0)
400         throw new PSQLException("postgresql.con.garbled");
401           if (fqp > 0)
402         fqp--;
403           if (fqp == 0)
404         hfr = true;
405           break;
406       case 'N':  // Error Notification
407           addWarning(pg_stream.ReceiveString(4096));
408           break;
409       case 'P':  // Portal Name
410           String pname = pg_stream.ReceiveString(8192);
411           break;
412       case 'T':  // MetaData Field Description
413           if (fields != null)
414         throw new PSQLException("postgresql.con.multres");
415           fields = ReceiveFields();
416           break;
417       case 'Z':       // backend ready for query, ignore for now :-)
418           break;
419       default:
420           throw new PSQLException("postgresql.con.type",new Character((char)c));
421       }
422     }
423       if (final_error != null)
424     throw final_error;
425     
426       return getResultSet(this, fields, tuples, recv_status, update_count);
427   }
428     }
429 
430     /**
431      * Receive the field descriptions from the back end
432      *
433      * @return an array of the Field object describing the fields
434      * @exception SQLException if a database error occurs
435      */
436     private Field[] ReceiveFields() throws SQLException
437     {
438   int nf = pg_stream.ReceiveIntegerR(2), i;
439   Field[] fields = new Field[nf];
440   
441   for (i = 0 ; i < nf ; ++i)
442       {
443     String typname = pg_stream.ReceiveString(8192);
444     int typid = pg_stream.ReceiveIntegerR(4);
445     int typlen = pg_stream.ReceiveIntegerR(2);
446     int typmod = pg_stream.ReceiveIntegerR(4);
447     fields[i] = new Field(this, typname, typid, typlen, typmod);
448       }
449   return fields;
450     }
451     
452     /**
453      * In SQL, a result table can be retrieved through a cursor that
454      * is named.  The current row of a result can be updated or deleted
455      * using a positioned update/delete statement that references the
456      * cursor name.
457      *
458      * We support one cursor per connection.
459      *
460      * setCursorName sets the cursor name.
461      *
462      * @param cursor the cursor name
463      * @exception SQLException if a database access error occurs
464      */
465     public void setCursorName(String cursor) throws SQLException
466     {
467   this.cursor = cursor;
468     }
469     
470     /**
471      * getCursorName gets the cursor name.
472      *
473      * @return the current cursor name
474      * @exception SQLException if a database access error occurs
475      */
476     public String getCursorName() throws SQLException
477     {
478   return cursor;
479     }
480     
481     /**
482      * We are required to bring back certain information by
483      * the DatabaseMetaData class.  These functions do that.
484      *
485      * Method getURL() brings back the URL (good job we saved it)
486      *
487      * @return the url
488      * @exception SQLException just in case...
489      */
490     public String getURL() throws SQLException
491     {
492   return this_url;
493     }
494     
495     /**
496      * Method getUserName() brings back the User Name (again, we
497      * saved it)
498      *
499      * @return the user name
500      * @exception SQLException just in case...
501      */
502     public String getUserName() throws SQLException
503     {
504   return PG_USER;
505     }
506     
507     /**
508      * This returns the Fastpath API for the current connection.
509      *
510      * <p><b>NOTE:</b> This is not part of JDBC, but allows access to
511      * functions on the postgresql backend itself.
512      *
513      * <p>It is primarily used by the LargeObject API
514      *
515      * <p>The best way to use this is as follows:
516      *
517      * <p><pre>
518      * import postgresql.fastpath.*;
519      * ...
520      * Fastpath fp = ((postgresql.Connection)myconn).getFastpathAPI();
521      * </pre>
522      *
523      * <p>where myconn is an open Connection to postgresql.
524      *
525      * @return Fastpath object allowing access to functions on the postgresql
526      * backend.
527      * @exception SQLException by Fastpath when initialising for first time
528      */
529     public Fastpath getFastpathAPI() throws SQLException
530     {
531   if(fastpath==null)
532       fastpath = new Fastpath(this,pg_stream);
533   return fastpath;
534     }
535     
536     // This holds a reference to the Fastpath API if already open
537     private Fastpath fastpath = null;
538     
539     /**
540      * This returns the LargeObject API for the current connection.
541      *
542      * <p><b>NOTE:</b> This is not part of JDBC, but allows access to
543      * functions on the postgresql backend itself.
544      *
545      * <p>The best way to use this is as follows:
546      *
547      * <p><pre>
548      * import postgresql.largeobject.*;
549      * ...
550      * LargeObjectManager lo = ((postgresql.Connection)myconn).getLargeObjectAPI();
551      * </pre>
552      *
553      * <p>where myconn is an open Connection to postgresql.
554      *
555      * @return LargeObject object that implements the API
556      * @exception SQLException by LargeObject when initialising for first time
557      */
558     public LargeObjectManager getLargeObjectAPI() throws SQLException
559     {
560   if(largeobject==null)
561       largeobject = new LargeObjectManager(this);
562   return largeobject;
563     }
564     
565     // This holds a reference to the LargeObject API if already open
566     private LargeObjectManager largeobject = null;
567     
568     /**
569      * This method is used internally to return an object based around
570      * postgresql's more unique data types.
571      *
572      * <p>It uses an internal Hashtable to get the handling class. If the
573      * type is not supported, then an instance of postgresql.util.PGobject
574      * is returned.
575      *
576      * You can use the getValue() or setValue() methods to handle the returned
577      * object. Custom objects can have their own methods.
578      *
579      * In 6.4, this is extended to use the postgresql.util.Serialize class to
580      * allow the Serialization of Java Objects into the database without using
581      * Blobs. Refer to that class for details on how this new feature works.
582      *
583      * @return PGobject for this type, and set to value
584      * @exception SQLException if value is not correct for this type
585      * @see postgresql.util.Serialize
586      */
587     public Object getObject(String type,String value) throws SQLException
588     {
589   try {
590       Object o = objectTypes.get(type);
591       
592       // If o is null, then the type is unknown, so check to see if type
593       // is an actual table name. If it does, see if a Class is known that
594       // can handle it
595       if(o == null) {
596     Serialize ser = new Serialize(this,type);
597     objectTypes.put(type,ser);
598     return ser.fetch(Integer.parseInt(value));
599       }
600       
601       // If o is not null, and it is a String, then its a class name that
602       // extends PGobject.
603       //
604       // This is used to implement the postgresql unique types (like lseg,
605       // point, etc).
606       if(o instanceof String) {
607     // 6.3 style extending PG_Object
608     PGobject obj = null;
609     obj = (PGobject)(Class.forName((String)o).newInstance());
610     obj.setType(type);
611     obj.setValue(value);
612     return (Object)obj;
613       } else {
614     // If it's an object, it should be an instance of our Serialize class
615     // If so, then call it's fetch method.
616     if(o instanceof Serialize)
617         return ((Serialize)o).fetch(Integer.parseInt(value));
618       }
619   } catch(SQLException sx) {
620       // rethrow the exception. Done because we capture any others next
621       sx.fillInStackTrace();
622       throw sx;
623   } catch(Exception ex) {
624       throw new PSQLException("postgresql.con.creobj",type,ex);
625   }
626   
627   // should never be reached
628   return null;
629     }
630     
631     /**
632      * This stores an object into the database.
633      * @param o Object to store
634      * @return OID of the new rectord
635      * @exception SQLException if value is not correct for this type
636      * @see postgresql.util.Serialize
637      */
638     public int putObject(Object o) throws SQLException
639     {
640   try {
641       String type = o.getClass().getName();
642       Object x = objectTypes.get(type);
643       
644       // If x is null, then the type is unknown, so check to see if type
645       // is an actual table name. If it does, see if a Class is known that
646       // can handle it
647       if(x == null) {
648     Serialize ser = new Serialize(this,type);
649     objectTypes.put(type,ser);
650     return ser.store(o);
651       }
652       
653       // If it's an object, it should be an instance of our Serialize class
654       // If so, then call it's fetch method.
655       if(x instanceof Serialize)
656     return ((Serialize)x).store(o);
657       
658       // Thow an exception because the type is unknown
659       throw new PSQLException("postgresql.con.strobj");
660       
661   } catch(SQLException sx) {
662       // rethrow the exception. Done because we capture any others next
663       sx.fillInStackTrace();
664       throw sx;
665   } catch(Exception ex) {
666       throw new PSQLException("postgresql.con.strobjex",ex);
667   }
668     }
669     
670     /**
671      * This allows client code to add a handler for one of postgresql's
672      * more unique data types.
673      *
674      * <p><b>NOTE:</b> This is not part of JDBC, but an extension.
675      *
676      * <p>The best way to use this is as follows:
677      *
678      * <p><pre>
679      * ...
680      * ((postgresql.Connection)myconn).addDataType("mytype","my.class.name");
681      * ...
682      * </pre>
683      *
684      * <p>where myconn is an open Connection to postgresql.
685      *
686      * <p>The handling class must extend postgresql.util.PGobject
687      *
688      * @see postgresql.util.PGobject
689      */
690     public void addDataType(String type,String name)
691     {
692   objectTypes.put(type,name);
693     }
694     
695     // This holds the available types
696     private Hashtable objectTypes = new Hashtable();
697     
698     // This array contains the types that are supported as standard.
699     //
700     // The first entry is the types name on the database, the second
701     // the full class name of the handling class.
702     //
703     private static final String defaultObjectTypes[][] = {
704   {"box",    "postgresql.geometric.PGbox"},
705   {"circle",  "postgresql.geometric.PGcircle"},
706   {"line",  "postgresql.geometric.PGline"},
707   {"lseg",  "postgresql.geometric.PGlseg"},
708   {"path",  "postgresql.geometric.PGpath"},
709   {"point",  "postgresql.geometric.PGpoint"},
710   {"polygon",  "postgresql.geometric.PGpolygon"},
711   {"money",  "postgresql.util.PGmoney"}
712     };
713     
714     // This initialises the objectTypes hashtable
715     private void initObjectTypes()
716     {
717   for(int i=0;i<defaultObjectTypes.length;i++)
718       objectTypes.put(defaultObjectTypes[i][0],defaultObjectTypes[i][1]);
719     }
720     
721     // These are required by other common classes
722     public abstract java.sql.Statement createStatement() throws SQLException;
723     
724     /**
725      * This returns a resultset. It must be overridden, so that the correct
726      * version (from jdbc1 or jdbc2) are returned.
727      */
728     protected abstract java.sql.ResultSet getResultSet(postgresql.Connection conn, Field[] fields, Vector tuples, String status, int updateCount) throws SQLException;
729     
730     public abstract void close() throws SQLException;
731   
732     /**
733      * Overides finalize(). If called, it closes the connection.
734      *
735      * This was done at the request of Rachel Greenham
736      * <rachel@enlarion.demon.co.uk> who hit a problem where multiple
737      * clients didn't close the connection, and once a fortnight enough
738      * clients were open to kill the postgres server.
739      */
740     public void finalize() throws Throwable
741     {
742   close();
743     }
744 }