changeset 144:d6abd1ffffbb

Use SQLNonTransientConnectionException instead of SQLException where applicable (SQLState starts with 08) Use SQLDataException instead of SQLException where applicable (SQLState starts with 22) Also updated documentation file: SQLSTATEs which documents the MonetDB JDBC driver specific SQLSTATEs
author Martin van Dinther <martin.van.dinther@monetdbsolutions.com>
date Thu, 03 Aug 2017 18:53:04 +0200 (2017-08-03)
parents dcf7879d4862
children b18cfb312330
files SQLSTATEs src/main/java/nl/cwi/monetdb/jdbc/MonetConnection.java src/main/java/nl/cwi/monetdb/jdbc/MonetDriver.java.in src/main/java/nl/cwi/monetdb/jdbc/MonetPreparedStatement.java src/main/java/nl/cwi/monetdb/jdbc/MonetResultSet.java src/main/java/nl/cwi/monetdb/jdbc/types/INET.java src/main/java/nl/cwi/monetdb/jdbc/types/URL.java
diffstat 7 files changed, 151 insertions(+), 125 deletions(-) [+]
line wrap: on
line diff
--- a/SQLSTATEs
+++ b/SQLSTATEs
@@ -1,56 +1,69 @@
-08M01 opening logfile failed
+Following SQLStates are MonetDB JDBC driver specific:
 01M02 redirect warning
-M0M03 illegal arguments (invalid call of internal function)
 01M03 illegal arguments (invalid call of internal function)
-M0M04 only supported in SQL mode
-M1M05 invalid argument (user supplied)
-M0M06 savepoint is not MonetSavepoint
 01M07 unrecognised clientinfo property
 01M08 read-only conection mode not supported
 01M09 transaction mode not supported
-M0M10 protocol violation/unexpected server response
 01M10 unexpected server output
 01M11 server-client autocommit state mismatch
-M0M12 matching client handle referenced by server not found
 01M13 concurrency mode not supported
 01M14 scrolling mode not supported
 01M15 holdability mode not supported
-M1M16 multistatements not supported in batches
-M1M17 result set not expected for DML or DDL-statement
 01M18 generated keys for columns not supported
-M1M19 response is not a result set
-M1M20 object closed
 01M21 cursors not supported
-0AM21 cursors not supported
 01M22 JDBC escape syntax not supported
 01M23 field size limitation not supported
 01M24 query time out not supported
-M1M25 failed reading from/writing to object stream
+
+08M01 opening logfile failed
 08M26 invalid URI
-M0M27 unknown error
+08M33 connection timed out
+
+0AM21 cursors not supported
+0AM34 Java generics not supported
+
 22M28 invalid BLOB format
-M0M29 assert
+22M29 invalid inet format
+22M30 invalid url format
+
+2BM37 dependent objects still exist
+2DM30 autocommit mode active
 3BM30 autocommit mode active
-2DM30 autocommit mode active
+
 42M31 user/role already exists
 42M32 user/role not found
-08M33 connection timeout
-0AM34 Java generics not supported
 42M35 sequence not found
 42M36 cannot restart sequence with NULL
-2BM37 dependent objects still exist
+
+M0M03 illegal arguments (invalid call of internal function)
+M0M04 only supported in SQL mode
+M0M06 savepoint is not MonetSavepoint
+M0M10 protocol violation/unexpected server response
+M0M12 matching client handle referenced by server not found
+M0M27 unknown error
+M0M29 assert
+
+M1M05 invalid argument (user supplied)
+M1M16 multistatements not supported in batches
+M1M17 result set not expected for DML or DDL-statement
+M1M19 response is not a result set
+M1M20 object closed
+M1M25 failed reading from/writing to object stream
 
-TODO:
-JDBC 4.1 suggests the following SQLException subclass mapping:
-NonTransientSQLExeceptions (fails when same operation executed again)
-0A SQLFeatureNotSupportedException
-08 SQLNonTransientConnectionException
-22 SQLDataException
-23 SQLIntegrityConstraintViolationException
-28 SQLInvalidAuthorizationException
-42 SQLSyntaxErrorException
-TransientSQLExeceptions (retry of same operation might succeed)
-08 SQLTransientConnectionException
-40 SQLTransactionRollbackException
-   SQLTimeoutException
+SQLState codes are used in SQLExceptions.
+JDBC 4.1 defines the following SQLException subclass mappings:
+ NonTransientSQLExceptions (fails when same operation executed again)
+   0A SQLFeatureNotSupportedException
+   08 SQLNonTransientConnectionException
+   22 SQLDataException
+   23 SQLIntegrityConstraintViolationException
+   28 SQLInvalidAuthorizationSpecException
+   42 SQLSyntaxErrorException
+ TransientSQLExeceptions (retry of same operation might succeed)
+   08 SQLTransientConnectionException
+   40 SQLTransactionRollbackException
+   null SQLTimeoutException
 
