List:Commits« Previous MessageNext Message »
From:mmatthews Date:May 2 2007 8:55pm
Subject:Connector/J commit: r6414 - in branches: branch_5_0/connector-j branch_5_0/connector-j/src/com/mysql/jdbc branch_5_0/connector-j/src/com/mysql/jdbc/co...
View as plain text  
Modified:
   branches/branch_5_0/connector-j/CHANGES
   branches/branch_5_0/connector-j/src/com/mysql/jdbc/ConnectionProperties.java
   branches/branch_5_0/connector-j/src/com/mysql/jdbc/LocalizedErrorMessages.properties
   branches/branch_5_0/connector-j/src/com/mysql/jdbc/NotUpdatable.java
   branches/branch_5_0/connector-j/src/com/mysql/jdbc/UpdatableResultSet.java
   branches/branch_5_0/connector-j/src/com/mysql/jdbc/configs/3-0-Compat.properties
   branches/branch_5_0/connector-j/src/testsuite/regression/ResultSetRegressionTest.java
   branches/branch_5_0/connector-j/src/testsuite/regression/StatementRegressionTest.java
   branches/branch_5_1/connector-j/CHANGES
   branches/branch_5_1/connector-j/src/com/mysql/jdbc/ConnectionProperties.java
   branches/branch_5_1/connector-j/src/com/mysql/jdbc/ConnectionPropertiesImpl.java
   branches/branch_5_1/connector-j/src/com/mysql/jdbc/LocalizedErrorMessages.properties
   branches/branch_5_1/connector-j/src/com/mysql/jdbc/NotUpdatable.java
   branches/branch_5_1/connector-j/src/com/mysql/jdbc/UpdatableResultSet.java
   branches/branch_5_1/connector-j/src/com/mysql/jdbc/configs/3-0-Compat.properties
   branches/branch_5_1/connector-j/src/testsuite/regression/ResultSetRegressionTest.java
   branches/branch_5_1/connector-j/src/testsuite/regression/StatementRegressionTest.java
Log:
	  	    
	- Fixed BUG#28085 - Generate more useful error messages for diagnostics
	  when the driver thinks a result set isn't updatable. (Thanks to Ashley Martens
	  for the patch -- contributed under CLA#43).
	  
	- Driver will now use INSERT INTO ... VALUES (DEFAULT) form of statement
	  for updatable result sets for ResultSet.insertRow(), rather than 
	  pre-populating the insert row with values from DatabaseMetaData.getColumns()
	  (which results in a "SHOW FULL COLUMNS" on the server for every result
	  set). If an application requires access to the default values before
	  insertRow() has been called, the JDBC URL should be configured with 
	  "populateInsertRowWithDefaultValues" set to "true".
	  
	  This fix specifically targets performance issues with ColdFusion and the 
	  fact that it seems to ask for updatable result sets no matter what the 
	  application does with them.

Modified: branches/branch_5_0/connector-j/CHANGES
===================================================================
--- branches/branch_5_0/connector-j/CHANGES	2007-04-24 21:03:27 UTC (rev 6413)
+++ branches/branch_5_0/connector-j/CHANGES	2007-05-02 18:55:06 UTC (rev 6414)
@@ -99,6 +99,22 @@
 	  is reconnected to a writable master unless "failoverReadOnly" had been set
 	  to "false".
 	    
+	- Fixed BUG#28085 - Generate more useful error messages for diagnostics
+	  when the driver thinks a result set isn't updatable. (Thanks to Ashley Martens
+	  for the patch).
+	  
+	- Driver will now use INSERT INTO ... VALUES (DEFAULT) form of statement
+	  for updatable result sets for ResultSet.insertRow(), rather than 
+	  pre-populating the insert row with values from DatabaseMetaData.getColumns()
+	  (which results in a "SHOW FULL COLUMNS" on the server for every result
+	  set). If an application requires access to the default values before
+	  insertRow() has been called, the JDBC URL should be configured with 
+	  "populateInsertRowWithDefaultValues" set to "true".
+	  
+	  This fix specifically targets performance issues with ColdFusion and the 
+	  fact that it seems to ask for updatable result sets no matter what the 
+	  application does with them.
+	  
 03-01-07 - Version 5.0.5
 
     - Fixed BUG#23645 - Some collations/character sets reported as "unknown"

Modified: branches/branch_5_0/connector-j/src/com/mysql/jdbc/ConnectionProperties.java
===================================================================
--- branches/branch_5_0/connector-j/src/com/mysql/jdbc/ConnectionProperties.java	2007-04-24 21:03:27 UTC (rev 6413)
+++ branches/branch_5_0/connector-j/src/com/mysql/jdbc/ConnectionProperties.java	2007-05-02 18:55:06 UTC (rev 6414)
@@ -1080,6 +1080,16 @@
 			+ " to support \"XA START ... JOIN\" after \"XA END\" has been called",
 			"5.0.1", MISC_CATEGORY, Integer.MIN_VALUE);
 	
+	private BooleanConnectionProperty populateInsertRowWithDefaultValues = new BooleanConnectionProperty(
+			"populateInsertRowWithDefaultValues", false,
+			"When using ResultSets that are CONCUR_UPDATABLE, should the driver pre-poulate " +
+			"the \"insert\" row with default values from the DDL for the table used in the query " +
+			" so those values are immediately available for ResultSet accessors? This functionality requires a " +
+			" call to the database for metadata each time a result set of this type is created. " +
+			" If disabled (the default), the default values will be populated by the an internal" +
+			" call to refreshRow() which pulls back default values and/or values changed by triggers.",
+			"5.0.5", MISC_CATEGORY, Integer.MIN_VALUE);
+	
 	private IntegerConnectionProperty preparedStatementCacheSize = new IntegerConnectionProperty(
 			"prepStmtCacheSize", 25, 0, Integer.MAX_VALUE,
 			"If prepared statement caching is enabled, "
@@ -3918,4 +3928,12 @@
 	public void setUseDynamicCharsetInfo(boolean flag) {
 		this.useDynamicCharsetInfo.setValue(flag);
 	}
+
+	public boolean getPopulateInsertRowWithDefaultValues() {
+		return this.populateInsertRowWithDefaultValues.getValueAsBoolean();
+	}
+
+	public void setPopulateInsertRowWithDefaultValues(boolean flag) {
+		this.populateInsertRowWithDefaultValues.setValue(flag);
+	}
 }

Modified: branches/branch_5_0/connector-j/src/com/mysql/jdbc/LocalizedErrorMessages.properties
===================================================================
--- branches/branch_5_0/connector-j/src/com/mysql/jdbc/LocalizedErrorMessages.properties	2007-04-24 21:03:27 UTC (rev 6413)
+++ branches/branch_5_0/connector-j/src/com/mysql/jdbc/LocalizedErrorMessages.properties	2007-05-02 18:55:06 UTC (rev 6414)
@@ -400,9 +400,19 @@
 ByteArrayBuffer.1=Unsupported character encoding '
 AssertionFailedException.0=ASSERT FAILS: Exception 
 AssertionFailedException.1=\ that should not be thrown, was thrown
+
 NotUpdatable.0=Result Set not updatable.
 NotUpdatable.1=This result set must come from a statement 
 NotUpdatable.2=that was created with a result set type of ResultSet.CONCUR_UPDATABLE, 
-NotUpdatable.3=the query must select only one table, and must 
+NotUpdatable.3=the query must select only one table, can not use functions and must 
 NotUpdatable.4=select all primary keys from that table. See the JDBC 2.1 API Specification, 
 NotUpdatable.5=section 5.6 for more details.
