|
| 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 | + |
0 commit comments