+See also: http://docs.oracle.com/javase/7/docs/api/java/sql/SQLException.html
+See also: https://en.wikibooks.org/wiki/Structured_Query_Language/SQLSTATE
+
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetConnection.java
+++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetConnection.java
@@ -19,6 +19,7 @@ import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.SQLFeatureNotSupportedException;
+import java.sql.SQLNonTransientConnectionException;
 import java.sql.SQLWarning;
 import java.sql.Savepoint;
 import java.sql.Statement;
@@ -260,7 +261,7 @@ public class MonetConnection extends Mon
 
 				server.debug(f.getAbsolutePath());
 			} catch (IOException ex) {
-				throw new SQLException("Opening logfile failed: " + ex.getMessage(), "08M01");
+				throw new SQLNonTransientConnectionException("Opening logfile failed: " + ex.getMessage(), "08M01");
 			}
 		}
 
@@ -279,16 +280,16 @@ public class MonetConnection extends Mon
 
 			String error = in.waitForPrompt();
 			if (error != null)
-				throw new SQLException((error.length() > 6) ? error.substring(6) : error, "08001");
+				throw new SQLNonTransientConnectionException((error.length() > 6) ? error.substring(6) : error, "08001");
 		} catch (IOException e) {
-			throw new SQLException("Unable to connect (" + hostname + ":" + port + "): " + e.getMessage(), "08006");
+			throw new SQLNonTransientConnectionException("Unable to connect (" + hostname + ":" + port + "): " + e.getMessage(), "08006");
 		} catch (MCLParseException e) {
-			throw new SQLException(e.getMessage(), "08001");
+			throw new SQLNonTransientConnectionException(e.getMessage(), "08001");
 		} catch (MCLException e) {
 			String[] connex = e.getMessage().split("\n");
-			SQLException sqle = new SQLException(connex[0], "08001", e);
+			SQLException sqle = new SQLNonTransientConnectionException(connex[0], "08001", e);
 			for (int i = 1; i < connex.length; i++) {
-				sqle.setNextException(new SQLException(connex[1], "08001"));
+				sqle.setNextException(new SQLNonTransientConnectionException(connex[1], "08001"));
 			}
 			throw sqle;
 		}
@@ -681,7 +682,7 @@ public class MonetConnection extends Mon
 	 * default result set type and concurrency to be overridden.
 	 *
 	 * @param sql a String object that is the SQL statement to be sent to the
-	 *            database; may contain one or more ? IN parameters
+	 *        database; may contain one or more ? IN parameters
 	 * @param resultSetType a result set type; one of
 	 *        ResultSet.TYPE_FORWARD_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE,
 	 *        or ResultSet.TYPE_SCROLL_SENSITIVE
@@ -691,8 +692,8 @@ public class MonetConnection extends Mon
 	 *         statement that will produce ResultSet objects with the given
 	 *         type and concurrency
 	 * @throws SQLException if a database access error occurs or the given
-	 *                      parameters are not ResultSet constants indicating
-	 *                      type and concurrency
+	 *         parameters are not ResultSet constants indicating
+	 *         type and concurrency
 	 */
 	@Override
 	public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
@@ -1507,7 +1508,7 @@ public class MonetConnection extends Mon
 		try {
 			server.setSoTimeout(millis);
 		} catch (SocketException e) {
-			throw new SQLException(e.getMessage(), "08000");
+			throw new SQLNonTransientConnectionException(e.getMessage(), "08000");
 		}
 	}
 
@@ -1530,7 +1531,7 @@ public class MonetConnection extends Mon
 		try {
 			return server.getSoTimeout();
 		} catch (SocketException e) {
-			throw new SQLException(e.getMessage(), "08000");
+			throw new SQLNonTransientConnectionException(e.getMessage(), "08000");
 		}
 	}
 