+NotUpdatableReason.0=Result Set not updatable (references more than one table).
+NotUpdatableReason.1=Result Set not updatable (references more than one database).
+NotUpdatableReason.2=Result Set not updatable (references no tables).
+NotUpdatableReason.3=Result Set not updatable (references computed values or doesn't reference any columns or tables).
+NotUpdatableReason.4=Result Set not updatable (references no primary keys).
+NotUpdatableReason.5=Result Set not updatable (referenced table has no primary keys).
+NotUpdatableReason.6=Result Set not updatable (references unknown primary key {0}).
+NotUpdatableReason.7=Result Set not updatable (does not reference all primary keys).
+

Modified: branches/branch_5_0/connector-j/src/com/mysql/jdbc/NotUpdatable.java
===================================================================
--- branches/branch_5_0/connector-j/src/com/mysql/jdbc/NotUpdatable.java	2007-04-24 21:03:27 UTC (rev 6413)
+++ branches/branch_5_0/connector-j/src/com/mysql/jdbc/NotUpdatable.java	2007-05-02 18:55:06 UTC (rev 6414)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -32,9 +32,9 @@
  * @author Mark Matthews
  */
 public class NotUpdatable extends SQLException {
-	// ~ Static fields/initializers
-	// ---------------------------------------------
 
+	private static final long serialVersionUID = 8084742846039782258L;
+
 	/**
 	 * The message to use when result set is not updatable.
 	 * 
@@ -49,13 +49,24 @@
 			+ Messages.getString("NotUpdatable.4") //$NON-NLS-1$
 			+ Messages.getString("NotUpdatable.5"); //$NON-NLS-1$
 
-	// ~ Constructors
-	// -----------------------------------------------------------
-
 	/**
 	 * Creates a new NotUpdatable exception.
 	 */
 	public NotUpdatable() {
-		super(NOT_UPDATEABLE_MESSAGE, SQLError.SQL_STATE_GENERAL_ERROR);
+		this(NOT_UPDATEABLE_MESSAGE);
 	}
-}
+
+	/**
+	 * Append the given reason to the not updatable message if the reason is not
+	 * null.
+	 */
+	public NotUpdatable(String reason) {
+		super(reason
+				+ Messages.getString("NotUpdatable.1")
+				+ Messages.getString("NotUpdatable.2")
+				+ Messages.getString("NotUpdatable.3")
+				+ Messages.getString("NotUpdatable.4")
+				+ Messages.getString("NotUpdatable.5"),
+				SQLError.SQL_STATE_GENERAL_ERROR);
+	}
+}
\ No newline at end of file

Modified: branches/branch_5_0/connector-j/src/com/mysql/jdbc/UpdatableResultSet.java
===================================================================
--- branches/branch_5_0/connector-j/src/com/mysql/jdbc/UpdatableResultSet.java	2007-04-24 21:03:27 UTC (rev 6413)
+++ branches/branch_5_0/connector-j/src/com/mysql/jdbc/UpdatableResultSet.java	2007-05-02 18:55:06 UTC (rev 6414)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -67,6 +67,9 @@
 	/** Is this result set updateable? */
 	private boolean isUpdatable = false;
 
+	/** Reason the result set is not updatable */
+	private String notUpdatableReason = null;
+
 	/** List of primary keys */
 	private List primaryKeyIndicies = null;
 
@@ -89,6 +92,8 @@
 
 	/** SQL for in-place modifcation */
 	private String updateSQL = null;
