Source code: org/postgresql/Driver.java
1 package org.postgresql;
2
3 import java.sql.*;
4 import java.util.*;
5
6 import org.postgresql.util.PSQLException;
7
8 /**
9 * The Java SQL framework allows for multiple database drivers. Each
10 * driver should supply a class that implements the Driver interface
11 *
12 * <p>The DriverManager will try to load as many drivers as it can find and
13 * then for any given connection request, it will ask each driver in turn
14 * to try to connect to the target URL.
15 *
16 * <p>It is strongly recommended that each Driver class should be small and
17 * standalone so that the Driver class can be loaded and queried without
18 * bringing in vast quantities of supporting code.
19 *
20 * <p>When a Driver class is loaded, it should create an instance of itself
21 * and register it with the DriverManager. This means that a user can load
22 * and register a driver by doing Class.forName("foo.bah.Driver")
23 *
24 * @see org.postgresql.Connection
25 * @see java.sql.Driver
26 */
27 public class Driver implements java.sql.Driver
28 {
29 // These should be in sync with the backend that the driver was
30 // distributed with
31 static final int MAJORVERSION = 7;
32 static final int MINORVERSION = 0;
33
34 static
35 {
36 try {
37 // moved the registerDriver from the constructor to here
38 // because some clients call the driver themselves (I know, as
39 // my early jdbc work did - and that was based on other examples).
40 // Placing it here, means that the driver is registered once only.
41 java.sql.DriverManager.registerDriver(new Driver());
42 } catch (SQLException e) {
43 e.printStackTrace();
44 }
45 }
46
47 /**
48 * Construct a new driver and register it with DriverManager
49 *
50 * @exception SQLException for who knows what!
51 */
52 public Driver() throws SQLException
53 {
54 // Set the connectClass variable so that future calls will handle the correct
55 // base class
56 //if(System.getProperty("java.version").startsWith("1.1")) {
57 //connectClass = "postgresql.jdbc1.Connection";
58 //} else {
59 //connectClass = "postgresql.jdbc2.Connection";
60 //}
61
62 // Ok, when the above code was introduced in 6.5 it's intention was to allow
63 // the driver to automatically detect which version of JDBC was being used
64 // and to detect the version of the JVM accordingly.
65 //
66 // It did this by using the java.version parameter.
67 //
68 // However, it was quickly discovered that not all JVM's returned an easily
69 // parseable version number (ie "1.2") and some don't return a value at all.
70 // The latter came from a discussion on the advanced java list.
71 //
72 // So, to solve this, I've moved the decision out of the driver, and it's now
73 // a compile time parameter.
74 //
75 // For this to work, the Makefile creates a pseudo class which contains the class
76 // name that will actually make the connection.
77 }
78
79 /**
80 * Try to make a database connection to the given URL. The driver
81 * should return "null" if it realizes it is the wrong kind of
82 * driver to connect to the given URL. This will be common, as
83 * when the JDBC driverManager is asked to connect to a given URL,
84 * it passes the URL to each loaded driver in turn.
85 *
86 * <p>The driver should raise an SQLException if it is the right driver
87 * to connect to the given URL, but has trouble connecting to the
88 * database.
89 *
90 * <p>The java.util.Properties argument can be used to pass arbitrary
91 * string tag/value pairs as connection arguments. Normally, at least
92 * "user" and "password" properties should be included in the
93 * properties.
94 *
95 * Our protocol takes the forms:
96 * <PRE>
97 * jdbc:org.postgresql://host:port/database?param1=val1&...
98 * </PRE>
99 *
100 * @param url the URL of the database to connect to
101 * @param info a list of arbitrary tag/value pairs as connection
102 * arguments
103 * @return a connection to the URL or null if it isnt us
104 * @exception SQLException if a database access error occurs
105 * @see java.sql.Driver#connect
106 */
107 public java.sql.Connection connect(String url, Properties info) throws SQLException
108 {
109 if((props = parseURL(url,info))==null)
110 return null;
111
112 DriverManager.println("Using "+DriverClass.connectClass);
113
114 try {
115 org.postgresql.Connection con = (org.postgresql.Connection)(Class.forName(DriverClass.connectClass).newInstance());
116 con.openConnection (host(), port(), props, database(), url, this);
117 return (java.sql.Connection)con;
118 } catch(ClassNotFoundException ex) {
119 throw new PSQLException("postgresql.jvm.version",ex);
120 } catch(PSQLException ex1) {
121 // re-throw the exception, otherwise it will be caught next, and a
122 // org.postgresql.unusual error will be returned instead.
123 throw ex1;
124 } catch(Exception ex2) {
125 throw new PSQLException("postgresql.unusual",ex2);
126 }
127 }
128
129 /**
130 * Returns true if the driver thinks it can open a connection to the
131 * given URL. Typically, drivers will return true if they understand
132 * the subprotocol specified in the URL and false if they don't. Our
133 * protocols start with jdbc:org.postgresql:
134 *
135 * @see java.sql.Driver#acceptsURL
136 * @param url the URL of the driver
137 * @return true if this driver accepts the given URL
138 * @exception SQLException if a database-access error occurs
139 * (Dont know why it would *shrug*)
140 */
141 public boolean acceptsURL(String url) throws SQLException
142 {
143 if(parseURL(url,null)==null)
144 return false;
145 return true;
146 }
147
148 /**
149 * The getPropertyInfo method is intended to allow a generic GUI
150 * tool to discover what properties it should prompt a human for
151 * in order to get enough information to connect to a database.
152 *
153 * <p>Note that depending on the values the human has supplied so
154 * far, additional values may become necessary, so it may be necessary
155 * to iterate through several calls to getPropertyInfo
156 *
157 * @param url the Url of the database to connect to
158 * @param info a proposed list of tag/value pairs that will be sent on
159 * connect open.
160 * @return An array of DriverPropertyInfo objects describing
161 * possible properties. This array may be an empty array if
162 * no properties are required
163 * @exception SQLException if a database-access error occurs
164 * @see java.sql.Driver#getPropertyInfo
165 */
166 public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException
167 {
168 Properties p = parseURL(url,info);
169
170 // naughty, but its best for speed. If anyone adds a property here, then
171 // this _MUST_ be increased to accomodate them.
172 DriverPropertyInfo d,dpi[] = new DriverPropertyInfo[0];
173 //int i=0;
174
175 //dpi[i++] = d = new DriverPropertyInfo("auth",p.getProperty("auth","default"));
176 //d.description = "determines if password authentication is used";
177 //d.choices = new String[4];
178 //d.choices[0]="default"; // Get value from org.postgresql.auth property, defaults to trust
179 //d.choices[1]="trust"; // No password authentication
180 //d.choices[2]="password"; // Password authentication
181 //d.choices[3]="ident"; // Ident (RFC 1413) protocol
182
183 return dpi;
184 }
185
186 /**
187 * Gets the drivers major version number
188 *
189 * @return the drivers major version number
190 */
191 public int getMajorVersion()
192 {
193 return MAJORVERSION;
194 }
195
196 /**
197 * Get the drivers minor version number
198 *
199 * @return the drivers minor version number
200 */
201 public int getMinorVersion()
202 {
203 return MINORVERSION;
204 }
205
206 /**
207 * Report whether the driver is a genuine JDBC compliant driver. A
208 * driver may only report "true" here if it passes the JDBC compliance
209 * tests, otherwise it is required to return false. JDBC compliance
210 * requires full support for the JDBC API and full support for SQL 92
211 * Entry Level.
212 *
213 * <p>For PostgreSQL, this is not yet possible, as we are not SQL92
214 * compliant (yet).
215 */
216 public boolean jdbcCompliant()
217 {
218 return false;
219 }
220
221 private Properties props;
222
223 static private String[] protocols = { "jdbc","postgresql" };
224
225 /**
226 * Constructs a new DriverURL, splitting the specified URL into its
227 * component parts
228 * @param url JDBC URL to parse
229 * @param defaults Default properties
230 * @return Properties with elements added from the url
231 * @exception SQLException
232 */
233 Properties parseURL(String url,Properties defaults) throws SQLException
234 {
235 int state = -1;
236 Properties urlProps = new Properties(defaults);
237 String key = new String();
238 String value = new String();
239
240 StringTokenizer st = new StringTokenizer(url, ":/;=&?", true);
241 for (int count = 0; (st.hasMoreTokens()); count++) {
242 String token = st.nextToken();
243
244 // PM June 29 1997
245 // Added this, to help me understand how this works.
246 // Unless you want each token to be processed, leave this commented out
247 // but don't delete it.
248 //DriverManager.println("wellFormedURL: state="+state+" count="+count+" token='"+token+"'");
249
250 // PM Aug 2 1997 - Modified to allow multiple backends
251 if (count <= 3) {
252 if ((count % 2) == 1 && token.equals(":"))
253 ;
254 else if((count % 2) == 0) {
255 boolean found=(count==0)?true:false;
256 for(int tmp=0;tmp<protocols.length;tmp++) {
257 if(token.equals(protocols[tmp])) {
258 // PM June 29 1997 Added this property to enable the driver
259 // to handle multiple backend protocols.
260 if(count == 2 && tmp > 0) {
261 urlProps.put("Protocol",token);
262 found=true;
263 }
264 }
265 }
266
267 if(found == false)
268 return null;
269 } else return null;
270 }
271 else if (count > 3) {
272 if (count == 4 && token.equals("/")) state = 0;
273 else if (count == 4) {
274 urlProps.put("PGDBNAME", token);
275 state = -2;
276 }
277 else if (count == 5 && state == 0 && token.equals("/"))
278 state = 1;
279 else if (count == 5 && state == 0)
280 return null;
281 else if (count == 6 && state == 1)
282 urlProps.put("PGHOST", token);
283 else if (count == 7 && token.equals(":")) state = 2;
284 else if (count == 8 && state == 2) {
285 try {
286 Integer portNumber = Integer.decode(token);
287 urlProps.put("PGPORT", portNumber.toString());
288 } catch (Exception e) {
289 return null;
290 }
291 }
292 else if ((count == 7 || count == 9) &&
293 (state == 1 || state == 2) && token.equals("/"))
294 state = -1;
295 else if (state == -1) {
296 urlProps.put("PGDBNAME", token);
297 state = -2;
298 }
299 else if (state <= -2 && (count % 2) == 1) {
300 // PM Aug 2 1997 - added tests for ? and &
301 if (token.equals(";") || token.equals("?") || token.equals("&") ) state = -3;
302 else if (token.equals("=")) state = -5;
303 }
304 else if (state <= -2 && (count % 2) == 0) {
305 if (state == -3) key = token;
306 else if (state == -5) {
307 value = token;
308 //DriverManager.println("put("+key+","+value+")");
309 urlProps.put(key, value);
310 state = -2;
311 }
312 }
313 }
314 }
315
316 // PM June 29 1997
317 // This now outputs the properties only if we are logging
318 // PM Sep 13 1999 Commented out, as it throws a Deprecation warning
319 // when compiled under JDK1.2.
320 //if(DriverManager.getLogStream() != null)
321 // urlProps.list(DriverManager.getLogStream());
322
323 return urlProps;
324
325 }
326
327 /**
328 * @return the hostname portion of the URL
329 */
330 public String host()
331 {
332 return props.getProperty("PGHOST","localhost");
333 }
334
335 /**
336 * @return the port number portion of the URL or -1 if no port was specified
337 */
338 public int port()
339 {
340 return Integer.parseInt(props.getProperty("PGPORT","5432"));
341 }
342
343 /**
344 * @return the database name of the URL
345 */
346 public String database()
347 {
348 return props.getProperty("PGDBNAME");
349 }
350
351 /**
352 * @return the value of any property specified in the URL or properties
353 * passed to connect(), or null if not found.
354 */
355 public String property(String name)
356 {
357 return props.getProperty(name);
358 }
359
360 /**
361 * This method was added in v6.5, and simply throws an SQLException
362 * for an unimplemented method. I decided to do it this way while
363 * implementing the JDBC2 extensions to JDBC, as it should help keep the
364 * overall driver size down.
365 */
366 public static SQLException notImplemented()
367 {
368 return new PSQLException("postgresql.unimplemented");
369 }
370 }
371