@@ -1546,8 +1547,7 @@ public class MonetConnection extends Mon
 		String language = "";
 		if (lang == LANG_MAL)
 			language = "?language=mal";
-		return "jdbc:monetdb://" + hostname + ":" + port + "/" +
-			database + language;
+		return "jdbc:monetdb://" + hostname + ":" + port + "/" + database + language;
 	}
 
 	/**
@@ -1595,13 +1595,12 @@ public class MonetConnection extends Mon
 						(queryTempl[1] == null ? "" : queryTempl[1]));
 				String error = in.waitForPrompt();
 				if (error != null)
-					throw new SQLException(error.substring(6),
-							error.substring(0, 5));
+					throw new SQLException(error.substring(6), error.substring(0, 5));
 			} catch (SocketTimeoutException e) {
 				close(); // JDBC 4.1 semantics: abort()
-				throw new SQLException("connection timed out", "08M33");
+				throw new SQLNonTransientConnectionException("connection timed out", "08M33");
 			} catch (IOException e) {
-				throw new SQLException(e.getMessage(), "08000");
+				throw new SQLNonTransientConnectionException(e.getMessage(), "08000");
 			}
 		}
 	}
@@ -1625,13 +1624,12 @@ public class MonetConnection extends Mon
 						(commandTempl[1] == null ? "" : commandTempl[1]));
 				String error = in.waitForPrompt();
 				if (error != null)
-					throw new SQLException(error.substring(6),
-							error.substring(0, 5));
+					throw new SQLException(error.substring(6), error.substring(0, 5));
 			} catch (SocketTimeoutException e) {
 				close(); // JDBC 4.1 semantics, abort()
-				throw new SQLException("connection timed out", "08M33");
+				throw new SQLNonTransientConnectionException("connection timed out", "08M33");
 			} catch (IOException e) {
-				throw new SQLException(e.getMessage(), "08000");
+				throw new SQLNonTransientConnectionException(e.getMessage(), "08000");
 			}
 		}
 	}
@@ -1771,7 +1769,7 @@ public class MonetConnection extends Mon
 		 * @param columncount the number of columns in the result set
 		 * @param rowcount the number of rows in the current block
 		 * @param parent the parent that created this Response and will
-		 *               supply new result blocks when necessary
+		 *        supply new result blocks when necessary
 		 * @param seq the query sequence number
 		 */
 		ResultSetResponse(
@@ -1929,7 +1927,7 @@ public class MonetConnection extends Mon
 		 * needs to be consistent with regard to its internal data.
 		 *
 		 * @throws SQLException if the data currently in this Response is not
-		 *                      sufficient to be consistant
+		 *         sufficient to be consistant
 		 */
 		@Override
 		public void complete() throws SQLException {
@@ -2727,23 +2725,26 @@ public class MonetConnection extends Mon
 					SQLException ret = null;
 					String[] errors = error.split("\n");
 					for (int i = 0; i < errors.length; i++) {
-						if (ret == null) {
-							ret = new SQLException(errors[i].substring(6),
-									errors[i].substring(0, 5));
+						SQLException newErr;
+						if (errors[i].length() >= 6) {
+							newErr = new SQLException(errors[i].substring(6), errors[i].substring(0, 5));
 						} else {
-							ret.setNextException(new SQLException(
-										errors[i].substring(6),
-										errors[i].substring(0, 5)));
+							newErr = new SQLNonTransientConnectionException(errors[i], "08000");
+						}
+						if (ret == null) {
+							ret = newErr;
+						} else {
+							ret.setNextException(newErr);
 						}
 					}
 					throw ret;
 				}
 			} catch (SocketTimeoutException e) {
 				close(); // JDBC 4.1 semantics, abort()
-				throw new SQLException("connection timed out", "08M33");
+				throw new SQLNonTransientConnectionException("connection timed out", "08M33");
 			} catch (IOException e) {
 				closed = true;
-				throw new SQLException(e.getMessage() + " (mserver still alive?)", "08000");
+				throw new SQLNonTransientConnectionException(e.getMessage() + " (mserver5 still alive?)", "08006");
 			}
 		}
 	}
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetDriver.java.in
+++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetDriver.java.in
@@ -16,6 +16,7 @@ import java.sql.DriverManager;
 import java.sql.DriverPropertyInfo;
 import java.sql.SQLException;
 import java.sql.SQLFeatureNotSupportedException;