+	
+	private boolean populateInserterWithDefaultValues = false;
 
 	/**
 	 * Create a result set for an executeUpdate statement.
@@ -132,6 +137,8 @@
 			Connection conn, Statement creatorStmt) throws SQLException {
 		super(catalog, fields, tuples, conn, creatorStmt);
 		checkUpdatability();
+		this.populateInserterWithDefaultValues = 
+			this.connection.getPopulateInsertRowWithDefaultValues();
 	}
 
 	/**
@@ -262,6 +269,21 @@
 
 		int primaryKeyCount = 0;
 
+		// We can only do this if we know that there is a currently
+		// selected database, or if we're talking to a > 4.1 version
+		// of MySQL server (as it returns database names in field
+		// info)
+		//
+		if ((this.catalog == null) || (this.catalog.length() == 0)) {
+			this.catalog = this.fields[0].getDatabaseName();
+
+			if ((this.catalog == null) || (this.catalog.length() == 0)) {
+				throw SQLError.createSQLException(Messages
+						.getString("UpdatableResultSet.43") //$NON-NLS-1$
+						, SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ //$NON-NLS-2$
+			}
+		}
+
 		if (this.fields.length > 0) {
 			singleTableName = this.fields[0].getOriginalTableName();
 			catalogName = this.fields[0].getDatabaseName();
@@ -271,6 +293,13 @@
 				catalogName = this.catalog;
 			}
 
+			if (singleTableName != null && singleTableName.length() == 0) {
+				this.isUpdatable = false;
+				this.notUpdatableReason = Messages.getString("NotUpdatableReason.3");
+
+				return;
+			}
+			
 			if (this.fields[0].isPrimaryKey()) {
 				primaryKeyCount++;
 			}
@@ -287,9 +316,17 @@
 					otherCatalogName = this.catalog;
 				}
 
+				if (otherTableName != null && otherTableName.length() == 0) {
+					this.isUpdatable = false;
+					this.notUpdatableReason = Messages.getString("NotUpdatableReason.3");
+
+					return;
+				}
+				
 				if ((singleTableName == null)
 						|| !otherTableName.equals(singleTableName)) {
 					this.isUpdatable = false;
+					this.notUpdatableReason = Messages.getString("NotUpdatableReason.0");
 
 					return;
 				}
@@ -298,6 +335,7 @@
 				if ((catalogName == null)
 						|| !otherCatalogName.equals(catalogName)) {
 					this.isUpdatable = false;
+					this.notUpdatableReason = Messages.getString("NotUpdatableReason.1");
 
 					return;
 				}
@@ -309,39 +347,17 @@
 
 			if ((singleTableName == null) || (singleTableName.length() == 0)) {
 				this.isUpdatable = false;
+				this.notUpdatableReason = Messages.getString("NotUpdatableReason.2");
 
 				return;
 			}
 		} else {
 			this.isUpdatable = false;
+			this.notUpdatableReason = Messages.getString("NotUpdatableReason.3");
 
 			return;
 		}
-
-		// 
-		// Must have at least one primary key
-		//
-		if (primaryKeyCount == 0) {
-			this.isUpdatable = false;
-
-			return;
-		}
-
-		// We can only do this if we know that there is a currently
-		// selected database, or if we're talking to a > 4.1 version
-		// of MySQL server (as it returns database names in field
-		// info)
-		//
-		if ((this.catalog == null) || (this.catalog.length() == 0)) {
-			this.catalog = this.fields[0].getDatabaseName();
-
-			if ((this.catalog == null) || (this.catalog.length() == 0)) {
-				throw SQLError.createSQLException(Messages
-						.getString("UpdatableResultSet.43") //$NON-NLS-1$
-						, SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ //$NON-NLS-2$
-			}
-		}
-
+		
 		if (this.connection.getStrictUpdates()) {
 			java.sql.DatabaseMetaData dbmd = this.connection.getMetaData();
 
@@ -368,8 +384,11 @@
 				}
 			}
 
-			if (primaryKeyNames.size() == 0) {
+			int existingPrimaryKeysCount = primaryKeyNames.size();
+			
+			if (existingPrimaryKeysCount == 0) {
 				this.isUpdatable = false;
+				this.notUpdatableReason = Messages.getString("NotUpdatableReason.5");
 
 				return; // we can't update tables w/o keys
 			}
@@ -391,6 +410,8 @@
 									.toUpperCase()) == null) {
 								// we don't know about this key, so give up :(
 								this.isUpdatable = false;
+								this.notUpdatableReason = Messages.getString("NotUpdatableReason.6", 
+										new Object[] {originalName});
 
 								return;
 							}
@@ -400,11 +421,30 @@
 			}
 
 			this.isUpdatable = primaryKeyNames.isEmpty();
+			
+			if (!this.isUpdatable) {
+				if (existingPrimaryKeysCount > 1) {
+					this.notUpdatableReason = Messages.getString("NotUpdatableReason.7");
+				} else {
+					this.notUpdatableReason = Messages.getString("NotUpdatableReason.4");
+				}
+				
+				return;
+			}
+		}
+		
+		// 
+		// Must have at least one primary key
+		//
+		if (primaryKeyCount == 0) {
+			this.isUpdatable = false;
+			this.notUpdatableReason = Messages.getString("NotUpdatableReason.4");
 
 			return;
 		}
 
 		this.isUpdatable = true;
+		this.notUpdatableReason = null;
 
 		return;
 	}
@@ -423,7 +463,7 @@
 		checkClosed();
 
 		if (!this.isUpdatable) {
-			throw new NotUpdatable();
+			throw new NotUpdatable(this.notUpdatableReason);
 		}
 
 		if (this.onInsertRow) {
@@ -565,7 +605,7 @@
 			this.doingUpdates = false;
 			this.onInsertRow = false;
 
-			throw new NotUpdatable();
+			throw new NotUpdatable(this.notUpdatableReason);
 		}
 
 		String quotedId = getQuotedIdChar();
@@ -755,13 +795,17 @@
 			//
 			if (this.fields[i].isAutoIncrement() && autoIncrementId > 0) {
 				newRow[i] = String.valueOf(autoIncrementId).getBytes();
+				this.inserter.setBytesNoEscapeNoQuotes(i + 1, newRow[i]);
 			}
 		}
-
+		
+		refreshRow(this.inserter, newRow);
+		
 		this.rowData.addRow(newRow);
 		resetInserter();
 	}
 
+
 	/**
 	 * JDBC 2.0
 	 * 
@@ -866,7 +910,7 @@
 		checkClosed();
 
 		if (!this.isUpdatable) {
-			throw new NotUpdatable();
+			throw new NotUpdatable(this.notUpdatableReason);
 		}
 
 		if (this.onInsertRow) {
@@ -896,7 +940,7 @@
 		checkClosed();
 
 		if (!this.isUpdatable) {
-			throw new NotUpdatable();
+			throw new NotUpdatable(this.notUpdatableReason);
 		}
 
 		if (this.inserter == null) {
@@ -906,7 +950,10 @@
 
 			this.inserter = this.connection
 					.clientPrepareStatement(this.insertSQL);
-			extractDefaultValues();
+			if (this.populateInserterWithDefaultValues) {
+				extractDefaultValues();
+			}
+			
 			resetInserter();
 		} else {
 			resetInserter();
@@ -920,52 +967,54 @@
 		this.thisRow = new byte[numFields][];
 
 		for (int i = 0; i < numFields; i++) {
-			if (this.defaultColumnValue[i] != null) {
-				Field f = this.fields[i];
-
-				switch (f.getMysqlType()) {
-				case MysqlDefs.FIELD_TYPE_DATE:
-				case MysqlDefs.FIELD_TYPE_DATETIME:
-				case MysqlDefs.FIELD_TYPE_NEWDATE:
-				case MysqlDefs.FIELD_TYPE_TIME:
-				case MysqlDefs.FIELD_TYPE_TIMESTAMP:
-
-					if (this.defaultColumnValue[i].length > 7
-							&& this.defaultColumnValue[i][0] == (byte) 'C'
-							&& this.defaultColumnValue[i][1] == (byte) 'U'
-							&& this.defaultColumnValue[i][2] == (byte) 'R'
-							&& this.defaultColumnValue[i][3] == (byte) 'R'
-							&& this.defaultColumnValue[i][4] == (byte) 'E'
-							&& this.defaultColumnValue[i][5] == (byte) 'N'
-							&& this.defaultColumnValue[i][6] == (byte) 'T'
-							&& this.defaultColumnValue[i][7] == (byte) '_') {
-						this.inserter.setBytesNoEscapeNoQuotes(i + 1,
-								this.defaultColumnValue[i]);
-
-						break;
+			if (!this.populateInserterWithDefaultValues) {
+				this.inserter.setBytesNoEscapeNoQuotes(i + 1, 
+						"DEFAULT".getBytes());
+				this.thisRow[i] = null;
+			} else {
+				if (this.defaultColumnValue[i] != null) {
+					Field f = this.fields[i];
+	
+					switch (f.getMysqlType()) {
+					case MysqlDefs.FIELD_TYPE_DATE:
+					case MysqlDefs.FIELD_TYPE_DATETIME:
+					case MysqlDefs.FIELD_TYPE_NEWDATE:
+					case MysqlDefs.FIELD_TYPE_TIME:
+					case MysqlDefs.FIELD_TYPE_TIMESTAMP:
+	
+						if (this.defaultColumnValue[i].length > 7
+								&& this.defaultColumnValue[i][0] == (byte) 'C'
+								&& this.defaultColumnValue[i][1] == (byte) 'U'
+								&& this.defaultColumnValue[i][2] == (byte) 'R'
+								&& this.defaultColumnValue[i][3] == (byte) 'R'
+								&& this.defaultColumnValue[i][4] == (byte) 'E'
+								&& this.defaultColumnValue[i][5] == (byte) 'N'
+								&& this.defaultColumnValue[i][6] == (byte) 'T'
+								&& this.defaultColumnValue[i][7] == (byte) '_') {
+							this.inserter.setBytesNoEscapeNoQuotes(i + 1,
+									this.defaultColumnValue[i]);
+	
+							break;
+						}
+					default:
+						this.inserter.setBytes(i + 1, this.defaultColumnValue[i],
+								false, false);
 					}
-				default:
-					this.inserter.setBytes(i + 1, this.defaultColumnValue[i],
-							false, false);
+	
+					// This value _could_ be changed from a getBytes(), so we
+					// need a copy....
+					byte[] defaultValueCopy = new byte[this.defaultColumnValue[i].length];
+					System.arraycopy(defaultColumnValue[i], 0, defaultValueCopy, 0,
+							defaultValueCopy.length);
+					this.thisRow[i] = defaultValueCopy;
+				} else {
+					this.inserter.setNull(i + 1, java.sql.Types.NULL);
+					this.thisRow[i] = null;
 				}
-
-				// This value _could_ be changed from a getBytes(), so we
-				// need a copy....
-				byte[] defaultValueCopy = new byte[this.defaultColumnValue[i].length];
-				System.arraycopy(defaultColumnValue[i], 0, defaultValueCopy, 0,
-						defaultValueCopy.length);
-				this.thisRow[i] = defaultValueCopy;
-			} else {
-				this.inserter.setNull(i + 1, java.sql.Types.NULL);
-				this.thisRow[i] = null;
 			}
 		}
 	}
-
-	// ---------------------------------------------------------------------
-	// Updates
-	// ---------------------------------------------------------------------
-
+	
 	/**
 	 * A ResultSet is initially positioned before its first row, the first call
 	 * to next makes the first row the current row; the second call makes the
@@ -1137,6 +1186,11 @@
 			throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.11")); //$NON-NLS-1$
 		}
 
+		refreshRow(this.updater, this.thisRow);
+	}
+	
+	private synchronized void refreshRow(PreparedStatement updateInsertStmt, 
+			Object[] rowToRefresh) throws SQLException {
 		if (this.refresher == null) {
 			if (this.refreshSQL == null) {
 				generateStatements();
@@ -1154,14 +1208,14 @@
 			byte[] dataFrom = null;
 			int index = ((Integer) this.primaryKeyIndicies.get(0)).intValue();
 
-			if (!this.doingUpdates) {
-				dataFrom = (byte[]) this.thisRow[index];
+			if (!this.doingUpdates && !this.onInsertRow) {
+				dataFrom = (byte[]) rowToRefresh[index];
 			} else {
-				dataFrom = this.updater.getBytesRepresentation(index);
+				dataFrom = updateInsertStmt.getBytesRepresentation(index);
 
 				// Primary keys not set?
-				if (this.updater.isNull(index) || (dataFrom.length == 0)) {
-					dataFrom = (byte[]) this.thisRow[index];
+				if (updateInsertStmt.isNull(index) || (dataFrom.length == 0)) {
+					dataFrom = (byte[]) rowToRefresh[index];
 				} else {
 					dataFrom = stripBinaryPrefix(dataFrom);
 				}
@@ -1174,13 +1228,13 @@
 				int index = ((Integer) this.primaryKeyIndicies.get(i))
 						.intValue();
 
-				if (!this.doingUpdates) {
-					dataFrom = (byte[]) this.thisRow[index];
+				if (!this.doingUpdates && !this.onInsertRow) {
+					dataFrom = (byte[]) rowToRefresh[index];
 				} else {
-					dataFrom = this.updater.getBytesRepresentation(index);
+					dataFrom = updateInsertStmt.getBytesRepresentation(index);
 
 					// Primary keys not set?
-					if (this.updater.isNull(index) || (dataFrom.length == 0)) {
+					if (updateInsertStmt.isNull(index) || (dataFrom.length == 0)) {
 						dataFrom = (byte[]) this.thisRow[index];
 					} else {
 						dataFrom = stripBinaryPrefix(dataFrom);
@@ -1203,9 +1257,9 @@
 					byte[] val = rs.getBytes(i + 1);
 
 					if ((val == null) || rs.wasNull()) {
-						this.thisRow[i] = null;
+						rowToRefresh[i] = null;
 					} else {
-						this.thisRow[i] = rs.getBytes(i + 1);
+						rowToRefresh[i] = rs.getBytes(i + 1);
 					}
 				}
 			} else {
@@ -2222,7 +2276,7 @@
 	 */
 	public synchronized void updateRow() throws SQLException {
 		if (!this.isUpdatable) {
-			throw new NotUpdatable();
+			throw new NotUpdatable(this.notUpdatableReason);
 		}
 
 		if (this.doingUpdates) {
@@ -2448,4 +2502,4 @@
 			java.sql.Timestamp x) throws SQLException {
 		updateTimestamp(findColumn(columnName), x);
 	}
-}
+}
\ No newline at end of file