+import java.sql.SQLNonTransientConnectionException;
 import java.sql.Types;
 import java.util.ArrayList;
 import java.util.List;
@@ -83,6 +84,7 @@ final public class MonetDriver implement
 	 * @param url the URL of the database
 	 * @return true if this driver understands the given URL; false otherwise
 	 */
+	@Override
 	public boolean acceptsURL(String url) {
 		return url != null && url.startsWith(MONETURL);
 	}
@@ -108,6 +110,7 @@ final public class MonetDriver implement
 	 * @return a Connection object that represents a connection to the URL
 	 * @throws SQLException if a database access error occurs
 	 */
+	@Override
 	public Connection connect(String url, Properties info)
 		throws SQLException
 	{
@@ -124,19 +127,19 @@ final public class MonetDriver implement
 
 		// url should be of style jdbc:monetdb://<host>/<database>
 		if (!acceptsURL(url))
-			throw new SQLException("Invalid URL: it does not start with: " + MONETURL, "08M26");
+			throw new SQLNonTransientConnectionException("Invalid URL: it does not start with: " + MONETURL, "08M26");
 
 		// remove leading "jdbc:" so the rest is a valid hierarchical URI
 		URI uri;
 		try {
 			uri = new URI(url.substring(5));
 		} catch (URISyntaxException e) {
-			throw new SQLException(e.toString(), "08M26");
+			throw new SQLNonTransientConnectionException(e.toString(), "08M26");
 		}
 
 		String uri_host = uri.getHost();
 		if (uri_host == null)
-			throw new SQLException("Invalid URL: no hostname given or unparsable in '" + url + "'", "08M26");
+			throw new SQLNonTransientConnectionException("Invalid URL: no hostname given or unparsable in '" + url + "'", "08M26");
 		info.put("host", uri_host);
 
 		int uri_port = uri.getPort();
@@ -171,6 +174,7 @@ final public class MonetDriver implement
 	 *
 	 * @return this driver's major version number
 	 */
+	@Override
 	public int getMajorVersion() {
 		return DRIVERMAJOR;
 	}
@@ -180,6 +184,7 @@ final public class MonetDriver implement
 	 *
 	 * @return this driver's minor version number
 	 */
+	@Override
 	public int getMinorVersion() {
 		return DRIVERMINOR;
 	}
@@ -201,6 +206,7 @@ final public class MonetDriver implement
 	 *         properties. This array may be an empty array if no properties
 	 *         are required.
 	 */
+	@Override
 	public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) {
 		if (!acceptsURL(url))
 			return null;
@@ -274,10 +280,29 @@ final public class MonetDriver implement
 	 *
 	 * @return true if this driver is JDBC Compliant; false otherwise
 	 */
+	@Override
 	public boolean jdbcCompliant() {
 		return MONETJDBCCOMPLIANT;
 	}
 
+	/**
+	 * Return the parent Logger of all the Loggers used by this data source.
+	 * This should be the Logger farthest from the root Logger that is
+	 * still an ancestor of all of the Loggers used by this data source.
+	 * Configuring this Logger will affect all of the log messages
+	 * generated by the data source.
+	 * In the worst case, this may be the root Logger.
+	 *
+	 * @return the parent Logger for this data source
+	 * @throws SQLFeatureNotSupportedException if the data source does
+	 *         not use java.util.logging
+	 * @since 1.7
+	 */
+	@Override
+	public Logger getParentLogger() throws SQLFeatureNotSupportedException {
+		throw new SQLFeatureNotSupportedException("java.util.logging not in use", "0A000");
+	}
+
 	//== end methods of interface driver
 
 
@@ -320,7 +345,7 @@ final public class MonetDriver implement
 		typeMap.put("url", Integer.valueOf(Types.VARCHAR));
 		typeMap.put("uuid", Integer.valueOf(Types.VARCHAR));
 		typeMap.put("varchar", Integer.valueOf(Types.VARCHAR));
-		typeMap.put("wrd", Integer.valueOf(Types.BIGINT));
+		typeMap.put("wrd", Integer.valueOf(Types.BIGINT));  // keep it in for older MonetDB servers
 	}
 
 	/**
@@ -384,20 +409,4 @@ final public class MonetDriver implement
 	public static int getDriverMinorVersion() {
 		return DRIVERMINOR;
 	}
-
-	/**
-	 * Return the parent Logger of all the Loggers used by this data
-	 * source.  This should be the Logger farthest from the root Logger
-	 * that is still an ancestor of all of the Loggers used by this data
-	 * source.  Configuring this Logger will affect all of the log
-	 * messages generated by the data source. In the worst case, this
-	 * may be the root Logger.
-	 *
-	 * @return the parent Logger for this data source
-	 * @throws SQLFeatureNotSupportedException if the data source does
-	 *         not use java.util.logging
-	 */
-	public Logger getParentLogger() throws SQLFeatureNotSupportedException {
-		throw new SQLFeatureNotSupportedException("java.util.logging not in use", "0A000");
-	}
 }
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetPreparedStatement.java
+++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetPreparedStatement.java
@@ -2585,19 +2585,20 @@ public class MonetPreparedStatement
 
 	/**
 	 * Small helper method that formats the "Invalid Parameter Index number ..." message
-	 * and creates a new SQLException object whose SQLState is set to "M1M05".
+	 * and creates a new SQLDataException object whose SQLState is set
+	 * to "22010": invalid indicator parameter value.
 	 *
 	 * @param paramIdx the parameter index number
-	 * @return a new created SQLException object with SQLState M1M05
+	 * @return a new created SQLDataException object with SQLState 22010
 	 */