Modified: branches/branch_5_0/connector-j/src/com/mysql/jdbc/configs/3-0-Compat.properties
===================================================================
--- branches/branch_5_0/connector-j/src/com/mysql/jdbc/configs/3-0-Compat.properties	2007-04-24 21:03:27 UTC (rev 6413)
+++ branches/branch_5_0/connector-j/src/com/mysql/jdbc/configs/3-0-Compat.properties	2007-05-02 18:55:06 UTC (rev 6414)
@@ -14,4 +14,5 @@
 useServerPrepStmts=false
 autoClosePStmtStreams=true
 processEscapeCodesForPrepStmts=false
-useFastDateParsing=false
\ No newline at end of file
+useFastDateParsing=false
+populateInsertRowWithDefaultValues=false
\ No newline at end of file

Modified: branches/branch_5_0/connector-j/src/testsuite/regression/ResultSetRegressionTest.java
===================================================================
--- branches/branch_5_0/connector-j/src/testsuite/regression/ResultSetRegressionTest.java	2007-04-24 21:03:27 UTC (rev 6413)
+++ branches/branch_5_0/connector-j/src/testsuite/regression/ResultSetRegressionTest.java	2007-05-02 18:55:06 UTC (rev 6414)
@@ -51,6 +51,7 @@
 
 import testsuite.BaseTestCase;
 
+import com.mysql.jdbc.Messages;
 import com.mysql.jdbc.MysqlDataTruncation;
 import com.mysql.jdbc.NotUpdatable;
 import com.mysql.jdbc.SQLError;
@@ -3811,6 +3812,10 @@
 	 * @throws Exception if the test fails.
 	 */
 	public void testbug25328() throws Exception {
+		if (!versionMeetsMinimum(5, 0)) {
+			return;
+		}
+		
 		createTable("testBug25382", "(BINARY_VAL BIT(64) NULL)");
 
 		byte[] bytearr = new byte[8];
@@ -4263,4 +4268,103 @@
 			closeMemberJDBCResources();
 		}
 	}
-}
+	
+	/**
+	 * Tests fix for BUG#28085 - Need more useful error messages for diagnostics
+	 * when the driver thinks a result set isn't updatable.
+	 * 
+	 * @throws Exception if the tests fail.
+	 */
+	public void testBug28085() throws Exception {
+
+		Statement updStmt = null;
+		
+		try {
+			createTable("testBug28085_oneKey", 
+				"(pk int primary key not null, field2 varchar(3))");
+			
+			this.stmt.executeUpdate("INSERT INTO testBug28085_oneKey (pk, field2) VALUES (1, 'abc')");
+			
+			createTable("testBug28085_multiKey", 
+				"(pk1 int not null, pk2 int not null, field2 varchar(3), primary key (pk1, pk2))");
+			
+			this.stmt.executeUpdate("INSERT INTO testBug28085_multiKey VALUES (1,2,'abc')");
+			
+			createTable("testBug28085_noKey", 
+					"(field1 varchar(3) not null)");
+	
+			this.stmt.executeUpdate("INSERT INTO testBug28085_noKey VALUES ('abc')");
+			
+			updStmt = this.conn.createStatement(ResultSet.TYPE_FORWARD_ONLY,
+					ResultSet.CONCUR_UPDATABLE);
+			
+			this.rs = updStmt.executeQuery("SELECT field2 FROM testBug28085_oneKey");
+			exerciseUpdatableResultSet(1, "NotUpdatableReason.4");
+			
+			this.rs = updStmt.executeQuery("SELECT pk1, field2 FROM testBug28085_multiKey");
+			this.rs.next();
+			exerciseUpdatableResultSet(1, "NotUpdatableReason.7");
+			
+			this.rs = updStmt.executeQuery("SELECT t1.field2, t1.pk, t2.pk1 FROM testBug28085_oneKey t1 INNER JOIN testBug28085_multiKey t2 ON t1.pk = t2.pk1");
+			exerciseUpdatableResultSet(1, "NotUpdatableReason.0");
+			
+			this.rs = updStmt.executeQuery("SELECT field1 FROM testBug28085_noKey");
+			exerciseUpdatableResultSet(1, "NotUpdatableReason.5");
+			
+			this.rs = updStmt.executeQuery("SELECT 1");
+			exerciseUpdatableResultSet(1, "NotUpdatableReason.3");
+			
+			this.rs = updStmt.executeQuery("SELECT pk1, pk2, LEFT(field2, 2) FROM testBug28085_multiKey");
+			this.rs.next();
+			exerciseUpdatableResultSet(1, "NotUpdatableReason.3");
+		} finally {
+			closeMemberJDBCResources();
+			
+			if (updStmt != null) {
+				updStmt.close();
+			}
+		}
+	}
+
+	private void exerciseUpdatableResultSet(int columnUpdateIndex,
+			String messageToCheck) throws Exception {
+		this.rs.next();
+		
+		try {
+			this.rs.updateString(columnUpdateIndex, "def");
+		} catch (SQLException sqlEx) {
+			checkUpdatabilityMessage(sqlEx, 
+					messageToCheck);
+		}
+		
+		try {
+			this.rs.moveToInsertRow();
+		} catch (SQLException sqlEx) {
+			checkUpdatabilityMessage(sqlEx, 
+					messageToCheck);
+		}
+		
+		try {
+			this.rs.deleteRow();
+		} catch (SQLException sqlEx) {
+			checkUpdatabilityMessage(sqlEx, 
+					messageToCheck);
+		}
+		
+		this.rs.close();
+	}
+	
+	private void checkUpdatabilityMessage(SQLException sqlEx,
+			String messageToCheck) throws Exception {
+
+		String message = sqlEx.getMessage();
+
+		assertNotNull(message);
+
+		String localizedMessage = Messages.getString(messageToCheck);
+
+		assertTrue("Didn't find required message component '"
+				+ localizedMessage + "', instead found:\n\n" + message,
+				message.indexOf(localizedMessage) != -1);
+	}
+}
\ No newline at end of file

Modified: branches/branch_5_0/connector-j/src/testsuite/regression/StatementRegressionTest.java
===================================================================
--- branches/branch_5_0/connector-j/src/testsuite/regression/StatementRegressionTest.java	2007-04-24 21:03:27 UTC (rev 6413)
+++ branches/branch_5_0/connector-j/src/testsuite/regression/StatementRegressionTest.java	2007-05-02 18:55:06 UTC (rev 6414)
@@ -1233,7 +1233,13 @@
 	 *             if test fails.
 	 */
 	public void testBug3557() throws Exception {
+		boolean populateDefaults = ((com.mysql.jdbc.ConnectionProperties) this.conn)
+				.getPopulateInsertRowWithDefaultValues();
+
 		try {
+			((com.mysql.jdbc.ConnectionProperties) this.conn)
+					.setPopulateInsertRowWithDefaultValues(true);
+
 			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3557");
 
 			this.stmt.executeUpdate("CREATE TABLE testBug3557 ( "
@@ -1253,6 +1259,9 @@
 			assertEquals("XYZ", this.rs.getObject(1));
 			assertEquals("123", this.rs.getObject(2));
 		} finally {
+			((com.mysql.jdbc.ConnectionProperties) this.conn)
+					.setPopulateInsertRowWithDefaultValues(populateDefaults);
+
 			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3557");
 		}
 	}

Modified: branches/branch_5_1/connector-j/CHANGES
===================================================================
--- branches/branch_5_1/connector-j/CHANGES	2007-04-24 21:03:27 UTC (rev 6413)
+++ branches/branch_5_1/connector-j/CHANGES	2007-05-02 18:55:06 UTC (rev 6414)
@@ -177,6 +177,22 @@
 	  setReadOnly(false), when that call should be ignored until the connection 
 	  is reconnected to a writable master unless "failoverReadOnly" had been set
 	  to "false".
+	  	    
+	- Fixed BUG#28085 - Generate more useful error messages for diagnostics
+	  when the driver thinks a result set isn't updatable. (Thanks to Ashley Martens
+	  for the patch).
+	  
+	- Driver will now use INSERT INTO ... VALUES (DEFAULT) form of statement
+	  for updatable result sets for ResultSet.insertRow(), rather than 
+	  pre-populating the insert row with values from DatabaseMetaData.getColumns()
+	  (which results in a "SHOW FULL COLUMNS" on the server for every result
+	  set). If an application requires access to the default values before
+	  insertRow() has been called, the JDBC URL should be configured with 
+	  "populateInsertRowWithDefaultValues" set to "true".
+	  
+	  This fix specifically targets performance issues with ColdFusion and the 
+	  fact that it seems to ask for updatable result sets no matter what the 
+	  application does with them.
 	  	    	  	  	      
 03-01-07 - Version 5.0.5
 

Modified: branches/branch_5_1/connector-j/src/com/mysql/jdbc/ConnectionProperties.java
===================================================================
--- branches/branch_5_1/connector-j/src/com/mysql/jdbc/ConnectionProperties.java	2007-04-24 21:03:27 UTC (rev 6413)
+++ branches/branch_5_1/connector-j/src/com/mysql/jdbc/ConnectionProperties.java	2007-05-02 18:55:06 UTC (rev 6414)
@@ -1471,5 +1471,9 @@
 	public abstract String getClientInfoProvider();
 
 	public abstract void setClientInfoProvider(String classname);
+	
+	public abstract boolean getPopulateInsertRowWithDefaultValues();
 
+	public abstract void setPopulateInsertRowWithDefaultValues(boolean flag);
+
 }
\ No newline at end of file

Modified: branches/branch_5_1/connector-j/src/com/mysql/jdbc/ConnectionPropertiesImpl.java
===================================================================
--- branches/branch_5_1/connector-j/src/com/mysql/jdbc/ConnectionPropertiesImpl.java	2007-04-24 21:03:27 UTC (rev 6413)
+++ branches/branch_5_1/connector-j/src/com/mysql/jdbc/ConnectionPropertiesImpl.java	2007-05-02 18:55:06 UTC (rev 6414)
@@ -24,7 +24,6 @@
  */
 package com.mysql.jdbc;
 
-import com.mysql.jdbc.log.Jdk14Logger;
 import com.mysql.jdbc.log.Log;
 import com.mysql.jdbc.log.StandardLogger;
 
@@ -1097,6 +1096,16 @@
 			+ " to support \"XA START ... JOIN\" after \"XA END\" has been called",
 			"5.0.1", MISC_CATEGORY, Integer.MIN_VALUE);
 	
+	private BooleanConnectionProperty populateInsertRowWithDefaultValues = new BooleanConnectionProperty(
+			"populateInsertRowWithDefaultValues", false,
+			"When using ResultSets that are CONCUR_UPDATABLE, should the driver pre-poulate " +
+			"the \"insert\" row with default values from the DDL for the table used in the query " +
+			" so those values are immediately available for ResultSet accessors? This functionality requires a " +
+			" call to the database for metadata each time a result set of this type is created. " +
+			" If disabled (the default), the default values will be populated by the an internal" +
+			" call to refreshRow() which pulls back default values and/or values changed by triggers.",
+			"5.0.5", MISC_CATEGORY, Integer.MIN_VALUE);
+	
 	private IntegerConnectionProperty preparedStatementCacheSize = new IntegerConnectionProperty(
 			"prepStmtCacheSize", 25, 0, Integer.MAX_VALUE,
 			"If prepared statement caching is enabled, "
@@ -4036,4 +4045,12 @@
 	public void setClientInfoProvider(String classname) {
 		this.clientInfoProvider.setValue(classname);
 	}
+	
+	public boolean getPopulateInsertRowWithDefaultValues() {
+		return this.populateInsertRowWithDefaultValues.getValueAsBoolean();
+	}
+
+	public void setPopulateInsertRowWithDefaultValues(boolean flag) {
+		this.populateInsertRowWithDefaultValues.setValue(flag);
+	}
 }

Modified: branches/branch_5_1/connector-j/src/com/mysql/jdbc/LocalizedErrorMessages.properties
===================================================================
--- branches/branch_5_1/connector-j/src/com/mysql/jdbc/LocalizedErrorMessages.properties	2007-04-24 21:03:27 UTC (rev 6413)
+++ branches/branch_5_1/connector-j/src/com/mysql/jdbc/LocalizedErrorMessages.properties	2007-05-02 18:55:06 UTC (rev 6414)
@@ -401,8 +401,16 @@
 NotUpdatable.0=Result Set not updatable.
 NotUpdatable.1=This result set must come from a statement 
 NotUpdatable.2=that was created with a result set type of ResultSet.CONCUR_UPDATABLE, 
-NotUpdatable.3=the query must select only one table, and must 
+NotUpdatable.3=the query must select only one table, can not use functions and must 
 NotUpdatable.4=select all primary keys from that table. See the JDBC 2.1 API Specification, 
 NotUpdatable.5=section 5.6 for more details.