-	private final static SQLException newSQLInvalidParameterIndexException(int paramIdx) {
-		return new SQLException("Invalid Parameter Index number: " + paramIdx, "M1M05");
+	private final static SQLDataException newSQLInvalidParameterIndexException(int paramIdx) {
+		return new SQLDataException("Invalid Parameter Index number: " + paramIdx, "22010");
 	}
 
 	/**
 	 * Small helper method that formats the "Method ... not implemented" message
 	 * and creates a new SQLFeatureNotSupportedException object
-	 * whose SQLState is set to "0A000".
+	 * whose SQLState is set to "0A000": feature not supported.
 	 *
 	 * @param name the method name
 	 * @return a new created SQLFeatureNotSupportedException object with SQLState 0A000
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetResultSet.java
+++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetResultSet.java
@@ -30,6 +30,7 @@ import java.sql.ResultSet;
 import java.sql.ResultSetMetaData;
 import java.sql.RowId;
 import java.sql.SQLData;
+import java.sql.SQLDataException;
 import java.sql.SQLException;
 import java.sql.SQLFeatureNotSupportedException;
 import java.sql.SQLInput;
@@ -2799,7 +2800,7 @@ public class MonetResultSet extends Mone
 				return ret == -1 ? null : new java.sql.Date(cal.getTimeInMillis());
 			}
 		} catch (IllegalArgumentException iae) {
-			throw new SQLException("Could not convert value to a Date. Expected JDBC date escape format yyyy-[m]m-[d]d. "
+			throw new SQLDataException("Could not convert value to a Date. Expected JDBC date escape format yyyy-[m]m-[d]d. "
 				+ iae.getMessage(), "22007"); // 22007 = invalid datetime format
 		} catch (IndexOutOfBoundsException e) {
 			throw newSQLInvalidColumnIndexException(columnIndex);
@@ -2892,7 +2893,7 @@ public class MonetResultSet extends Mone
 				return ret == -1 ? null : new Time(cal.getTimeInMillis());
 			}
 		} catch (IllegalArgumentException iae) {
-			throw new SQLException("Could not convert value to a Time. Expected JDBC time escape format hh:mm:ss. "
+			throw new SQLDataException("Could not convert value to a Time. Expected JDBC time escape format hh:mm:ss. "
 				+ iae.getMessage(), "22007"); // 22007 = invalid datetime format
 		} catch (IndexOutOfBoundsException e) {
 			throw newSQLInvalidColumnIndexException(columnIndex);
@@ -2990,7 +2991,7 @@ public class MonetResultSet extends Mone
 				return ts;
 			}
 		} catch (IllegalArgumentException iae) {
-			throw new SQLException("Could not convert value to a Timestamp. Expected JDBC time escape format yyyy-[m]m-[d]d hh:mm:ss[.f...]. "
+			throw new SQLDataException("Could not convert value to a Timestamp. Expected JDBC time escape format yyyy-[m]m-[d]d hh:mm:ss[.f...]. "
 				+ iae.getMessage(), "22007"); // 22007 = invalid datetime format
 		} catch (IndexOutOfBoundsException e) {
 			throw newSQLInvalidColumnIndexException(columnIndex);
@@ -3795,30 +3796,32 @@ public class MonetResultSet extends Mone
 
 	/**
 	 * Small helper method that formats the "Invalid Column Index number ..." message
-	 * and creates a new SQLException object whose SQLState is set to "M1M05".
+	 * and creates a new SQLDataException object whose SQLState is set
+	 * to "22010": invalid indicator parameter value.
 	 *
 	 * @param colIdx the column index number
-	 * @return a new created SQLException object with SQLState M1M05
+	 * @return a new created SQLDataException object with SQLState 22010
 	 */
-	public final static SQLException newSQLInvalidColumnIndexException(int colIdx) {
-		return new SQLException("Invalid Column Index number: " + colIdx, "M1M05");
+	public final static SQLDataException newSQLInvalidColumnIndexException(int colIdx) {
+		return new SQLDataException("Invalid Column Index number: " + colIdx, "22010");
 	}
 
 	/**
 	 * Small helper method that formats the "Could not convert value to a number" message
-	 * and creates a new SQLException object whose SQLState is set to "22003": Numeric value out of range.
+	 * and creates a new SQLDataException object whose SQLState is set
+	 * to "22003": Numeric value out of range.
 	 *
 	 * @param error the NumberFormatException
-	 * @return a new created SQLException object with SQLState 22003
+	 * @return a new created SQLDataException object with SQLState 22003
 	 */
-	private final static SQLException newSQLNumberFormatException(NumberFormatException error) {
-		return new SQLException("Could not convert value to a number. " + error.getMessage(), "22003");
+	private final static SQLDataException newSQLNumberFormatException(NumberFormatException error) {
+		return new SQLDataException("Could not convert value to a number. " + error.getMessage(), "22003");
 	}
 
 	/**
 	 * Small helper method that formats the "Method ... not implemented" message
 	 * and creates a new SQLFeatureNotSupportedException object
-	 * whose SQLState is set to "0A000".
+	 * whose SQLState is set to "0A000": feature not supported.
 	 *
 	 * @param name the method name
 	 * @return a new created SQLFeatureNotSupportedException object with SQLState 0A000
--- a/src/main/java/nl/cwi/monetdb/jdbc/types/INET.java
+++ b/src/main/java/nl/cwi/monetdb/jdbc/types/INET.java
@@ -12,14 +12,15 @@ import java.net.Inet4Address;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.sql.SQLData;
+import java.sql.SQLDataException;
 import java.sql.SQLException;
 import java.sql.SQLInput;
 import java.sql.SQLOutput;
 
 /**
- * The INET class represents the INET datatype in MonetDB.  It
- * represents a IPv4 address with a certain mask applied.  Currently,
- * IPv6 is not supported.
+ * The INET class represents the INET datatype in MonetDB.
+ * It represents a IPv4 address with a certain mask applied.
+ * Currently, IPv6 is not supported.
  * <br />
  * The input format for INET is x.x.x.x/y where x.x.x.x is an IP address
  * and y is the number of bits in the netmask. If the /y part is left
@@ -27,8 +28,8 @@ import java.sql.SQLOutput;
  * host. On display, the /y portion is suppressed if the netmask is 32.
  * <br />
  * This class allows to retrieve the value of this INET as InetAddress.
- * This is probably meaningful only and only if the netmask is 32.  The
- * getNetmaskBits() method can be used to retrieve the subnet bits.
+ * This is probably meaningful only and only if the netmask is 32.
+ * The getNetmaskBits() method can be used to retrieve the subnet bits.
  */
 public class INET implements SQLData {
 	private String inet;
@@ -40,9 +41,8 @@ public class INET implements SQLData {
 
 	@Override
 	public void readSQL(SQLInput stream, String typeName) throws SQLException {
-		if (typeName.compareTo("inet") != 0)
-			throw new SQLException("can only use this class with 'inet' type",
-					"M1M05");
+		if (!"inet".equals(typeName))
+			throw new SQLException("can only use this class with 'inet' type", "M1M05");
 		inet = stream.readString();
 	}
 
@@ -56,40 +56,39 @@ public class INET implements SQLData {
 		return inet;
 	}
 
-	public void fromString(String newinet) throws Exception {
+	public void fromString(String newinet) throws SQLException {
 		if (newinet == null) {
 			inet = newinet;
 			return;
 		}
+		String tinet = newinet;
 		int slash = newinet.indexOf('/');
-		String tinet = newinet;
 		if (slash != -1) {
 			int netmask;
 			// ok, see if it is a valid netmask
 			try {
 				netmask = Integer.parseInt(newinet.substring(slash + 1));
 			} catch (NumberFormatException nfe) {
-				throw new Exception("cannot parse netmask bits: " +
-						newinet.substring(slash + 1));
+				throw new SQLDataException("cannot parse netmask bits: " +
+						newinet.substring(slash + 1), "22M29");
 			}
 			if (netmask <= 0 || netmask > 32)
-				throw new Exception("netmask must be >0 and <32");
+				throw new SQLDataException("netmask must be >0 and <32", "22M29");
 			tinet = newinet.substring(0, slash);
 		}
 		// check dotted quad
 		String quads[] = tinet.split("\\.");
 		if (quads.length != 4)
-			throw new Exception("expected dotted quad (xxx.xxx.xxx.xxx)");
+			throw new SQLDataException("expected dotted quad (xxx.xxx.xxx.xxx)", "22M29");
 		for (int i = 0; i < 4; i++) {
 			int quadv;
 			try {
 				quadv = Integer.parseInt(quads[i]);
 			} catch (NumberFormatException nfe) {
-				throw new Exception("cannot parse number: " + quads[i]);
+				throw new SQLDataException("cannot parse number: " + quads[i], "22M29");
 			}
 			if (quadv < 0 || quadv > 255)
-				throw new Exception("value must be between 0 and 255: " +
-						quads[i]);
+				throw new SQLDataException("value must be between 0 and 255: " + quads[i], "22M29");
 		}
 		// everything is fine
 		inet = newinet;
@@ -128,8 +127,8 @@ public class INET implements SQLData {
 		try {
 			return Integer.parseInt(inet.substring(slash + 1));
 		} catch (NumberFormatException nfe) {
-			throw new SQLException("cannot parse netmask bits: " +
-					inet.substring(slash + 1), "M0M27");
+			throw new SQLDataException("cannot parse netmask bits: " +
+					inet.substring(slash + 1), "22M29");
 		}
 	}
 
@@ -155,7 +154,7 @@ public class INET implements SQLData {
 		try {
 			return InetAddress.getByName(getAddress());
 		} catch (UnknownHostException uhe) {
-			throw new SQLException("could not resolve IP address", "M0M27");
+			throw new SQLDataException("could not resolve IP address", "22M29");
 		}
 	}
 
--- a/src/main/java/nl/cwi/monetdb/jdbc/types/URL.java
+++ b/src/main/java/nl/cwi/monetdb/jdbc/types/URL.java
@@ -10,14 +10,14 @@ package nl.cwi.monetdb.jdbc.types;
 
 import java.net.MalformedURLException;
 import java.sql.SQLData;
+import java.sql.SQLDataException;
 import java.sql.SQLException;
 import java.sql.SQLInput;
 import java.sql.SQLOutput;
 
 /**
- * The URL class represents the URL datatype in MonetDB.  It
- * represents an URL, that is, a well-formed string conforming to
- * RFC2396.
+ * The URL class represents the URL datatype in MonetDB.
+ * It represents an URL, that is, a well-formed string conforming to RFC2396.
  */
 public class URL implements SQLData {
 	private String url;
@@ -29,7 +29,7 @@ public class URL implements SQLData {
 
 	@Override
 	public void readSQL(SQLInput stream, String typeName) throws SQLException {
-		if (typeName.compareTo("url") != 0)
+		if (!"url".equals(typeName))
 			throw new SQLException("can only use this class with 'url' type", "M1M05");
 		url = stream.readString();
 	}
@@ -61,7 +61,7 @@ public class URL implements SQLData {
 		try {
 			return new java.net.URL(url);
 		} catch (MalformedURLException mue) {
-			throw new SQLException("data is not a valid URL", "M0M27");
+			throw new SQLDataException("data is not a valid URL: " + mue.getMessage(), "22M30");
 		}
 	}