+NotUpdatableReason.0=Result Set not updatable (references more than one table).
+NotUpdatableReason.1=Result Set not updatable (references more than one database).
+NotUpdatableReason.2=Result Set not updatable (references no tables).
+NotUpdatableReason.3=Result Set not updatable (references computed values or doesn't reference any columns or tables).
+NotUpdatableReason.4=Result Set not updatable (references no primary keys).
+NotUpdatableReason.5=Result Set not updatable (referenced table has no primary keys).
+NotUpdatableReason.6=Result Set not updatable (references unknown primary key {0}).
+NotUpdatableReason.7=Result Set not updatable (does not reference all primary keys).
 
 JDBC4Connection.ClientInfoNotImplemented=Configured clientInfoProvider class '{0}' does not implement com.mysql.jdbc.JDBC4ClientInfoProvider.

Modified: branches/branch_5_1/connector-j/src/com/mysql/jdbc/NotUpdatable.java
===================================================================
--- branches/branch_5_1/connector-j/src/com/mysql/jdbc/NotUpdatable.java	2007-04-24 21:03:27 UTC (rev 6413)
+++ branches/branch_5_1/connector-j/src/com/mysql/jdbc/NotUpdatable.java	2007-05-02 18:55:06 UTC (rev 6414)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -32,9 +32,9 @@
  * @author Mark Matthews
  */
 public class NotUpdatable extends SQLException {
-	// ~ Static fields/initializers
-	// ---------------------------------------------
 
+	private static final long serialVersionUID = 8084742846039782258L;
+
 	/**
 	 * The message to use when result set is not updatable.
 	 * 
@@ -49,13 +49,24 @@
 			+ Messages.getString("NotUpdatable.4") //$NON-NLS-1$
 			+ Messages.getString("NotUpdatable.5"); //$NON-NLS-1$
 
-	// ~ Constructors
-	// -----------------------------------------------------------
-
 	/**
 	 * Creates a new NotUpdatable exception.
 	 */
 	public NotUpdatable() {
-		super(NOT_UPDATEABLE_MESSAGE, SQLError.SQL_STATE_GENERAL_ERROR);
+		this(NOT_UPDATEABLE_MESSAGE);
 	}
-}
+
+	/**
+	 * Append the given reason to the not updatable message if the reason is not
+	 * null.
+	 */
+	public NotUpdatable(String reason) {
+		super(reason
+				+ Messages.getString("NotUpdatable.1")
+				+ Messages.getString("NotUpdatable.2")
+				+ Messages.getString("NotUpdatable.3")
+				+ Messages.getString("NotUpdatable.4")
+				+ Messages.getString("NotUpdatable.5"),
+				SQLError.SQL_STATE_GENERAL_ERROR);
+	}
+}
\ No newline at end of file

Modified: branches/branch_5_1/connector-j/src/com/mysql/jdbc/UpdatableResultSet.java
===================================================================
--- branches/branch_5_1/connector-j/src/com/mysql/jdbc/UpdatableResultSet.java	2007-04-24 21:03:27 UTC (rev 6413)
+++ branches/branch_5_1/connector-j/src/com/mysql/jdbc/UpdatableResultSet.java	2007-05-02 18:55:06 UTC (rev 6414)
@@ -69,6 +69,9 @@
 	/** Is this result set updateable? */
 	private boolean isUpdatable = false;
 
+	/** Reason the result set is not updatable */
+	private String notUpdatableReason = null;
+
 	/** List of primary keys */
 	private List primaryKeyIndicies = null;
 
@@ -92,6 +95,8 @@
 	/** SQL for in-place modifcation */
 	private String updateSQL = null;
 
+	private boolean populateInserterWithDefaultValues = false;
+
 	/**
 	 * Creates a new ResultSet object.
 	 * 
@@ -243,6 +248,21 @@
 
 		int primaryKeyCount = 0;
 
+		// We can only do this if we know that there is a currently
+		// selected database, or if we're talking to a > 4.1 version
+		// of MySQL server (as it returns database names in field
+		// info)
+		//
+		if ((this.catalog == null) || (this.catalog.length() == 0)) {
+			this.catalog = this.fields[0].getDatabaseName();
+
+			if ((this.catalog == null) || (this.catalog.length() == 0)) {
+				throw SQLError.createSQLException(Messages
+						.getString("UpdatableResultSet.43") //$NON-NLS-1$
+						, SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ //$NON-NLS-2$
+			}
+		}
+
 		if (this.fields.length > 0) {
 			singleTableName = this.fields[0].getOriginalTableName();
 			catalogName = this.fields[0].getDatabaseName();
@@ -252,6 +272,13 @@
 				catalogName = this.catalog;
 			}
 
+			if (singleTableName != null && singleTableName.length() == 0) {
+				this.isUpdatable = false;
+				this.notUpdatableReason = Messages.getString("NotUpdatableReason.3");
+
+				return;
+			}
+			
 			if (this.fields[0].isPrimaryKey()) {
 				primaryKeyCount++;
 			}
@@ -268,9 +295,17 @@
 					otherCatalogName = this.catalog;
 				}
 
+				if (otherTableName != null && otherTableName.length() == 0) {
+					this.isUpdatable = false;
+					this.notUpdatableReason = Messages.getString("NotUpdatableReason.3");
+
+					return;
+				}
+				
 				if ((singleTableName == null)
 						|| !otherTableName.equals(singleTableName)) {
 					this.isUpdatable = false;
+					this.notUpdatableReason = Messages.getString("NotUpdatableReason.0");
 
 					return;
 				}
@@ -279,6 +314,7 @@
 				if ((catalogName == null)
 						|| !otherCatalogName.equals(catalogName)) {
 					this.isUpdatable = false;
+					this.notUpdatableReason = Messages.getString("NotUpdatableReason.1");
 
 					return;
 				}
@@ -290,39 +326,17 @@
 
 			if ((singleTableName == null) || (singleTableName.length() == 0)) {
 				this.isUpdatable = false;
+				this.notUpdatableReason = Messages.getString("NotUpdatableReason.2");
 
 				return;
 			}
 		} else {
 			this.isUpdatable = false;
+			this.notUpdatableReason = Messages.getString("NotUpdatableReason.3");
 
 			return;
 		}
-
-		// 
-		// Must have at least one primary key
-		//
-		if (primaryKeyCount == 0) {
-			this.isUpdatable = false;
-
-			return;
-		}
-
-		// We can only do this if we know that there is a currently
-		// selected database, or if we're talking to a > 4.1 version
-		// of MySQL server (as it returns database names in field
-		// info)
-		//
-		if ((this.catalog == null) || (this.catalog.length() == 0)) {
-			this.catalog = this.fields[0].getDatabaseName();
-
-			if ((this.catalog == null) || (this.catalog.length() == 0)) {
-				throw SQLError.createSQLException(Messages
-						.getString("UpdatableResultSet.43") //$NON-NLS-1$
-						, SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ //$NON-NLS-2$
-			}
-		}
-
+		
 		if (this.connection.getStrictUpdates()) {
 			java.sql.DatabaseMetaData dbmd = this.connection.getMetaData();
 
@@ -349,8 +363,11 @@
 				}
 			}
 
-			if (primaryKeyNames.size() == 0) {
+			int existingPrimaryKeysCount = primaryKeyNames.size();
+			
+			if (existingPrimaryKeysCount == 0) {
 				this.isUpdatable = false;
+				this.notUpdatableReason = Messages.getString("NotUpdatableReason.5");
 
 				return; // we can't update tables w/o keys
 			}
@@ -372,6 +389,8 @@
 									.toUpperCase()) == null) {
 								// we don't know about this key, so give up :(
 								this.isUpdatable = false;
+								this.notUpdatableReason = Messages.getString("NotUpdatableReason.6", 
+										new Object[] {originalName});
 
 								return;
 							}
@@ -381,11 +400,30 @@
 			}
 
 			this.isUpdatable = primaryKeyNames.isEmpty();
+			
+			if (!this.isUpdatable) {
+				if (existingPrimaryKeysCount > 1) {
+					this.notUpdatableReason = Messages.getString("NotUpdatableReason.7");
+				} else {
+					this.notUpdatableReason = Messages.getString("NotUpdatableReason.4");
+				}
+				
+				return;
+			}
+		}
+		
+		// 
+		// Must have at least one primary key
+		//
+		if (primaryKeyCount == 0) {
+			this.isUpdatable = false;
+			this.notUpdatableReason = Messages.getString("NotUpdatableReason.4");
 
 			return;
 		}
 
 		this.isUpdatable = true;
+		this.notUpdatableReason = null;
 
 		return;
 	}
@@ -404,7 +442,7 @@
 		checkClosed();
 
 		if (!this.isUpdatable) {
-			throw new NotUpdatable();
+			throw new NotUpdatable(this.notUpdatableReason);
 		}
 
 		if (this.onInsertRow) {
@@ -546,7 +584,7 @@
 			this.doingUpdates = false;
 			this.onInsertRow = false;
 
-			throw new NotUpdatable();
+			throw new NotUpdatable(this.notUpdatableReason);
 		}
 
 		String quotedId = getQuotedIdChar();
@@ -736,9 +774,12 @@
 			//
 			if (this.fields[i].isAutoIncrement() && autoIncrementId > 0) {
 				newRow[i] = String.valueOf(autoIncrementId).getBytes();
+				this.inserter.setBytesNoEscapeNoQuotes(i + 1, newRow[i]);
 			}
 		}
-
+		
+		refreshRow(this.inserter, newRow);
+		
 		this.rowData.addRow(newRow);
 		resetInserter();
 	}
@@ -847,7 +888,7 @@
 		checkClosed();
 
 		if (!this.isUpdatable) {
-			throw new NotUpdatable();
+			throw new NotUpdatable(this.notUpdatableReason);
 		}
 
 		if (this.onInsertRow) {
@@ -877,7 +918,7 @@
 		checkClosed();
 
 		if (!this.isUpdatable) {
-			throw new NotUpdatable();
+			throw new NotUpdatable(this.notUpdatableReason);
 		}
 
 		if (this.inserter == null) {
@@ -887,7 +928,10 @@
 
 			this.inserter = this.connection
 					.clientPrepareStatement(this.insertSQL);
-			extractDefaultValues();
+			if (this.populateInserterWithDefaultValues) {
+				extractDefaultValues();
+			}
+			
 			resetInserter();
 		} else {
 			resetInserter();
@@ -901,44 +945,50 @@
 		this.thisRow = new byte[numFields][];
 
 		for (int i = 0; i < numFields; i++) {
-			if (this.defaultColumnValue[i] != null) {
-				Field f = this.fields[i];
-
-				switch (f.getMysqlType()) {
-				case MysqlDefs.FIELD_TYPE_DATE:
-				case MysqlDefs.FIELD_TYPE_DATETIME:
-				case MysqlDefs.FIELD_TYPE_NEWDATE:
-				case MysqlDefs.FIELD_TYPE_TIME:
-				case MysqlDefs.FIELD_TYPE_TIMESTAMP:
-
-					if (this.defaultColumnValue[i].length > 7
-							&& this.defaultColumnValue[i][0] == (byte) 'C'
-							&& this.defaultColumnValue[i][1] == (byte) 'U'
-							&& this.defaultColumnValue[i][2] == (byte) 'R'
-							&& this.defaultColumnValue[i][3] == (byte) 'R'
-							&& this.defaultColumnValue[i][4] == (byte) 'E'
-							&& this.defaultColumnValue[i][5] == (byte) 'N'
-							&& this.defaultColumnValue[i][6] == (byte) 'T'
-							&& this.defaultColumnValue[i][7] == (byte) '_') {
-						this.inserter.setBytesNoEscapeNoQuotes(i + 1,
-								this.defaultColumnValue[i]);
-
-						break;
+			if (!this.populateInserterWithDefaultValues) {
+				this.inserter.setBytesNoEscapeNoQuotes(i + 1, 
+						"DEFAULT".getBytes());
+				this.thisRow[i] = null;
+			} else {
+				if (this.defaultColumnValue[i] != null) {
+					Field f = this.fields[i];
+	
+					switch (f.getMysqlType()) {
+					case MysqlDefs.FIELD_TYPE_DATE:
+					case MysqlDefs.FIELD_TYPE_DATETIME:
+					case MysqlDefs.FIELD_TYPE_NEWDATE:
+					case MysqlDefs.FIELD_TYPE_TIME:
+					case MysqlDefs.FIELD_TYPE_TIMESTAMP:
+	
+						if (this.defaultColumnValue[i].length > 7
+								&& this.defaultColumnValue[i][0] == (byte) 'C'
+								&& this.defaultColumnValue[i][1] == (byte) 'U'
+								&& this.defaultColumnValue[i][2] == (byte) 'R'
+								&& this.defaultColumnValue[i][3] == (byte) 'R'
+								&& this.defaultColumnValue[i][4] == (byte) 'E'
+								&& this.defaultColumnValue[i][5] == (byte) 'N'
+								&& this.defaultColumnValue[i][6] == (byte) 'T'
+								&& this.defaultColumnValue[i][7] == (byte) '_') {
+							this.inserter.setBytesNoEscapeNoQuotes(i + 1,
+									this.defaultColumnValue[i]);
+	
+							break;
+						}
+					default:
+						this.inserter.setBytes(i + 1, this.defaultColumnValue[i],
+								false, false);
 					}
-				default:
-					this.inserter.setBytes(i + 1, this.defaultColumnValue[i],
-							false, false);
+	
+					// This value _could_ be changed from a getBytes(), so we
+					// need a copy....
+					byte[] defaultValueCopy = new byte[this.defaultColumnValue[i].length];
+					System.arraycopy(defaultColumnValue[i], 0, defaultValueCopy, 0,
+							defaultValueCopy.length);
+					this.thisRow[i] = defaultValueCopy;
+				} else {
+					this.inserter.setNull(i + 1, java.sql.Types.NULL);
+					this.thisRow[i] = null;
 				}
-
-				// This value _could_ be changed from a getBytes(), so we
-				// need a copy....
-				byte[] defaultValueCopy = new byte[this.defaultColumnValue[i].length];
-				System.arraycopy(defaultColumnValue[i], 0, defaultValueCopy, 0,
-						defaultValueCopy.length);
-				this.thisRow[i] = defaultValueCopy;
-			} else {
-				this.inserter.setNull(i + 1, java.sql.Types.NULL);
-				this.thisRow[i] = null;
 			}
 		}
 	}
@@ -1118,6 +1168,11 @@
 			throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.11")); //$NON-NLS-1$
 		}
 
+		refreshRow(this.updater, this.thisRow);
+	}
+	
+	private synchronized void refreshRow(PreparedStatement updateInsertStmt, 
+			Object[] rowToRefresh) throws SQLException {
 		if (this.refresher == null) {
 			if (this.refreshSQL == null) {
 				generateStatements();
@@ -1135,14 +1190,14 @@
 			byte[] dataFrom = null;
 			int index = ((Integer) this.primaryKeyIndicies.get(0)).intValue();
 
-			if (!this.doingUpdates) {
-				dataFrom = (byte[]) this.thisRow[index];
+			if (!this.doingUpdates && !this.onInsertRow) {
+				dataFrom = (byte[]) rowToRefresh[index];
 			} else {
-				dataFrom = this.updater.getBytesRepresentation(index);
+				dataFrom = updateInsertStmt.getBytesRepresentation(index);
 
 				// Primary keys not set?
-				if (this.updater.isNull(index) || (dataFrom.length == 0)) {
-					dataFrom = (byte[]) this.thisRow[index];
+				if (updateInsertStmt.isNull(index) || (dataFrom.length == 0)) {
+					dataFrom = (byte[]) rowToRefresh[index];
 				} else {
 					dataFrom = stripBinaryPrefix(dataFrom);
 				}
@@ -1155,13 +1210,13 @@
 				int index = ((Integer) this.primaryKeyIndicies.get(i))
 						.intValue();
 
-				if (!this.doingUpdates) {
-					dataFrom = (byte[]) this.thisRow[index];
+				if (!this.doingUpdates && !this.onInsertRow) {
+					dataFrom = (byte[]) rowToRefresh[index];
 				} else {
-					dataFrom = this.updater.getBytesRepresentation(index);
+					dataFrom = updateInsertStmt.getBytesRepresentation(index);
 
 					// Primary keys not set?
-					if (this.updater.isNull(index) || (dataFrom.length == 0)) {
+					if (updateInsertStmt.isNull(index) || (dataFrom.length == 0)) {
 						dataFrom = (byte[]) this.thisRow[index];
 					} else {
 						dataFrom = stripBinaryPrefix(dataFrom);
@@ -1184,9 +1239,9 @@
 					byte[] val = rs.getBytes(i + 1);
 
 					if ((val == null) || rs.wasNull()) {
-						this.thisRow[i] = null;
+						rowToRefresh[i] = null;
 					} else {
-						this.thisRow[i] = rs.getBytes(i + 1);
+						rowToRefresh[i] = rs.getBytes(i + 1);
 					}
 				}
 			} else {
@@ -2203,7 +2258,7 @@
 	 */
 	public synchronized void updateRow() throws SQLException {
 		if (!this.isUpdatable) {
-			throw new NotUpdatable();
+			throw new NotUpdatable(this.notUpdatableReason);
 		}
 
 		if (this.doingUpdates) {

Modified: branches/branch_5_1/connector-j/src/com/mysql/jdbc/configs/3-0-Compat.properties
===================================================================
--- branches/branch_5_1/connector-j/src/com/mysql/jdbc/configs/3-0-Compat.properties	2007-04-24 21:03:27 UTC (rev 6413)
+++ branches/branch_5_1/connector-j/src/com/mysql/jdbc/configs/3-0-Compat.properties	2007-05-02 18:55:06 UTC (rev 6414)
@@ -14,4 +14,5 @@
 useServerPrepStmts=false
 autoClosePStmtStreams=true
 processEscapeCodesForPrepStmts=false
-useFastDateParsing=false
\ No newline at end of file
+useFastDateParsing=false
+populateInsertRowWithDefaultValues=false
\ No newline at end of file

Modified: branches/branch_5_1/connector-j/src/testsuite/regression/ResultSetRegressionTest.java
===================================================================
--- branches/branch_5_1/connector-j/src/testsuite/regression/ResultSetRegressionTest.java	2007-04-24 21:03:27 UTC (rev 6413)
+++ branches/branch_5_1/connector-j/src/testsuite/regression/ResultSetRegressionTest.java	2007-05-02 18:55:06 UTC (rev 6414)
@@ -50,6 +50,7 @@
 
 import testsuite.BaseTestCase;
 
+import com.mysql.jdbc.Messages;
 import com.mysql.jdbc.MysqlDataTruncation;
 import com.mysql.jdbc.NotUpdatable;
 import com.mysql.jdbc.SQLError;
@@ -4270,4 +4271,101 @@
 			closeMemberJDBCResources();
 		}
 	}
-}
+
+	/**
+	 * Tests fix for BUG#28085 - Need more useful error messages for diagnostics
+	 * when the driver thinks a result set isn't updatable.
+	 * 
+	 * @throws Exception if the tests fail.
+	 */
+	public void testBug28085() throws Exception {
+
+		Statement updStmt = null;
+		
+		try {
+			createTable("testBug28085_oneKey", 
+				"(pk int primary key not null, field2 varchar(3))");
+			
+			this.stmt.executeUpdate("INSERT INTO testBug28085_oneKey (pk, field2) VALUES (1, 'abc')");
+			
+			createTable("testBug28085_multiKey", 
+				"(pk1 int not null, pk2 int not null, field2 varchar(3), primary key (pk1, pk2))");
+			
+			this.stmt.executeUpdate("INSERT INTO testBug28085_multiKey VALUES (1,2,'abc')");
+			
+			createTable("testBug28085_noKey", 
+					"(field1 varchar(3) not null)");
+	
+			this.stmt.executeUpdate("INSERT INTO testBug28085_noKey VALUES ('abc')");
+			
+			updStmt = this.conn.createStatement(ResultSet.TYPE_FORWARD_ONLY,
+					ResultSet.CONCUR_UPDATABLE);
+			
+			this.rs = updStmt.executeQuery("SELECT field2 FROM testBug28085_oneKey");
+			exerciseUpdatableResultSet(1, "NotUpdatableReason.4");
+			
+			this.rs = updStmt.executeQuery("SELECT pk1, field2 FROM testBug28085_multiKey");
+			this.rs.next();
+			exerciseUpdatableResultSet(1, "NotUpdatableReason.7");
+			
+			this.rs = updStmt.executeQuery("SELECT t1.field2, t1.pk, t2.pk1 FROM testBug28085_oneKey t1 INNER JOIN testBug28085_multiKey t2 ON t1.pk = t2.pk1");
+			exerciseUpdatableResultSet(1, "NotUpdatableReason.0");
+			
+			this.rs = updStmt.executeQuery("SELECT field1 FROM testBug28085_noKey");
+			exerciseUpdatableResultSet(1, "NotUpdatableReason.5");
+			
+			this.rs = updStmt.executeQuery("SELECT 1");
+			exerciseUpdatableResultSet(1, "NotUpdatableReason.3");
+			
+			this.rs = updStmt.executeQuery("SELECT pk1, pk2, LEFT(field2, 2) FROM testBug28085_multiKey");
+			this.rs.next();
+			exerciseUpdatableResultSet(1, "NotUpdatableReason.3");
+		} finally {
+			closeMemberJDBCResources();
+			
+			if (updStmt != null) {
+				updStmt.close();
+			}
+		}
+	}
+	
+	private void exerciseUpdatableResultSet(int columnUpdateIndex,
+			String messageToCheck) throws Exception {
+		this.rs.next();
+		
+		try {
+			this.rs.updateString(columnUpdateIndex, "def");
+		} catch (SQLException sqlEx) {
+			checkUpdatabilityMessage(sqlEx, 
+					messageToCheck);
+		}
+		
+		try {
+			this.rs.moveToInsertRow();
+		} catch (SQLException sqlEx) {
+			checkUpdatabilityMessage(sqlEx, 
+					messageToCheck);
+		}
+		
+		try {
+			this.rs.deleteRow();
+		} catch (SQLException sqlEx) {
+			checkUpdatabilityMessage(sqlEx, 
+					messageToCheck);
+		}
+	}
+	
+	private void checkUpdatabilityMessage(SQLException sqlEx,
+			String messageToCheck) throws Exception {
+
+		String message = sqlEx.getMessage();
+
+		assertNotNull(message);
+
+		String localizedMessage = Messages.getString(messageToCheck);
+
+		assertTrue("Didn't find required message component '"
+				+ localizedMessage + "', instead found:\n\n" + message,
+				message.indexOf(localizedMessage) != -1);
+	}
+}
\ No newline at end of file

Modified: branches/branch_5_1/connector-j/src/testsuite/regression/StatementRegressionTest.java
===================================================================
--- branches/branch_5_1/connector-j/src/testsuite/regression/StatementRegressionTest.java	2007-04-24 21:03:27 UTC (rev 6413)
+++ branches/branch_5_1/connector-j/src/testsuite/regression/StatementRegressionTest.java	2007-05-02 18:55:06 UTC (rev 6414)
@@ -1233,7 +1233,13 @@
 	 *             if test fails.
 	 */
 	public void testBug3557() throws Exception {
+		boolean populateDefaults = ((com.mysql.jdbc.ConnectionProperties) this.conn)
+				.getPopulateInsertRowWithDefaultValues();
+
 		try {
+			((com.mysql.jdbc.ConnectionProperties) this.conn)
+					.setPopulateInsertRowWithDefaultValues(true);
+
 			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3557");
 
 			this.stmt.executeUpdate("CREATE TABLE testBug3557 ( "
@@ -1253,6 +1259,9 @@
 			assertEquals("XYZ", this.rs.getObject(1));
 			assertEquals("123", this.rs.getObject(2));
 		} finally {
+			((com.mysql.jdbc.ConnectionProperties) this.conn)
+					.setPopulateInsertRowWithDefaultValues(populateDefaults);
+
 			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3557");
 		}
 	}

Thread
Connector/J commit: r6414 - in branches: branch_5_0/connector-j branch_5_0/connector-j/src/com/mysql/jdbc branch_5_0/connector-j/src/com/mysql/jdbc/co...mmatthews2 May